Class Modifier

3DS Max Plug-In SDK

Class Modifier

See Also: Class BaseObject, Class ObjectState, Class ModContext, Class ModContextEnumProc, Class Interval, Class ISave, Class ILoad, Class Class_ID.

class Modifier : public BaseObject

Description:

This is the class from which you may derive Object Space and Space Warp (World Space) Modifier plug-ins. This class is subclassed off of BaseObject so the modifier can put up a graphical representation in the viewport to use as a gizmo.

Method Groups:

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

Modifier Name Access

InputType, ChannelsUsed, ChannelsChanged.

Object Modification Methods

Topology Dependence

Loading and Saving Methods

Notification of Change

Modifier Stack Enable/Disable Methods

ModContext Enumeration

Validity Intervals

Methods:

Modifier Name Access

Prototype:

virtual TSTR GetName();

Remarks:

Implemented by the System.

Returns the name of the modifier.

Prototype:

virtual void SetName(TSTR n);

Remarks:

Implemented by the System.

Sets the name of the modifier to the name passed.

Parameters:

TSTR n

Specifies the name to set.

InputType, ChannelsUsed, ChannelsChanged.

Prototype:

virtual Class_ID InputType()=0;

Remarks:

Implemented by the Plug-In.

This is the type of object that the modifier knows how to modify. Simple modifiers that just modify points of an object can operate on generic 'Deformable' objects. Deformable objects are any type of object that has points. A modifier could also work on a particular type of object such as a TriObject or PatchObject.

Return Value:

The Class_ID of the item. You can request any Class_ID for your input type. For example, Class_ID(OMNI_LIGHT_CLASS_ID, 0). See List of Class_IDs.

Prototype:

virtual ChannelMask ChannelsUsed()=0;

Remarks:

Implemented by the Plug-In.

These are channels that the modifier needs in order to perform its modification. This should at least include the channels specified in ChannelsChanged() but may include more.

Note that ChannelsUsed() is called many times but the channels returned should not change on the fly.

Return Value:

The channels required. See List of Channel Bits.

Sample Code:

{ return GEOM_CHANNEL|TOPO_CHANNEL; }

Prototype:

virtual ChannelMask ChannelsChanged()=0;

Remarks:

Implemented by the Plug-In.

These are the channels that the modifier actually modifies. Note that ChannelsChanged() is called many times but the channels returned should not change on the fly.

Return Value:

The channels that are changed. See List of Channel Bits.

Prototype:

ChannelMask TotalChannelsUsed();

Remarks:

Returns the same value as ChannelsUsed() above except GFX_DATA_CHANNEL will be ORed in if the TOPO_CHANNEL or the TEXMAP_CHANNEL are being used.

Prototype:

ChannelMask TotalChannelsChanged();

Remarks:

Returns the same value as ChannelsChanged() above except GFX_DATA_CHANNEL will be ORed in if the TOPO_CHANNEL, the TEXMAP_CHANNEL , or the VERTCOLOR_CHANNEL are being changed.

Prototype:

virtual bool ChangesSelType();

Remarks:

This method is available in release 4.0 and later only.

If a modifier want to make it possible to sitch dynamically between changing the selection type that flows up the stack, or leaving it like it is, it can overwrite this. The default implementation indicates that it changes the selection type, if the SUBSEL_TYPE_CHANNEL is part of ChannelsChanged(). Note that ChannelsChanged() can not dynamically changed for various reasons.

Default Implementation:

{ return ChannelsChanged()&SUBSEL_TYPE_CHANNEL ? true : false; }

Object Modification

Prototype:

virtual void ModifyObject(TimeValue t, ModContext &mc, ObjectState* os, INode *node)=0;

Remarks:

Implemented by the Plug-In.

This is the method that actually modifies the input object. This method is responsible for altering the object and then updating the validity interval of the object to reflect the validity of the modifier.

Parameters:

TimeValue t

The time at which the modification is being done.

ModContext &mc

A reference to the ModContext.

ObjectState* os

The object state flowing through the pipeline. This contains a pointer to the object to modify.

INode *node

The node the modifier is applied to. This parameter is always NULL for Object Space Modifiers and non-NULL for World Space Modifiers (Space Warps). This is because a given WSM is only applied to a single node at a time whereas an OSM may be applied to several nodes. This may be used for example by particle system space warps to get the transformation matrix of the node at various times.

See Also: Advanced Topics section on Object Modification.

Validity Intervals

Prototype:

virtual Interval LocalValidity(TimeValue t);

Remarks:

Implemented by the Plug-In.

This method returns the validity interval of a modifier. It is simply the combination of the validity of all the modifier's parameters. It's used to determine when to cache in the pipeline, but is not directly responsible for determining when ModifyObject() is called. ModifyObject() is called when the pipeline needs to be evaluated either because someone sent a REFMSG_CHANGE message or the validity of the object does not include the current time.

If a modifier is not animated it's OK to simply return FOREVER from this method. In the case where the modifier changes because a user changes a non-animated control in the user interface (for instance a check box), you can cause reevaluation by notifying your dependents of the change, i.e.:

NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);

Parameters:

TimeValue t

The time to calculate the Interval.

See Also: Advanced Topics on Intervals.

Topology Dependence

Prototype:

virtual BOOL DependOnTopology(ModContext &mc)

Remarks:

Implemented by the Plug-In.

Modifiers that place a dependency on topology should return TRUE for this method. An example would be a modifier that stores a selection set base on vertex indices. This modifier depends on the indices being intact for it to operate correctly.

Parameters:

ModContext &mc

Reference to the ModContext.

Default Implementation:

{ returns FALSE; }

Return Value:

TRUE if the modifier depends on topology; otherwise FALSE.

Modifier Stack Access

Prototype:

void DisableMod()

Remarks:

Implemented by the System.

This disables the modifier in the history browser (modifier stack).

Prototype:

void EnableMod()

Remarks:

Implemented by the System.

This enables the modifier in the history browser (modifier stack).

Prototype:

int IsEnabled()

Remarks:

Implemented by the System.

This returns the status (enabled or disabled) of the modifier in the history browser.

Return Value:

Nonzero if enabled; otherwise 0.

 

Prototype:

void DisableModInRender()

Remarks:

Implemented by the System.

This turns off the modifier in the renderer

 

Prototype:

void EnableModInRender()

Remarks:

Implemented by the System.

This turns on the modifier in the renderer

 

Prototype:

int IsEnabledInRender()

Remarks:

Implemented by the System.

This returns the status (enabled or disabled) of the modifier in the renderer.

Return Value:

Nonzero if enabled; otherwise 0.

 

Prototype:

void DisableModInViews();

Remarks:

Implemented by the System.

Disables the modifier in the viewports (it remains active in the renderer unless DisableMod() above is used).

Prototype:

void EnableModInViews();

Remarks:

Implemented by the System.

Enables the modifier in the viewports.

Prototype:

int IsEnabledInViews();

Remarks:

Implemented by the System.

Returns nonzero if the modifier is enabled in the viewports; otherwise zero.

Prototype:

void DisableModApps()

Remarks:

This method is used internally.

Prototype:

void EnableModApps()

Remarks:

This method is used internally.

Notification of Input Changed

Prototype:

virtual void NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc)

Remarks:

Implemented by the Plug-In.

This method is called when an item in the modifier stack before this modifier sends a REFMSG_CHANGE message via NotifyDependents().

Consider the following example: Assume the modifier stack contains a Sphere Object, then a Bend, then a Edit Mesh. The Edit Mesh modifier does not have a reference to the Bend or the Sphere because it does not officially depend on these items. However it does depend on them in a certain sense, because it modifies the data that these items produce. So, if they change it may affect the modifier. A modifier may build a cache based on its input object. The modifier needs a way to know when to discard this cache because the input object has changed. Whenever one of the items before this modifier in the stack sends out a REFMSG_CHANGE message via NotifyDependents() to indicate it has changed this method is called. The modifier may respond in a way appropriate to it, for example by discarding its cache of the input object.

It is not legal, to issue a NotifyDependent()'s in the NotifyInputChanged() method of a modifier, without checking for reentrancy. Imagine, you have an instanced modifier applied to the same object in the stack. Sending a refmsg from the NotifyInputChanged method will casue an endles loop. Simply putting a guard in, that checks for reentrancy should get rid of the problem.

Parameters:

Interval changeInt

This is the interval from the message. It is reserved for future use - now it will always be FOREVER.

PartID partID

This is the partID from the message.

RefMessage message

This is the message sent.

ModContext *mc

The ModContext for the pipeline that changed. If a modifier is applied to multiple objects, then there are ModApps in each pipeline that it is applied to. These ModApps are pointing to the same modifier. Consider the following example: Say you apply a Bend modifier to a Sphere, a Cylinder and a Box object. There are three ModApps but only one Bend modifier. Then you go to the Sphere and adjust its Radius. This will cause NotifyInputChanged() to be called on the Bend because the Bend's input changed. However only one of its inputs changed - only the Sphere changed and not the Cylinder or the Box. Therefore NotifyInputChanged() will be called once, and the ModContext passed in will be for the Sphere's changed pipeline. It is possible that all three objects could change at the same time. If an instanced float controller was assigned to the radius, width, and height - one parameter for each object - then the controller was adjusted in the function curve editor, all three items would change. In this case NotifyInputChanged() would be called three times on the Bend. Once for each pipeline, once with each ModContext.

Loading and Saving Methods

Prototype:

IOResult Save(ISave *isave);

Remarks:

Implemented by the System.

This method handles saving the modifier name. This method should be called by the derived class BEFORE it saves any chunks. See the sample code below.

Parameters:

ISave *isave

You may use this pointer to call methods of ISave to write data.

Return Value:

One of the following values: IO_OK, IO_ERROR.

Sample Code:

IOResult DispMod::Save(ISave *isave)

 {

 // First save the modifier name by

 // calling the base class version.

 Modifier::Save(isave);

 // Then save this modifiers data.

 isave->BeginChunk(BMIO_CHUNK);

 bi.Save(isave);

 isave->EndChunk();

 return IO_OK;

 }

Prototype:

IOResult Load(ILoad *iload);

This method handles loading the modifier name. It should be called by the derived class BEFORE it loads any chunks.

Remarks:

Implemented by the System.

Parameters:

ILoad *iload

You may use this pointer to call methods of ILoad to read data.

Return Value:

One of the following values: IO_OK, IO_ERROR.

Prototype:

virtual IOResult LoadLocalData(ILoad *iload, LocalModData **pld)

Remarks:

Implemented by the Plug-In.

When a 3ds max file is being loaded, this method is called so that the modifier can load the LocalModData structure that is hung off each ModContext. If the modifier doesn't store any data in the ModContext it can ignore this method.

Parameters:

ILoad *iload

You may use this pointer to call methods of ILoad to read data.

LocalModData **pld

A pointer to a pointer in the ModContext. The modifier must set this pointer to point at a new LocalModData derived class.

Return Value:

One of the following values: IO_OK, IO_ERROR.

Prototype:

virtual IOResult SaveLocalData(ISave *isave, LocalModData *ld)

Remarks:

Implemented by the Plug-In.

When a 3ds max file is being saved, this method is called so that the modifier can save the localData structure that is hung off each ModContext. If the modifier doesn't store any data in the ModContext it can ignore this method.

Parameters:

ISave *isave

You may use this pointer to call methods of ISave to write data.

LocalModData *ld

Pointer to the LocalModData for the modifier.

Return Value:

One of the following values: IO_OK, IO_ERROR.

ModContext Enumeration

Prototype:

void EnumModContexts(ModContextEnumProc *proc);

Remarks:

Implemented by the System.

This method will call the callback object proc method once for each application of the modifier.

Parameters:

ModContextEnumProc *proc

The callback object whose proc method is called.

See Also: Class ModContextEnumProc.

Prototype:

void GetIDerivedObject(ModContext *mc, IDerivedObject *&derObj, int &modIndex);

Remarks:

This method is available in release 4.0 and later only.

This method will retrieve the IDerivedObject and index of this modifier for a given modifier context.

Parameters:

ModContext *mc

Points to the ModContext for the modifier.

IDerivedObject *&derObj

A pointer to the IDerivedObject is returned here.

int &modIndex

The zero based index of the modifier in the derived object is returned here.

Prototype:

virtual void CopyAdditionalChannels(Object *fromObj, Object *toObj);

Remarks:

This method is available in release 4.0 and later only.

In case the modifier changes the object type (basically the os->obj pointer in ModifyObject) *and* changes the ExtensionChannel, it has to overwrite this method and copy only the channels that it doesn't modify/added already to the new object.

Parameters:

Object *fromObj

 

Object *toObj

 

Default Implementation:

{ toObj->CopyAdditionalChannels(fromObj);}

Prototype:

virtual int NeedUseSubselButton();

Remarks:

This method is no longer used.