Class Control

3DS Max Plug-In SDK

Class Control

See Also: Class ReferenceTarget, Class IKeyControl, Class IKDeriv, Class IKEnumCallback, Class StdControl, List of Additional Controller Related Functions, Class AngAxis, Class Quat, Class Interval, Class Matrix3, Class Point3, Class ScaleValue, Class JointParams, Class SetXFormPacket.

class Control : public ReferenceTarget

Description:

Control is the class from which you may derived controller objects. Controllers are the objects in 3ds max that control animation. Controllers come in different types based on the type of data they control. For example, Transform controllers control the 4x3 matrices used to define the position of nodes in the scene while float controllers control simple floating point values.

Note: Many controller plug-ins may be able to subclass from StdControl rather than Control. This simplifies the developers job. StdControl handles the processing of Out of Range Types, Ease Curves, and Multiplier Curves. See Class StdControl for more information.

Plug-In Information:

Class Defined In CONTROL.H

Super Class ID CTRL_FLOAT_CLASS_ID - Used by float controllers.

 CTRL_POINT3_CLASS_ID - Used by Point3 controllers.

 CTRL_MATRIX3_CLASS_ID - Used by Matrix3 controllers.

 CTRL_POSITION_CLASS_ID - Used by position controllers.

 CTRL_ROTATION_CLASS_ID - Used by rotation controllers.

 CTRL_SCALE_CLASS_ID - Used by scale controllers.

 CTRL_MORPH_CLASS_ID - Used by morph controllers.

Standard File Name Extension DLC

Extra Include File Needed None

Method Groups:

The following hyperlinks jump to the start of groups of related methods within the class:

GetValue and SetValue

Inverse Kinematics (IK)

Ease Curve, Multiplier Curve and Out of Range Types

Access to Sub-Controllers

New Controller Assignment

Transform Controller methods

Lookat Controller methods

IsLeaf/IsKeyable / IsColorController

Copying and Pasting

Gizmo / Sub-Object methods

Load / Save

Post Clone Notification

Undo / Redo

Miscellaneous methods

Methods:

Prototype:

virtual void GetValue(TimeValue t, void *val, Interval &valid,

GetSetMethod method=CTRL_ABSOLUTE)=0;

Remarks:

Implemented by the Plug-In.

Retrieves the value of the controller at the specified time, and updates the validity interval passed in to reflect the interval of the controller. This method is responsible for handling Out of Range Types, Ease Curves and Multiplier Curves. See the sample code below.

Parameters:

TimeValue t

Specifies the time to retrieve the value.

void *val

This points to a variable to hold the computed value of the controller at the specified time. What the plug-in needs to do to store the value of the controller depends on the controller type. There are six controller types: float, Point3, Position, Rotation, Scale, and Transform. The way the value is stored also depends on the GetSetMethod parameter method. Below is list of the possible cases and how the value should be stored in each case.

float

If method == CTRL_ABSOLUTE

*val points to a float

The controller should simply store the value.

 

If method == CTRL_RELATIVE

*val points to a float

The controller should add its value to the existing floating point value.

Point3

If method == CTRL_ABSOLUTE

*val points to a Point3

The controller should simply store the value.

If method == CTRL_RELATIVE

*val points to a Point3

The controller should add its value to the existing Point3 value.

Position

If method == CTRL_ABSOLUTE

*val points to a Point3

The controller should simply store the value.

If method == CTRL_RELATIVE

*val points to a Matrix3

The controller should apply its value to the matrix by pre-multiplying its position.

Matrix3 *mat = (Matrix3*)val;

Point3 v = the computed value of the controller...

mat->PreTranslate(v);

Rotation

If method == CTRL_ABSOLUTE

*val points to a Quat

The controller should simply store the value.

If method == CTRL_RELATIVE

*val points to a Matrix3

The controller should apply its value to the matrix by pre-multiplying its rotation.

Matrix3 *mat = (Matrix3*)val;

Quat q = the computed value of the controller...

PreRotateMatrix(*mat,q);

Scale

If method == CTRL_ABSOLUTE

*val points to a ScaleValue

The controller should simply store the value.

If method == CTRL_RELATIVE

*val points to a Matrix3

The controller should apply its value to the matrix by pre-multiplying its scale.

Matrix3 *mat = (Matrix3*)val;

ScaleValue s = the computed value of the controller...

ApplyScaling(*mat,s);

Transform (Matrix3)

If method == CTRL_ABSOLUTE

*val points to a Matrix3

The controller should simply store the value.

Important Note: Developers should only pass CTRL_RELATIVE when getting the value of a Matrix3 controller. This is because the controller may use the matrix as input to compute the value. Therefore it is not acceptable to use CTRL_ABSOLUTE to get the value.

If method == CTRL_RELATIVE

*val points to a Matrix3.

The controller should apply its value to the matrix by pre-multiplying. When GetValue() is called on a transform controller the method is CTRL_RELATIVE and the matrix passed is usually the parent of the node.

Important Note for Matrix3 Controllers: when SetValue() is called *val points to an instance of Class SetXFormPacket. See that class for more details on how it is used.

 

Interval &valid

The validity interval to update. The controllers validity interval should be intersected with this interval. This updates the interval to reflect the interval of the controller.

GetSetMethod method=CTRL_ABSOLUTE

One of the following values:

CTRL_RELATIVE

Indicates the plug-in should apply the value of the controller to *val. See Above.

CTRL_ABSOLUTE

Indicates the controller should simply store its value in *val. See Above.

Sample Code:

The following code is from the StdControl implementation of this method. It demonstrates how the out of range and multiplier curves are handled.

void StdControl::GetValue(TimeValue t, void *val, Interval &valid, GetSetMethod method)

 {

 Interval range = GetTimeRange(TIMERANGE_ALL);

 Interval wvalid = FOREVER, cvalid = FOREVER;

 void *ptr = val;

 int ort;

 float m;

 TimeValue oldTime = t;

 

 if (method==CTRL_RELATIVE) {

  ptr = CreateTempValue();

  }

 

 // Grab the multiplier before the time warp.

 m = GetMultVal(t,valid);

 

 // Apply the time warp.

 t = ApplyEase(t,wvalid);

 

 if (t<=range.Start()) {

  ort = GetORT(ORT_BEFORE);

 } else {

  ort = GetORT(ORT_AFTER);

  }

 

 if (/*ort==ORT_CONSTANT*/TestAFlag(A_ORT_DISABLED) ||

  range.Empty() || range.InInterval(t)) {

 

  GetValueLocalTime(t,ptr,cvalid);

 } else {

  switch (ort) {

   case ORT_CONSTANT:

    if (t<range.Start()) {

     GetValueLocalTime(range.Start(),ptr,cvalid,CTRL_ABSOLUTE);

     cvalid.Set(TIME_NegInfinity,range.Start());

    } else {

     GetValueLocalTime(range.End(),ptr,cvalid,CTRL_ABSOLUTE);

     cvalid.Set(range.End(),TIME_PosInfinity);

     }

    break;

 

   case ORT_LOOP:

   case ORT_CYCLE:

    GetValueLocalTime(CycleTime(range,t),ptr,

     cvalid,CTRL_ABSOLUTE);

    break;

 

   case ORT_OSCILLATE: {

    int cycles = NumCycles(range,t);

    TimeValue tp = CycleTime(range,t);

    if (cycles&1) {

     tp = range.End()-(tp-range.Start());

     }

     GetValueLocalTime(tp,ptr,cvalid,CTRL_ABSOLUTE);

    break;

    }

 

   case ORT_RELATIVE_REPEAT:

   case ORT_IDENTITY:

   case ORT_LINEAR:

    Extrapolate(range,t,ptr,cvalid,ort);

    break;

   }

  }

 

 if (m!=1.0f) {

  MultiplyValue(ptr,m);

  }

 if (method==CTRL_RELATIVE) {

  ApplyValue(val,ptr);

  DeleteTempValue(ptr);

  }

 

 if (ort!=ORT_CONSTANT) {

  cvalid.Set(oldTime,oldTime);

  }

 valid &= cvalid;

 valid &= wvalid;

 // Time warps can cause this to happen.

 if (valid.Empty()) valid.Set(oldTime,oldTime);

 }

Prototype:

virtual void SetValue(TimeValue t, void *val, int commit=1,

 GetSetMethod method=CTRL_ABSOLUTE)=0;

Remarks:

Implemented by the Plug-In.

This method sets the value of the controller at the specified time. This method is responsible for handling Out of Range Types, Ease Curves and Multiplier Curves. See the sample code below.

Note: Developers who want to create keys for a keyframe controller by calling SetValue() directly can do so, but the animate button should be turned on using the following code:

SuspendAnimate();

AnimateOn();

// Call SetValue() -- make sure commit=1

ResumeAnimate();

Parameters:

TimeValue t

Specifies the time to save the value.

void *val

Points to an instance of a data type that corresponds with the controller type. These are the same as GetValue() above with the following exceptions:

For rotation controllers, if the GetSetMethod is CTRL_RELATIVE, *val points to an AngAxis, while if it is CTRL_ABSOLUTE it points to a Quat.

For Matrix3 controllers *val points to an instance of class SetXFormPacket. See Class SetXFormPacket.

int commit=1

When SetValue() is called the controller should store it value (usually into a cache it maintains), and if this parameter is set, also 'commit' it's value (usually by calling CommitValue()).

For example, consider a 3ds max keyframe controller: If commit==1 and if the Animate button is on, then the cache should be updated and a key should be created. If the Animate button is off then the cache should be updated and the keys should all be offset. If commit==0 then the cache value is set and its validity interval is set to the current time. If later commit==1 then a key would be created from that cached value. If SetValue() is never called with commit=1 then the key is never set. For instance with Inverse Kinetmatics, SetValue() is called many times over and over at the same TimeValue with commit=0. The controller doesn't create a key, it just changes its cached value. When an IK solution is finally reached, SetValue() is called with commit=1 and a key is created.

Note that calling SetValue() with commit=0 and then calling CommitValue() should have the same effect as calling SetValue() with commit=1.

See the methods CommitValue() and RestoreValue() below.

GetSetMethod method=CTRL_ABSOLUTE

One of the following values:

CTRL_RELATIVE

Indicates the plug-in should add the value to the existing value *val (i.e. Move/Rotate/Scale)

CTRL_ABSOLUTE

Indicates the plug-in should just set the value.

Important Note for Matrix3 Controllers: When SetValue() is called this method parameter is ignored. The *val pointer passed to SetValue() points to an instance of Class SetXFormPacket. See that class for more details on how it is used.

Sample Code:

The following code is from the StdControl implementation of this method. It demonstrates how the out of range and multiplier curves are handled.

void StdControl::SetValue(TimeValue t, void *val, int commit, GetSetMethod method)

 {

 Interval range = GetTimeRange(TIMERANGE_ALL);

 Interval wvalid, mvalid;

 int ort;

 float m;

 

 // Grab the multiplier before the time warp.

 m = GetMultVal(t,mvalid);

 if (m!=1.0f && m!=0.0f) {

  MultiplyValue(val,1.0f/m);

  }

 

 // Apply the time warp.

 t = ApplyEase(t,wvalid);

 if (range.Empty()) {

  SetValueLocalTime(t,val,commit,method);

  return;

  }

 

 if (t<=range.Start()) {

  ort = GetORT(ORT_BEFORE);

 } else {

  ort = GetORT(ORT_AFTER);

  }

 if (TestAFlag(A_ORT_DISABLED)) ort = ORT_CONSTANT;

 

 switch (ort) {

  case ORT_LOOP:

  case ORT_CYCLE:

   SetValueLocalTime(CycleTime(range,t),val,commit,method);

   break;

 

  case ORT_OSCILLATE: {

   int cycles = NumCycles(range,t);

   if (cycles&1) {

    t = range.Duration() - CycleTime(range,t);

   } else {

    t = CycleTime(range,t);

    }

    SetValueLocalTime(t,val,commit,method);

   break;

   }

 

   // These ORTs aren't cyclic so we just set the value out of range.

  case ORT_RELATIVE_REPEAT:

  case ORT_CONSTANT:

  case ORT_IDENTITY:

  case ORT_LINEAR:

   SetValueLocalTime(t,val,commit,method);

   break;

  }

 }

Prototype:

virtual bool GetLocalTMComponents(TimeValue t, TMComponentsArg& cmpts, Matrix3Indirect& parentMatrix);

Remarks:

This method is available in release 4.0 and later only.

This method returns the PRS components of the local matrix. In general, controller cannot decide on the local matrix without knowing the parent matrix. However, many controllers, such as default controllers, are well defined without the parent matrix. In these cases, it is more efficient to compute the local components directly without going through the world matrix.

Therefore, the argument parentMatrix is a reference to Matrix3Indirect. This would allow clients to supply a "delayed parent matrix," which will be computed only if it is necessary. It returns true for Matrix3, Position, Rotation, or Scale controllers, and return false otherwise.

The PRS components will be put in argument cmpts in the respective fields with corresponding validity intervals. NULL pointer, of TMComponentsArg::position for example, indicates that the client is not concerned about the component. When it is not NULL, the corresponding pointer to the validity interval MUST NOT be NULL. When it is not NULL, TMComponentsArg::rotation is a float[4]. rotRep tells what the numbers mean.

Position, Rotation, or Scale, controllers will put results at the respective component when the corresponding pointer is not NULL.

Upon entry, parentMatrix should represent the parent matrix up to the first requested components. For Matrix3 controllers, for example, if cmpts.position==NULL && cmpts.rotation!=NULL, then parentMatrix should be matrix that includes the parent node matrix plus the position of this node. Upon return, this matrix may be modified.

Parameters:

TimeValue t

The time at which to get the local TM components.

TMComponentsArg& cmpts

See Structure TMComponentsArgs.

Matrix3Indirect& parentMatrix

The parent matrix. Note the definition: LocalMatrix = WorldMatrix * ParentWorldMatrix^(-1)

Post Clone Notification

Prototype:

virtual void PostCloneNode();

Remarks:

This method is available in release 2.0 and later only.

This method is called on a transform controller after a node is cloned and the clone process has finished. This allows the controller to do any work it needs to after the clone is complete.

Default Implementation:

{}

Undo / Redo methods

Controllers, like objects and modifiers, need to be able to undo and redo themselves. Whenever a controller is about to modify its data, it checks the state of the global Hold object to see if it is holding. If so it must register a RestoreObject with the hold (see the Advanced Topics section Undo/Redo). Controllers also support another type of undo and redo through two methods: CommitValue() and RestoreValue().

The purpose of this 'inner' hold and restore buffer is not to hold and restore the entire state of the controller, but to hold and restore the value of the controller at a single instant in time. When SetValue()is called on a controller with the commit parameter equal to zero, the controller records the new value, but does not necessarily modify any data. For example, a keyframe controller doesn't actually generate a new key -- it just updates a cache it maintains. Then, if the controller is asked to evaluate itself at the exact same TimeValue for which the controller was just set, it can just return the cached value.

The RestoreValue() method will simply throw out the temporary value whereas the CommitValue() method will cause the value to be actually committed (a key generated in the case of a keyframe controller).

The purpose of this inner hold and restore is for iterative procedures that need to set values many times at a single TimeValue and don't want to incur the overhead of things like recalculating the tangents at adjacent keys. Some examples of these types of procedures are inverse kinematics and collision detection.

Prototype:

virtual void CommitValue(TimeValue t)

Remarks:

Implemented by the Plug-In.

This method, along with RestoreValue(), comprise an "inner" hold and restore mechanism (see above). When the controller's SetValue() method is called, if the commit parameter is nonzero, then the controller should save the value of the controller at the current time into its cache and also 'commit' the value. For example, this stores a key in the case of a keyframe controller. If the set value was not committed then RestoreValue() may be called to restore the previous value.

Parameters:

TimeValue t

Specifies the time to save the value.

Default Implementation:

{}

Prototype:

virtual void RestoreValue(TimeValue t)

Remarks:

Implemented by the Plug-In.

This method is the other half of the "inner" hold and restore mechanism. This method is called to restore a previously saved value. This method restores the current cache value to the value that was set before SetValue() was last called. They way the standard 3ds max controllers handle this is as follows: When SetValue() is called a temporary hold mechanism (TempStore define in CONTROL.H) is used to hold the current value. Then the new value is set. If RestoreValue() is later called then it restores the current value from the temporary storage. Note that in addition to restoring from the TempStore, another way a controller may restore the current value is to re-interpolate the keys.

Parameters:

TimeValue t

Specifies the time to restore the value.

Default Implementation:

{}

New Controller Assignment

Prototype:

virtual void Copy(Control *from)=0;

Remarks:

Implemented by the Plug-In.

When a controller is assigned to a track in the track view, the new controller is plugged into the parameter and this method is called on the new controller. A pointer to the old controller is passed in to this method. The new controller can attempt to copy any data that it can from the old controller. At the very least it should initialize itself to the value of the old controller at frame 0.

Parameters:

Control *from

A pointer to the previous controller.

Prototype:

virtual BOOL IsReplaceable()

Remarks:

Implemented by the Plug-In.

This method determines if another controller can replace this one. A controller can return FALSE from this method to not allow the user to assign a new controller in its place. This will also prevent the controller from being replaced by a paste controller operation.

Return Value:

TRUE to allow the controller to be replaced; otherwise FALSE.

Default Implementation:

{return TRUE;}

Lookat Controller methods

The following two methods work with lookat controllers. If the controller is not a lookat controller, these methods should be ignored.

Prototype:

virtual INode* GetTarget()

Remarks:

Implemented by the Plug-In.

This method retrieves a lookat controller's target.

Return Value:

The lookat controllers target node.

Default Implementation:

{ return NULL; }

Prototype:

virtual RefResult SetTarget(INode *targ)

Remarks:

Implemented by the Plug-In.

This method stores a lookat controller's target.

Parameters:

INode *targ

The target node to store.

Return Value:

One of the following values:

REF_SUCCEED

Indicates the target was set.

REF_FAIL

Indicates the target was not set.

Default Implementation:

{return REF_SUCCEED;}

Sub-Controllers

Prototype:

virtual Control *GetPositionController()

Remarks:

Implemented by the Plug-In.

Implemented by transform controllers that have a position controller that can be edited in the motion branch. This method returns a pointer to the position controller of the transform controller.

Default Implementation:

{return NULL;}

Prototype:

virtual Control *GetRotationController()

Remarks:

Implemented by the Plug-In.

Implemented by transform controllers that have a rotation controller that can be edited in the motion branch. This method returns a pointer to the rotation controller of the transform controller.

Return Value:

Default Implementation:

{return NULL;}

Prototype:

virtual Control *GetScaleController()

Remarks:

Implemented by the Plug-In.

Implemented by transform controllers that have a scale controller that can be edited in the motion branch. This method returns the a pointer to the scale controller of the transform controller.

Return Value:

Default Implementation:

{return NULL;}

Prototype:

virtual Control *GetRollController()

Remarks:

Implemented by the Plug-In.

Implemented by lookat controllers that have a float valued roll controller so that the roll can be edited via the transform type-in. This method returns a pointer to the roll controller of the lookat controller.

Default Implementation:

{return NULL;}

Prototype:

virtual BOOL SetPositionController(Control *c);

Remarks:

Implemented by the Plug-In.

This method assigns a new position controller. Plug-Ins don't need to be concerned with freeing the previous controller if this method is called. Any previous controller assigned will be deleted by 3ds max if it is not used elsewhere in the scene.

Default Implementation:

{return FALSE;}

Prototype:

virtual BOOL SetRotationController(Control *c);

Remarks:

Implemented by the Plug-In.

This method assigns a new rotation controller. Plug-Ins don't need to be concerned with freeing the previous controller if this method is called. Any previous controller assigned will be deleted by 3ds max if it is not used elsewhere in the scene.

Default Implementation:

{return FALSE;}

Prototype:

virtual BOOL SetScaleController(Control *c);

Remarks:

Implemented by the Plug-In.

This method assigns a new scale controller. Plug-Ins don't need to be concerned with freeing the previous controller if this method is called. Any previous controller assigned will be deleted by 3ds max if it is not used elsewhere in the scene.

Default Implementation:

{return FALSE;}

Prototype:

virtual BOOL SetRollController(Control *c);

Remarks:

Implemented by the Plug-In.

This method assigns a new roll controller. Plug-Ins don't need to be concerned with freeing the previous controller if this method is called. Any previous controller assigned will be deleted by 3ds max if it is not used elsewhere in the scene.

Default Implementation:

{return FALSE;}

Prototype:

virtual Control *GetXController();

Remarks:

This method is available in release 2.0 and later only.

Returns a pointer to the 'X' sub-controller of this controller. If a controller has an 'X', 'Y', or 'Z' controller, it can implement this set of methods so that its sub-controllers can respect track view filters. Examples of controllers that have XYZ sub-controllers are the Euler angle controller or the Position XYZ controller.

Default Implementation:

{return NULL;}

Prototype:

virtual Control *GetYController();

Remarks:

This method is available in release 2.0 and later only.

Returns a pointer to the 'Y' sub-controller of this controller. If a controller has an 'X', 'Y', or 'Z' controller, it can implement this set of methods so that its sub-controllers can respect track view filters. Examples of controllers that have XYZ sub-controllers are the Euler angle controller or the Position XYZ controller.

Default Implementation:

{return NULL;}

Prototype:

virtual Control *GetZController();

Remarks:

This method is available in release 2.0 and later only.

Returns a pointer to the 'Z' sub-controller of this controller. If a controller has an 'X', 'Y', or 'Z' controller, it can implement this set of methods so that its sub-controllers can respect track view filters. Examples of controllers that have XYZ sub-controllers are the Euler angle controller or the Position XYZ controller.

Default Implementation:

{return NULL;}

Transform Controller methods

Prototype:

virtual BOOL ChangeParents(TimeValue t,const Matrix3& oldP, const Matrix3& newP,const Matrix3& tm)

Remarks:

Implemented by the Plug-In.

This method is called on transform, position, rotation, and scale controllers when their input matrix is about to change. This happens when the user links an object (either from one object to another or when the user links an object for the first time). Because a controllers transformation is relative to its parent, when the user changes parents, the transform controller will need to change itself. If a plug-in returns FALSE the node will calculate a change and call SetValue() to make the necessary adjustments at the specific time passed.

Consider the following example of a position controller:

If a node in the scene that is NOT animated, is linked to another node, this method would be called. If the method returned FALSE then the node would calculate a change and call SetValue() to make the adjustment and this would be okay. If however the node was animated there would be a problem. Say for example that an unlinked node was bouncing up and down along the world Z axis. If this node is then linked to a node that was rotated such that its Z axis was pointed in the direction of the world X axis (so the object is flipped over on its side) the linked node (whose animation keys are stored relative to its previous parent (the world)) would then begin to bounce up and down along the world X axis instead. This is because it is still moving along its parent's Z axis, but its parents Z axis is really the world X axis. Thus the object needs to be counter-rotated to compensate. Additionally, all the animation keys for the object also need to be counter-rotated. A position keyframe controller would need to implement this method to handle the correction of the object and its keyframes. See the sample code below.

Parameters:

TimeValue t

The time of the change.

const Matrix3& oldP

The old parent matrix.

const Matrix3& newP

The new parent matrix.

const Matrix3& tm

The nodes current world transformation.

Return Value:

If FALSE the node will call SetValue() to make the necessary adjustments.

Default Implementation:

{return FALSE;}

Sample Code:

This is the code used inside 3ds max' position controller. It takes the difference between the two parents and transforms the position track by that amount. It computes the relative transformation which is the old parent times the inverse of the new parent.

A plug-in could provide an implementation for this method using a similar concept.

INTERP_CONT_TEMPLATE

BOOL InterpControl<INTERP_CONT_PARAMS>::ChangeParents(

  TimeValue t,

  const Matrix3& oldP,const Matrix3& newP,const Matrix3& tm)

 {

 if (SuperClassID()==CTRL_POSITION_CLASS_ID) {

  HoldTrack();

  // Position controllers need their path counter rotated to

  // account for the new parent.

  Matrix3 rel = oldP * Inverse(newP);

  // Modify the controllers current value (the controllers cache)

  *((Point3*)(&curval)) = *((Point3*)(&curval)) * rel;

  // Then modify the keys...

  for (int i=0; i<keys.Count(); i++) {

   // All this casting keeps the compiler happy

   // for non-Point3 versions of this template.

   *((Point3*)(&keys[i].val)) =

    *((Point3*)(&keys[i].val)) * rel;

   }

  keys.KeysChanged(FALSE);

  ivalid.SetEmpty();

  return TRUE;

 } else {

  return FALSE;

  }

 }

Prototype:

virtual RefResult PinNodeChanged(RefMessage message,Interval changeInt, PartID &partID);

Remarks:

This method is available in release 2.0 and later only.

If a node is pinned to another node, and the node gets a NotifyRefChanged() message that its pinned node has changed, then this method is called on the transform controller of the node. Otherwise the controller wouldn't get notified since the controller doesn't have a reference to the pin node (but the node does). Most controllers don't really care, but the IK controller does.

Parameters:

RefMessage message

The message that was sent.

Interval changeInt

This is the interval of time over which the message is active. Currently, all controllers will receive FOREVER for this interval.

PartID &partID

This contains information specific to the message passed in. Some messages don't use the partID at all. See the section List of Reference Messages for more information about the meaning of the partID for some common messages.

Return Value:

The return value from this method is of type RefResult. This is usually REF_SUCCEED indicating the message was processed. Sometimes, the return value may be REF_STOP. This return value is used to stop the message from being propagated to the dependents of the item.

Default Implementation:

{return REF_SUCCEED;}

Prototype:

virtual void NodeIKParamsChanged();

Remarks:

This method is available in release 2.0 and later only.

This method is called on a transform controller when one of the node level IK parameters has been changed.

Default Implementation:

{}

Prototype:

virtual BOOL InheritsParentTransform()

Remarks:

Implemented by the Plug-In.

This method is only implemented by transform controllers. Transform controllers that do not inherit their parent's transform should override this method.

When a transform controller is evaluated, the parent transform is passed in to the controller and the controller typically applies its value to the parent transform. However, some controllers (for example Biped) may choose to control the TM in an absolute manner and therefore ignore the incoming parent's TM. The system needs to know about this because normally if an object and its parent are selected and the user attempts to move them only the parent transform is modified because it is assumed that the child will inherit its parents TM.

Note: This method may still return TRUE even if all the bits returned from GetInheritanceFlags() are SET to indicate that nothing is inherited from the parent. This is simply because these methods don't have the same level of 'granularity'. This method deals with the overall inheritance of the parent's transform whereas the inheritance flags relate to individual parts.

Return Value:

TRUE if the controller inherits its parents TM; otherwise FALSE. Returning FALSE will cause SetValue() to be called even in the case when the parent is also being transformed.

Default Implementation:

{ return TRUE; }

Prototype:

virtual DWORD GetInheritanceFlags();

Remarks:

Implemented by the Plug-In.

This method should be implemented by TM controllers that support filtering out inheritance. It returns the state of the transform inheritance flags. These are the values that show up in the Hierarchy branch, under the Link Info section, in the Inheritance rollup.

Return Value:

One or more of the following values:

Note: Each bit is used to represent a single inheritance. If the bit is CLEAR (OFF) it means inherit (checked in the 3ds max UI). If the bit is SET it means DON'T inherit (unchecked in the 3ds max UI).

INHERIT_POS_X

INHERIT_POS_Y

INHERIT_POS_Z

INHERIT_ROT_X

INHERIT_ROT_Y

INHERIT_ROT_Z

INHERIT_SCL_X

INHERIT_SCL_Y

INHERIT_SCL_Z

INHERIT_ALL

Default Implementation:

{return INHERIT_ALL;}

Prototype:

virtual BOOL SetInheritanceFlags(DWORD f, BOOL keepPos);

Remarks:

Implemented by the Plug-In.

This method should be implemented by TM controllers that support filtering out inheritance.

Note: Each bit is used to represent a single inheritance. This method expects the bits of the flags passed to be CLEAR (OFF) to mean DON'T inherit (unchecked in the 3ds max UI). If they are SET it means inherit (checked in the 3ds max UI).

Parameters:

DWORD f

The inheritance flags. One or more of the following values:

INHERIT_POS_X

INHERIT_POS_Y

INHERIT_POS_Z

INHERIT_ROT_X

INHERIT_ROT_Y

INHERIT_ROT_Z

INHERIT_SCL_X

INHERIT_SCL_Y

INHERIT_SCL_Z

INHERIT_ALL

BOOL keepPos

If TRUE the position of the node should remain the same; otherwise the node may move.

Return Value:

Return TRUE if TM controller supports inheritance; otherwise FALSE.

Default Implementation:

{return FALSE;}

IsLeaf / IsKeyable / IsColor

Prototype:

virtual BOOL IsLeaf()

Remarks:

Implemented by the Plug-In.

Indicates whether the controller is a leaf controller. If a controller is a leaf controller, then it MUST NOT BY DEFINITION have any sub-controllers or references. The controller should return TRUE if it has no sub-controllers. For example, a PRS controller is not a leaf controller (because it has sub-controllers for Position, Rotation and Scale), but a simple keyframed float controller is a leaf controller.

Return Value:

TRUE if the controller is a leaf controller; FALSE otherwise.

Default Implementation:

{return TRUE;}

Prototype:

virtual int IsKeyable()

Remarks:

Implemented by the Plug-In.

Indicates if the controller is a keyframe controller. This means the controller stores keys at certain frames and interpolates between keys at other times.

Return Value:

Nonzero if the controller is a keyframe controller; zero otherwise.

Default Implementation:

{return 1;}

Prototype:

virtual BOOL IsColorController()

Remarks:

Implemented by the Plug-In.

Implemented by any Point3 controller that wishes to indicate that it is intended to control floating point RGB color values. Returns TRUE to indicate that it controls float color values; otherwise FALSE.

Default Implementation:

{return FALSE;}

Ease / Mulitplier Curve and ORT methods

The following methods involve ease and multiplier curves. See the sample code after the methods GetValue() and SetValue() to see how these methods are used.

Prototype:

virtual BOOL CanApplyEaseMultCurves()

Remarks:

Implemented by the Plug-In.

This method determines if a controller may have ease or multiplier curves applied to it. This method defaults to returning TRUE, but can be implemented to return FALSE by a controller that does not wish to let ease or multiplier curves be applied to it.

Return Value:

TRUE to allow the application of ease and multiplier curves; otherwise FALSE.

Default Implementation:

{return TRUE;}

Prototype:

TimeValue ApplyEase(TimeValue t,Interval &valid);

Remarks:

Implemented by the System.

The controller calls this method to pipe the TimeValue passed through the ease curve to get the modified TimeValue.

Parameters:

TimeValue t

The time to have modified by the ease curve.

Interval &valid

The validity interval of the TimeValue returned.

Return Value:

The modified TimeValue.

Prototype:

void AppendEaseCurve(Control *cont);

Remarks:

Implemented by the System.

Adds an ease curve to the specified controller.

Parameters:

Control *cont

The controller that the ease curve will be applied to.

Prototype:

int NumEaseCurves();

Remarks:

Implemented by the System.

Returns the number of ease curves applied to the controller.

Prototype:

void DeleteEaseCurve(int i);

Remarks:

Implemented by the System.

Deletes the 'i-th' ease curve from the controller.

Parameters:

int i

The index of the ease curve to delete.

Prototype:

float GetMultVal(TimeValue t,Interval &valid);

Remarks:

Implemented by the System.

Retrieves a floating point value that is the product of all the multiplier curves at the specified time.

Parameters:

TimeValue t

The time to retrieve the

Interval &valid

The validity interval of the value.

Return Value:

The product of all the multiplier curves applied to the controller.

Prototype:

void AppendMultCurve(Control *cont);

Remarks:

Implemented by the System.

Adds a multiplier curve to the specified controller.

Parameters:

Control *cont

The controller to have the multiplier curve added.

Prototype:

int NumMultCurves();

Remarks:

Implemented by the System.

Returns the number of multiplier curves assigned to the controller.

Prototype:

void DeleteMultCurve(int i);

Remarks:

Implemented by the System.

Deletes the 'i-th' multiplier curve from this controller.

Parameters:

int i

The index of the curve to delete.

When a user brings up the track view and sets the out of range types for the controller the following two methods are called.

Prototype:

virtual int GetORT(int type)

Remarks:

Implemented by the System.

Returns the specified Out of Range Type used by the controller. The system handles this method but the controller needs to process the ORT in its implementation of GetValue() and SetValue().

Parameters:

int type

One of the following values:

ORT_BEFORE - leading up to the pattern

ORT_AFTER - beyond the key pattern

Return Value:

One of the following values:

See List of Out of Range Types.

Prototype:

virtual void SetORT(int ort,int type);

Remarks:

Implemented by the System.

Sets the specified Out of Range Type to be used by the controller. The system handles this method but the controller needs to process the ORT in its implementation of GetValue() and SetValue().

Parameters:

int ort

See List of Out of Range Types.

int type

One of the following values:

ORT_BEFORE - leading up to the pattern

ORT_AFTER - beyond the key pattern

Prototype:

virtual void EnableORTs(BOOL enable);

Remarks:

Implemented by the System.

Sets the enabled/disabled state for Out of Range Types. If disabled, this temporarily causes the Out of Range Types to behave as if set to constant. This can be used if you want to modify a controller but don't want ORT mapping for ORT_LOOP, ORT_CYCLE, or ORT_OSCILLATE.

Parameters:

BOOL enable

TRUE to enable ORTs; FALSE to disable.

The methods below are used with Inverse Kinematics. The following is an overview of the control flow of the system calling these IK methods on the controller during IK calculations:

First the system calls EnumIKParams() once to get the number of IK parameters (degrees of freedom) from the plug-in. This lets the system know how many parameters the controller has. This happens only once when the entire chain is set up. For example during interactive IK, when the user presses the mouse button down the chain is set up. In applied IK when the user presses the Apply IK button the chain is set up.

Next the IK task is defined. For example, if the user is doing interactive IK they press the mouse button down and the IK chain is set up. Next they move the mouse a few pixels. This results in a delta for the end effector. This defines the task for the end effector. The end effector wants to move some delta or rotate some amount. This has defined a task to be solved.

In order to solve the task the system must iterate towards a solution.

To do this it first needs to compute the derivatives of all the parameters. The system calls CompDerivs() on the controller. The controller computes the derivative and tells the system by calling methods on the IKDeriv argument passed to the method. It provides its derivative for each of the parameters.

Next the system uses this derivative information to formulate a change in the parameter that will get closer to the solution. The system then computes an amount to change the parameter and then calls IncIKParams(). The delta passed to this method is the amount of change it wants in the parameter. The controller then applies this change (perhaps not all of it due to constraints it might have) and returns the amount that was applied.

This has completed a single iteration. All the parameters have been adjusted a bit and hopefully we have move closer to a solution. At this point the system calls CompDerivs() again because just moving the parameters a small amount may have actually changed the derivative. The cycle begins again.

When the IK solver has reached its solution the IK task is finished. Below are the methods that must be implemented to participate in this process.

Prototype:

virtual void EnumIKParams(IKEnumCallback &callback)

Remarks:

Implemented by the Plug-In.

This tells the system how many parameters the controller has. A controller can have as many IK parameters as it wants. An IK parameter corresponds to a degree of freedom in IK. The parameter is a floating point scalar value. For example a position controller has three degrees of freedom (X, Y, Z) and thus three parameters that IK can vary in its solution. The path controller has only a single parameter (degree of freedom) - the position along the path. The 3ds max user may set the number of degrees of freedom. For example, a user can specify that a rotation controller cannot rotate about one or more axes. These are then no longer degrees of freedom or IK parameters.

This method is called by the system so the plug-in can specify how many IK parameters it has. It does this by calling the provided callback object proc() method once for each parameter it has. It passes a pointer to itself and the index of the IK parameter. For example a position controller with three degrees of freedom (and thus three IK parameters) would call the callback.proc() three time passing an index of 0, then 1, then 2. See the sample code below.

Parameters:

IKEnumCallback &callback

This callback is provided by the system and should be called by the plug-in once for each IK parameter the plug-in has. See Class IKEnumCallback.

Default Implementation:

{}

Sample Code:

void QuatEnumIKParams(Control *cont,IKEnumCallback &callback)

 {

 JointParams *jp = (JointParams*)cont->GetProperty(PROPID_JOINTPARAMS);

 for (int i=0; i<3; i++) {

  if (!jp || jp->Active(i)) {

   callback.proc(cont,i);

   }

  }

 }

Prototype:

virtual BOOL CompDeriv(TimeValue t,Matrix3& ptm,

 IKDeriv& derivs,DWORD flags)

Remarks:

Implemented by the Plug-In.

This method is used to determine what effect a change in the parameter has on the end effector. This is the derivative of the end effector with respect to the parameter. What the derivative means in this case is what happens to the end effector if the parameter is changed by some small delta.

The plug-in provides the derivatives to the system calling derivs.DP() and derivs.DR(). It should call derivs.DP() and derivs.DR() in the same order as the callback.proc() was called in the NumIKParams() method implementation.

When the controller computes the derivative it should apply itself to the parent matrix. For example a position controller would compute its derivative based on the parent and the position of the end effector and then apply itself to the parent matrix. If it does apply itself to the parent it should return TRUE. If it does not apply itself it should return FALSE.

Parameters:

TimeValue t

Specifies the time to compute the derivative.

Matrix3& ptm

The parents transformation.

IKDeriv& derivs

This class provides methods the plug-in calls to set the derivatives. See Class IKDeriv.

DWORD flags

One of the following values:

POSITION_DERIV

Indicates that derivs.DP() should be called.

ROTATION_DERIV

Indicates that derivs.DR() should be called.

Return Value:

If a controller isn't participating in IK then it should return FALSE and the client (usually PRS) will apply the controller's value to the parent TM.

Default Implementation:

{return FALSE;}

Sample Code:

The following sample code shows how the quaternion controller has implemented this method. Note that the method loops based on the number of end effectors, and calls derivs.NextDOF() after each iteration.

BOOL QuatCompDeriv(Control *cont,TimeValue t,Matrix3& ptm,

  IKDeriv& derivs,DWORD flags)

 {

 JointParams *jp = (JointParams*)cont->GetProperty(PROPID_JOINTPARAMS);

 Quat q;

 Interval valid;

 for (int i=0; i<3; i++) {

  if (!jp || jp->Active(i)) {

   for (int j=0; j<derivs.NumEndEffectors(); j++) {

    Point3 r = derivs.EndEffectorPos(j) - ptm.GetRow(3);

    if (flags&POSITION_DERIV) {

     derivs.DP(CrossProd(ptm.GetRow(i),r),j);

     }

    if (flags&ROTATION_DERIV) {

     derivs.DR(ptm.GetRow(i),j);

     }

    }

   derivs.NextDOF();

   }

  }

 return FALSE;

 }

Prototype:

virtual float IncIKParam(TimeValue t,int index,float delta);

Remarks:

Implemented by the Plug-In.

When the system has computed a change in the parameter it will call this method. The controller should increment the specified parameter by the specified delta. The controller can increment the parameter less than this delta if it needs to. This could be for several reasons:

1 Its parameter may be constrained to lie within a specific interval. It would not want to add a delta that took the parameter outside of this interval.

2 It was asked to calculate a constant partial derivative for a linkage that could be nonlinear. Therefore the derivative may have only been an instantaneous approximation. Due to the locality of the IK solution, the controller might not want to allow a delta that was too large.

After the controller has applied the delta, it needs to indicate to the system how much of the delta was used.

Parameters:

TimeValue t

The time of the increment.

int index

Specifies the IK parameter to increment.

float delta

The delta to apply to the parameter. The controller can increment the parameter less than this delta if it needs to in order to accommodate a limit it has. This methods returns the amount that was actually incremented.

Return Value:

The amount the parameter was actually incremented. This allows the IK solver to know the value was not incremented the full amount.

Default Implementation:

{return 0.0f;}

Sample Code:

float QuatIncIKParam(Control *cont,TimeValue t,int index,float delta)

 {

 JointParams *jp = (JointParams*)cont->GetProperty(PROPID_JOINTPARAMS);

 if ((float)fabs(delta)>MAX_IKROT) delta = MAX_IKROT * SGN(delta);

 if (jp) {

  float v=0.0f;

  if (jp->Limited(index)) {

   Quat q;

   Interval valid;

   cont->GetValue(t,&q,valid,CTRL_ABSOLUTE);

   v = GetRotation(q,index);

   }

  delta = jp->ConstrainInc(index,v,delta);

  }

 Point3 a(0,0,0);

 a[index] = 1.0f;

 AngAxis aa(a,-delta);

 cont->SetValue(t,&aa,FALSE,CTRL_RELATIVE);

 return delta;

 }

static float GetRotation(Quat& q,int axis)

 {

 Matrix3 tm;

 q.MakeMatrix(tm);

 MRow* t = tm.GetAddr();

 int n = (axis+1)%3, nn = (axis+2)%3;

 if (fabs(t[n][axis]) < fabs(t[nn][axis])) {

  return (float)atan2(t[n][nn],t[n][n]);

 } else {

  return -(float)atan2(t[nn][n],t[nn][nn]);

  }

 }

Prototype:

virtual void ClearIKParam(Interval iv,int index)

Remarks:

Implemented by the Plug-In.

This method is called to have the controller delete its keys. If the user has the 'Clear Keys' check box checked when they press the 'Apply IK' button, this method is called to have the controller deletes keys in the given interval for the specified degree of freedom.

Parameters:

Interval iv

The interval over which the keys should be deleted.

int index

Specified the degree of freedom (parameter) that the keys should be deleted for.

Default Implementation:

{return;}

Prototype:

virtual void InitIKJoints(InitJointData *posData,InitJointData *rotData)

Remarks:

Implemented by the Plug-In.

This is an optional method that can be implemented by controllers that support IK to initialize their joint parameters based on data loaded from 3D Studio R4/ DOS files.

Parameters:

InitJointData *posData

The position data from the 3DS file. See Class InitJointData.

InitJointData *rotData

The rotation data from the 3DS file.

Default Implementation:

{}

Prototype:

virtual void InitIKJoints2(InitJointData2 *posData, InitJointData2 *rotData);

Remarks:

Implemented by the Plug-In.

This method is available in release 4.0 and later only.

This is an optional method that can be implemented by controllers that support IK to initialize their joint parameters based on data loaded from 3D Studio R4/ DOS files.

Parameters:

InitJointData2 *posData

The position data from the 3DS file. See Class InitJointData2.

InitJointData2 *rotData

The rotation data from the 3DS file.

Default Implementation:

{}

Prototype:

virtual BOOL GetIKJoints(InitJointData *posData,InitJointData *rotData);

Remarks:

This method is available in release 2.0 and later only.

This method retrieves the IK joint parameter data from the UI.

Parameters:

InitJointData *posData

Points to the object to hold the position data. See Class InitJointData.

InitJointData *rotData

Points to the object to hold the rotation data.

Return Value:

TRUE if the data was retrieved; otherwise FALSE.

Default Implementation:

{return FALSE;}

Prototype:

virtual BOOL GetIKJoints2(InitJointData2 *posData, InitJointData2 *rotData);

Remarks:

This method is available in release 4.0 and later only.

This method retrieves the IK joint parameter data from the UI.

Parameters:

InitJointData2 *posData

Points to the object to hold the position data. See Class InitJointData2.

InitJointData2 *rotData

Points to the object to hold the rotation data.

Return Value:

TRUE if the data was retrieved; otherwise FALSE.

Default Implementation:

{return FALSE;}

Prototype:

virtual BOOL GetDOFParams(TimeValue t, Matrix3 &ptm, DOFParams &dofs, BOOL nodeSel);

Remarks:

This method is available in release 2.0 and later only.

The new IK system has some axes gizmos which show the degrees of freedom, etc. This method is called by the system

Parameters:

TimeValue t

The current time.

Matrix3 &ptm

The parent matrix.

DOFParams &dofs

This is the structure to be filled in. See Class DOFParams.

BOOL nodeSel

TRUE if the node is currently selected; otherwise FALSE.

Return Value:

TRUE if the method is implemented; FALSE otherwise.

Default Implementation:

{return FALSE;}

Prototype:

virtual BOOL CreateLockKey(TimeValue t, int which);

Remarks:

This method is available in release 2.0 and later only.

This method is called to create a locking key. This is a key that looks back to the previous key and creates a new key at the specified time which matches the previous key in value. It also adjusts the parameters for the key such that the value stays constant from the previous key to this key. For instance, the TCB controller will set the previous and new continuity to 0. The Bezier controller sets the out tangent type of the previous key to linear and the in tangent type of the new key to linear.

Parameters:

TimeValue t

The time to create the key.

int which

Specifies which type of key to create: 0 for position, 1 for rotation.

Return Value:

TRUE if the method is implemented; FALSE otherwise.

Default Implementation:

{return FALSE;}

Prototype:

virtual void MirrorIKConstraints(int axis, int which, BOOL pasteMirror=FALSE);

Remarks:

This method is available in release 2.0 and later only.

This method is called to mirror the specified IK constraints about the specified axis. When IK constraints are mirrored they need to be updated to reflect the new orientation. For instance, if you set the constraints for a left arm to bend only +90 degrees along one axis and then copied these to a right arm the joint would bend backwards. What you need to do is provides the appropriate compensation so the orientation is kept proper.

Parameters:

int axis

Specifies the axis of reflection: 0 for X, 1 for Y, 2 for Z.

int which

Specifies which type of constraints are being mirrored: 0 for position, 1 for rotation.

BOOL pasteMirror=FALSE

TRUE if the mirror is being done as part of a paste operation; otherwise FALSE (for example if the mirror was being done with the mirror tool).

Return Value:

TRUE if the method is implemented; FALSE otherwise.

Default Implementation:

{}

Prototype:

virtual BOOL TerminateIK();

Remarks:

This method is available in release 2.0 and later only.

User can specifiy a node as a terminator. This method gives the associated controller the chance to specify that it's terminated.

Return Value:

TRUE if the method is implemented; FALSE otherwise.

Default Implementation:

{return FALSE;}

Prototype:

virtual void NodeIKParamsChanged();

Remarks:

This method is available in release 2.0 and later only.

This method is called on a transform controller when one of the node level IK parameters has been changed

Default Implementation:

{}

Prototype:

virtual void TMInvalidated();

Remarks:

This method is available in release 2.0 and later only.

This method is called in a transform controller when a node invalidates its TM cache

Default Implementation:

{}

Prototype:

virtual BOOL OKToBindToNode(INode *node);

Remarks:

This method is available in release 2.0 and later only.

This method lets a TM controller determine if it's OK to IK bind to a particular node.

Parameters:

INode *node

Points to the node to check.

Return Value:

TRUE if it's okay to bind; FALSE if it's not.

Default Implementation:

{return TRUE;}

Prototype:

virtual BOOL PreventNodeDeletion();

Remarks:

This method is available in release 2.0 and later only.

This method is called on TM controllers so that system slave controllers can prevent the Interface::DeleteNode() API from deleting them. Note that DeleteNode()has an optional parameter to override this so master controllers can easily ddelete slave nodes if they want to.

Return Value:

TRUE to prevent deletion; FALSE to allow it.

Default Implementation:

{return FALSE;}

Prototype:

virtual float EvalVisibility(TimeValue t, View &view, Box3 pbox, Interval &valid);

Remarks:

This method is available in release 2.0 and later only.

The Level of Detail utility lets you construct an object that alters its geometric complexity (or level of detail) based on its size in the rendered image. You do this by creating several versions of the same object -- each with different levels of detail, grouping them as one, and then assigning the Level of Detail utility, which automatically creates a special LOD controller as a Visibility track. The LOD controller then hides and unhides the various objects in the group, depending on their size in the rendered scene.

This method is called on visibility float controllers with view related parameters. This is used by the Level of Detail controller to allow view dependent visibility.

Parameters:

TimeValue t

The time at which to evaluate.

View &view

This class contains information about the view being rendered. This includes information such as the image width and height, the projection type, and matrices to convert between world to view and world to camera. See Class View.

Box3 pbox

The bounding box of the node that's being evaluated.

Interval &valid

This interval should be updated to reflect the validity of the visibility controller.

Return Value:

The visibility of the object at the specified time.

Default Implementation:

The default implementation will simply call GetValue().

Prototype:

virtual BOOL VisibleInViewports();

Remarks:

This method is available in release 2.0 and later only.

This method is called on visibility controllers. This gives them the option to completely hide an object in the viewports.

Return Value:

TRUE if the object is visible in the viewport; FALSE if invisible.

Default Implementation:

{return TRUE;}

Prototype:

virtual BOOL CanInstanceController();

Remarks:

This method is available in release 2.0 and later only.

Called on transform controllers or visibility controllers when a node is cloned and the user has chosen to instance

Return Value:

 

Default Implementation:

{return TRUE;}

Prototype:

void CloneControl(Control *ctrl,RemapDir &remap);

Remarks:

This method is available in release 2.0 and later only.

This method is implemented by the System. It should be called by any leaf controller's Clone() method so that ease and multipier curves are cloned.

Parameters:

Control *ctrl

Points to the cloned controller (the new one).

RemapDir &remap

The RemapDir passed to this controller's Clone() method.

Sample Code:

RefTargetHandle Clone(RemapDir& remap=NoRemap()) {

 ExprControl *ctrl = new ExprControl(this->type, *this);

 CloneControl(ctrl,remap);

 return ctrl;

 }  

The following functions are not part of class Control but are provided for use. These functions are for use by Position or Rotation controllers. Position controllers would use the Point3 versions and Rotation controllers would use the Quat versions

 

void QuatEnumIKParams(Control *cont,IKEnumCallback &callback);

 

BOOL QuatCompDeriv(Control *cont,TimeValue t,Matrix3& ptm,

 IKDeriv& derivs,DWORD flags);

 

float QuatIncIKParam(Control *cont,TimeValue t,int index,float delta);

 

void QuatBeginIKParams(Control *cont,IObjParam *ip, ULONG flags,

 Animatable *prev);

 

void Point3EnumIKParams(Control *cont,IKEnumCallback &callback);

 

BOOL Point3CompDeriv(Control *cont,TimeValue t,Matrix3& ptm,

 IKDeriv& derivs,DWORD flags);

 

float Point3IncIKParam(Control *cont,TimeValue t,int index,float delta);

 

void Point3BeginIKParams(Control *cont,IObjParam *ip,

 ULONG flags,Animatable *prev);

The methods below deal with copying and pasting IK parameters in the Hierarchy branch.

Prototype:

virtual BOOL CanCopyIKParams(int which)

Remarks:

Implemented by the Plug-In.

This method returns TRUE if the controller has IK parameters it can copy and FALSE otherwise.

Parameters:

int which

One of the following values:

COPYPASTE_IKPOS

COPYPASTE_IKROT

Return Value:

TRUE if the controller can copy the specified IK parameters; otherwise FALSE.

Default Implementation:

{return FALSE;}

Prototype:

virtual IKClipObject *CopyIKParams(int which)

Remarks:

Implemented by the Plug-In.

This method is called to have the controller copy the specified IK parameters to an IKClipObject and return a pointer to it. The plug-in should derive a class from the IKClipObject, put its data in the class, and return a new instance of it. See Class IKClipObject.

Parameters:

int which

One of the following values:

COPYPASTE_IKPOS

COPYPASTE_IKROT

Default Implementation:

{return NULL;}

Prototype:

virtual BOOL CanPasteIKParams(IKClipObject *co,int which)

Remarks:

Implemented by the Plug-In.

Returns TRUE if the controller can paste the specified IK parameters; otherwise FALSE.

Parameters:

IKClipObject *co

A pointer to the current IKClipObject in the clipboard. This class identifies the creator of the clip object. See Class IKClipObject. The plug-in should look at the IDs in the IKClipObject to make sure it matches this controller. If it does not, the plug-in should return FALSE.

int which

One of the following values:

COPYPASTE_IKPOS

COPYPASTE_IKROT

Return Value:

TRUE if the controller can paste the specified IK parameters; otherwise FALSE.

Default Implementation:

{return FALSE;}

Prototype:

virtual void PasteIKParams(IKClipObject *co,int which)

Remarks:

Implemented by the Plug-In.

This method is called to have the controller paste the specified IK parameters from the specified IKClipObject to itself.

Parameters:

IKClipObject *co

A pointer to an IKClipObject. See Class IKClipObject.

int which

One of the following values:

COPYPASTE_IKPOS

COPYPASTE_IKROT

Default Implementation:

{}

Controllers that wish to have an gizmo (apparatus) available in the scene will implement these methods.

Prototype:

virtual int Display(TimeValue t, INode* inode,

 ViewExp *vpt, int flags);

Remarks:

Implemented by the Plug-In.

This is called by the system to have the controller display its gizmo. When a controller is being edited in the Motion branch, this method is called to allow it to display any apparatus it may have in the scene. Note that Display() is only called on Transform Controllers. It is not called only any sub-controllers, for example it wouldn't be called on the position controller of a PRS transform controller.

In R4 and higher however the display method WILL be called on Position, Rotation and scale controllers as well

Parameters:

TimeValue t

The time to display the object.

INode* inode

The node to display.

ViewExp *vpt

An interface pointer that exposes methods the plug-in may call related to the viewports.

int flags

See List of Display Flags.

Return Value:

Nonzero if the item was displayed; otherwise 0.

Default Implementation:

{ return 0; }

Prototype:

virtual int HitTest(TimeValue t, INode* inode, int type,

 int crossing, int flags, IPoint2 *p, ViewExp *vpt)

Remarks:

Implemented by the Plug-In.

This method is called to determine if the specified screen point intersects the controller gizmo. The method returns nonzero if the gizmo was hit; otherwise 0.

Parameters:

TimeValue t

The time to perform the hit test.

INode* inode

A pointer to the node whose gizmo should be tested.

int type

The type of hit testing to perform. See Hit Test Types for details.

int crossing

The state of the crossing setting. If TRUE crossing selection is on.

int flags

The hit test flags. See Hit Test Flags for details.

IPoint2 *p

The screen point to test.

ViewExp *vpt

An interface pointer that may be used to call methods associated with the viewports.

Return Value:

Nonzero if the controller gizmo was hit; otherwise 0.

Default Implementation:

{ return 0; }

Prototype:

virtual void GetWorldBoundBox(TimeValue t,INode* inode,

 ViewExp *vpt, Box3& box)

Remarks:

Implemented by the Plug-In.

This is the world space bounding box of the controllers gizmo.

Parameters:

TimeValue t

The time to retrieve the bounding box.

INode * inode

The node to calculate the bounding box for.

ViewExp* vp

An interface pointer that exposes portions of View3D that are exported for use by plug-ins.

Box3& box

The bounding box is returned through box.

Default Implementation:

{}

Prototype:

virtual void ActivateSubobjSel(int level, XFormModes& modes)

Remarks:

Implemented by the Plug-In.

When the user changes the selection of the sub-object drop down, this method is called to notify the plug-in. This method should provide instances of a class derived from CommandMode to support move, rotate, non-uniform scale, uniform scale, and squash modes. These modes replace their object mode counterparts however the user still uses the move/rotate/scale tool buttons in the toolbar to activate them. If a certain level of sub-object selection does not support one or more of the modes NULL may be passed. If NULL is specified the corresponding toolbar button will be grayed out.

Parameters:

int level

The sub-object selection level the command modes should be set to support. A level of 0 indicates object level selection. If level is greater than or equal to 1 the index refers to the types registered by the object in the order they appeared in the list when registered by Interface::RegisterSubObjectTypes(). See Class Interface.

XFormModes& modes

The command modes to support. See Class XFormModes.

Default Implementation:

{}

Prototype:

virtual void SelectSubComponent(CtrlHitRecord *hitRec,

 BOOL selected, BOOL all, BOOL invert=FALSE)

Remarks:

Implemented by the Plug-In.

This method is called to change the selection state of the component identified by hitRec.

Parameters:

CtrlHitRecord *hitRec

Identifies the component whose selected state should be modified. See Class CtrlHitRecord.

BOOL selected

TRUE if the item should be selected; FALSE if the item should be de-selected.

BOOL all

TRUE if the entire object should be selected; FALSE if only the portion of the identified by hitRec.

BOOL invert=FALSE

This is set to TRUE when all is also set to TRUE and the user is holding down the Shift key while region selecting in select mode. This indicates the items hit in the region should have their selection state inverted

Default Implementation:

{}

Prototype:

virtual void ClearSelection(int selLevel)

Remarks:

Implemented by the Plug-In.

This method is called to clear the selection for the given sub-object level. All sub-object elements of this type should be deselected.

Parameters:

int selLevel

Specifies the selection level to clear.

Default Implementation:

{}

Prototype:

virtual int SubObjectIndex(CtrlHitRecord *hitRec)

Remarks:

Implemented by the System.

Returns the index of the sub-object element identified by the CtrlHitRecord hitRec. The sub-object index identifies a sub-object component. The relationship between the index and the component is established by the controller. For example a controller may allow the user to select a group of footprints and these groups may be identified as group 0, group 1, group 2, etc. Given a hit record that identifies a footstep, the controller's implementation of this method would return the group index that the footprint belonged to.

Parameters:

CtrlHitRecord *hitRec

Identifies the component whose index should be returned. See Class CtrlHitRecord.

Return Value:

The index of the sub-object element.

Default Implementation:

{return 0;}

Prototype:

virtual void SelectAll(int selLevel);

Remarks:

This method is available in release 2.0 and later only.

This method is called to select every element of the given sub-object level. This will be called when the user chooses Select All from the 3ds max Edit menu.

Parameters:

int selLevel

Specifies the selection level to select.

Default Implementation:

{}

Prototype:

virtual void InvertSelection(int selLevel);

Remarks:

This method is available in release 2.0 and later only.

This method is called to invert the specified sub-object level. If the element is selected it should be deselected. If it's deselected it should be selected. This will be called when the user chooses Select Invert from the 3ds max Edit menu.

Parameters:

int selLevel

Specifies the selection level to invert.

Default Implementation:

{}

When the user is in a sub-object selection level, the system needs to get the reference coordinate system definition from the current controller being edited so that it can display the axis.

Two methods allows the system to do this:

Prototype:

virtual void GetSubObjectCenters(SubObjAxisCallback *cb,

 TimeValue t,INode *node)

Remarks:

Implemented by the Plug-In.

When the user is in a sub-object selection level, the system needs to get the reference coordinate system definition from the current controller being edited so that it can display the axes. This method specifies the position of the center. The plug-in enumerates its centers and calls the callback cb once for each. See Sub-Object Coordinate Systems.

Parameters:

SubObjAxisCallback *cb

The callback object whose methods may be called. See Class SubObjAxisCallback.

TimeValue t

The time to enumerate the centers.

INode *node

A pointer to the node.

Default Implementation:

{}

Prototype:

virtual void GetSubObjectTMs(SubObjAxisCallback *cb,

 TimeValue t,INode *node)

Remarks:

Implemented by the Plug-In.

When the user is in a sub-object selection level, the system needs to get the reference coordinate system definition from the current controller being edited so that it can display the axes. This method returns the axis system of the reference coordinate system. The plug-in enumerates its TMs and calls the callback cb once for each. See Sub-Object Coordinate Systems.

Parameters:

SubObjAxisCallback *cb

The callback object whose methods may be called. See Class SubObjAxisCallback.

TimeValue t

The time to enumerate the TMs.

INode *node

A pointer to the node.

Default Implementation:

{}

 

Modify sub-object apparatuses

Prototype:

virtual void SubMove( TimeValue t, Matrix3& partm,

 Matrix3& tmAxis, Point3& val,

 BOOL localOrigin=FALSE )

Remarks:

Implemented by the Plug-In.

When this method is called the plug-in should respond by moving its selected sub-object components.

Parameters:

TimeValue t

The time of the transformation.

Matrix3& partm

The 'parent' transformation matrix. This matrix represents a transformation that would take points in the controller's space and convert them into world space points.

Matrix3& tmAxis

The matrix that represents the axis system. This is the space in which the transformation is taking place.

Point3& val

This value is a vector with X, Y, and Z representing the movement along each axis.

BOOL localOrigin=FALSE

When TRUE the transformation is occurring about the sub-object's local origin.

Default Implementation:

{}

Prototype:

virtual void SubRotate( TimeValue t, Matrix3& partm,

 Matrix3& tmAxis, Quat& val, BOOL localOrigin=FALSE ){}

Remarks:

Implemented by the Plug-In.

When this method is called the plug-in should respond by rotating its selected sub-object components.

Parameters:

TimeValue t

The time of the transformation.

Matrix3& partm

The 'parent' transformation matrix. This matrix represents a transformation that would take points in the controller's space and convert them into world space points.

Matrix3& tmAxis

The matrix that represents the axis system. This is the space in which the transformation is taking place.

Quat& val

The amount to rotate the selected components.

BOOL localOrigin=FALSE

When TRUE the transformation is occurring about the sub-object's local origin. Note: This information may be passed onto a transform controller (if there is one) so they may avoid generating 0 valued position keys for rotation and scales. For example if the user is rotating an item about anything other than its local origin then it will have to translate in addition to rotating to achieve the result. If a user creates an object, turns on the animate button, and rotates the object about the world origin, and then plays back the animation, the object does not do what the was done interactively. The object ends up in the same position, but it does so by both moving and rotating. Therefore both a position and a rotation key are created. If the user performs a rotation about the local origin however there is no need to create a position key since the object didn't move (it only rotated). So a transform controller can use this information to avoid generating 0 valued position keys for rotation and scales.

Prototype:

virtual void SubScale(TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE)

Remarks:

Implemented by the Plug-In.

When this method is called the plug-in should respond by scaling its selected sub-object components.

Parameters:

TimeValue t

The time of the transformation.

Matrix3& partm

The 'parent' transformation matrix. This matrix represents a transformation that would take points in the modifier's space and convert them into world space points.

Matrix3& tmAxis

The matrix that represents the axis system. This is the space in which the transformation is taking place.

Point3& val

This value is a vector with X, Y, and Z representing the scale along X, Y, and Z respectively.

BOOL localOrigin=FALSE

When TRUE the transformation is occurring about the sub-object's local origin. See the note above in the Rotate method.

Prototype:

virtual BOOL RescaleTime(Interval oseg, Interval nseg);

Remarks:

This method is available in release 3.0 and later only.

This method is called when the user rescales time in the time configuration dialog. If FALSE is returned from this method then Animatable::MapKeys() will be used to perform the scaling. Controllers can override this method to handle things like rescaling tagents that MapKeys() won't affect and return TRUE if they don't want MapKeys() to be called.

Parameters:

Interval oseg

The old time segment.

Interval nseg

The new time segment.

Default Implementation:

{return FALSE;}

Prototype:

virtual int GetDrawPixelStep();

Remarks:

This method is available in release 4.0 and later only.

Prior to R4 TrackView was using static defines to determines the number samples/pixel it used to draw and compute curve extents. Now a controller can override these defaults by implementing GetDrawPixelStep() and GetExtentTimeStep().

This method allows a control to get sampled at a different rate than what trackview does by default so the controller can speed up redraws. It returns the pixel sample rate for when the curve is drawn.

Default Implementation:

{return 5;}

Prototype:

virtual int GetExtentTimeStep();

Remarks:

This method is available in release 4.0 and later only.

Prior to R4 TrackView was using static defines to determines the number samples/pixel it used to draw and compute curve extents. Now a controller can override these defaults by implementing GetDrawPixelStep() and GetExtentTimeStep().

This method returns the ticks sample rate used when the curve is checked for its Y extents.

Default Implementation:

{return 40;}

Load / Save

Prototype:

IOResult Save(ISave *isave);

Remarks:

Implemented by the System.

The default implementation of Save() handles the saving of the out of range types. The plug-in should call this method from its implementation of Save(). The plug-in should call this method before it saves any of its chunks.

The out of range types are saved in these chunks:

CONTROLBASE_CHUNK

INORT_CHUNK

OUTORT_CHUNK

Parameters:

ISave *isave

This pointer may be used to call methods to write data to disk. See Class ISave.

Return Value:

One of the following values:

IO_OK - The result was acceptable - no errors.

IO_ERROR - This is returned if an error occurred.

Prototype:

IOResult Load(ILoad *iload);

Remarks:

Implemented by the System.

The default implementation of Load() handles the loading of the out of range types. The plug-in should call this method from its implementation of Load(). The plug-in should call this method before it loads any of its chunks.

The out of range types are saved in these chunks:

CONTROLBASE_CHUNK

INORT_CHUNK

OUTORT_CHUNK

Parameters:

ILoad *iload

This pointer may be used to call methods to load data from disk. See Class ILoad.

Return Value:

One of the following values:

IO_OK - The result was acceptable - no errors.

IO_ERROR - This is returned if an error occurred.

These are implemented to handle ease curves.

If a controller is a leaf controller, then it MUST NOT BY DEFINITION have any sub-controllers or references. If it is a leaf controller, then these are implemented to handle the ease curve list. If it is NOT a leaf controller, then these can be overridden.

From ReferenceMaker. See this class for details on these methods.

int NumRefs();

RefTargetHandle GetReference(int i);

void SetReference(int i, RefTargetHandle rtarg);

From Animatable. See this class for details on these methods.

int NumSubs();

Animatable* SubAnim(int i);

TSTR SubAnimName(int i);

Default implementations of some Animatable methods:

void* GetInterface(ULONG id);

int PaintFCurves(ParamDimensionBase *dim,

 HDC hdc, Rect& rcGraph, Rect& rcPaint,

 float tzoom, int tscroll, float vzoom, int vscroll, DWORD flags );

int GetFCurveExtents(ParamDimensionBase *dim,

 float &min, float &max, DWORD flags);