action (lx_action.hpp)

From The Foundry MODO SDK wiki
(Redirected from ILxChannelReadID (index))
Jump to: navigation, search
There are security restrictions on this page


Contents

Animation Performance

Items describe the animation objects, their relationships to each other, and what parameter channels are available for animation, but they do not store any actual motions. Channel values are stored separately from the item hierarchy in order to allow the same setup to have different performances, and perhaps even to allow performance blending. Whole blocks of related keyframed and constant parameter channels are stored together in units.

Final animation is computed by an animation pipeline. The items are all fed into the pipe with some initial keyframed animation, and then a series of modifier transforms are applied which alter the state of the items in a coordinated fashion. These modifiers represent the rules for the interaction between items, such as parenting, IK and dynamics. The final state of the items can then be fed to the renderer for output.

Channel Read Interface

The channel-read interface can be presented by anything that contains values for item channels. This includes actions and scene evaluation, both channel-read objects allocated through the scene interface. The envelope method returns an envelope only if one exists for the channel.

(1) SDK: ILxChannelRead interface
         LXxMETHOD( LxResult,
 ValueObj) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         void                   **ppvObj);
 
         LXxMETHOD( LxResult,
 Integer) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         int                     *value);
 
         LXxMETHOD( LxResult,
 Double) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         double                  *value);
 
         LXxMETHOD( LxResult,
 String) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         const char             **value);
 
         LXxMETHOD( LxResult,
 Envelope) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         void                   **ppvObj);
 
         LXxMETHOD( double,
 Time) (
         LXtObjectID              self);

Test to see if a channel is animated.

If this is an evaluated channel-read (specified by any time), IsAnimated() tests whether the evaluation for the channel changes at any time during the animation.

If this an action channel-read, IsAnimated() tests whether a channel contains animation, by looking at the whole action hierarchy and instances, and also takes animated gradients into account.

(2) SDK: ILxChannelRead interface
         LXxMETHOD( int,
 IsAnimated) (
         LXtObjectID              self,
         LXtObjectID              item,
         int                      index);

Some channels may be baked -- that is they have samples cached over a range of time. These methods test if a channel is baked and return the samples as a ValueArray object.

(3) SDK: ILxChannelRead interface
         LXxMETHOD( LxResult,
 IsBaked) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel);
 
         LXxMETHOD( LxResult,
 BakedSamples) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         double                  *firstSample,
         double                  *spsRate,
         void                   **ppvObj);

The time at which values are read can be changed.

(4) SDK: ILxChannelRead interface
         LXxMETHOD( LxResult,
 SetTime) (
         LXtObjectID              self,
         double                   time);

(5) SDK: Declarations
 #define LXu_CHANNELREAD         "D5A8C4FD-151C-4D8B-97E1-6E1B4087886B"

(6) User Class: ChannelRead method
         int
 IValue (
         ILxUnknownID             item,
         unsigned                 channel)
 {
         int                      value = 0;
 
         Integer (item, channel, &value);
         return value;
 }
 
         bool
 Encoded (
         CLxLoc_Item             &item,
         unsigned                 channel,
         std::string             &result)
 {
         CLxUser_ValueService     valueSvc;
         const LXtTextValueHint  *hints;
         int                      value;
 
         return (    LXx_OK (Integer (item, channel, &value))
                 &&  LXx_OK (item.ChannelIntHint (channel, &hints))
                 &&  valueSvc.EncodeHint (value, hints, result) );
 }
 
         double
 FValue (
         ILxUnknownID             item,
         unsigned                 channel)
 {
         double                   value = 0.0;
 
         Double (item, channel, &value);
         return value;
 }
 
         bool
 GetString (
         ILxUnknownID             item,
         unsigned                 channel,
         std::string             &result)
 {
         const char              *str;
 
         if (LXx_FAIL (String (item, channel, &str)))
                 return false;
 
         result = str;
         return true;
 }
 
         bool
 Object (
         ILxUnknownID             item,
         unsigned                 channel,
         CLxLocalizedObject      &loc)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (ValueObj (item, channel, &obj)))
                 return false;
 
         return loc.take (obj);
 }
 
         bool
 GetEnv (
         ILxUnknownID             item,
         unsigned                 channel,
         CLxLoc_Envelope         &env)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (Envelope (item, channel, &obj)))
                 return false;
 
         return env.take (obj);
 }
 
         bool
 GetBake (
         ILxUnknownID             item,
         unsigned                 channel,
         double                  *firstSample,
         double                  *spsRate,
         CLxLoc_ValueArray       &array)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (BakedSamples (item, channel, firstSample, spsRate, &obj)))
                 return false;
 
         return array.take (obj);
 }

Same again, this time taking the channel name instead of the index.

(7) User Class: ChannelRead method
         int
 IValue (
         CLxLoc_Item             &item,
         const char              *channel)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return 0;
 
         return IValue (item, index);
 }
 
         bool
 Encoded (
         CLxLoc_Item             &item,
         const char              *channel,
         std::string             &result)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return Encoded (item, index, result);
 }
 
         double
 FValue (
         CLxLoc_Item             &item,
         const char              *channel)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return 0.0;
 
         return FValue (item, index);
 }
 
         bool
 GetString (
         CLxLoc_Item             &item,
         const char              *channel,
         std::string             &result)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return GetString (item, index, result);
 }
 
         bool
 Object (
         const CLxUser_Item      &item,
         const char              *channel,
         CLxLocalizedObject      &loc)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return Object (reinterpret_cast<ILxUnknownID>(item.m_loc), index, loc);
 }
 
         bool
 GetEnv (
         CLxLoc_Item             &item,
         const char              *channel,
         CLxLoc_Envelope         &env)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return GetEnv (item, index, env);
 }

If the channel is of the OBJREF type then it's actually a container for some other object. These methods allow direct access to the contents.

(8) User Class: ChannelRead method
         LxResult
 GetRef (
         ILxUnknownID             item,
         unsigned                 channel,
         void                   **ppvObj)
 {
         CLxLoc_ValueReference    ref;
         LxResult                 rc;
         LXtObjectID              obj;
 
         rc = ValueObj (item, channel, &obj);
         if (LXx_FAIL (rc))
                 return rc;
 
         if (!ref.take (obj))
                 return LXe_NOTFOUND;
 
         return ref.GetObject (ppvObj);
 }
 
         LxResult
 GetRef (
         CLxLoc_Item             &item,
         const char              *channel,
         void                   **ppvObj)
 {
         unsigned int             index;
         LxResult                 rc;
 
         rc = item.ChannelLookup (channel, &index);
         if (LXx_FAIL (rc))
                 return rc;
 
         return GetRef (item, index, ppvObj);
 }
 
         template <class T>
         bool
 GetRef (
         CLxLoc_Item             &item,
         T                        channel,
         CLxLocalizedObject      &loc)
 {
         CLxLoc_ValueReference    ref;
 
         if (!Object (item, channel, ref))
                 return false;
 
         return ref.GetObject (loc);
 }

The Python method is very much easier, just returning the right value type for any channel, given by index or name.

(9) PY: ChannelRead method
 def Value(self, item, index):
     """value = Value(item, channel)
     Channel can be given by index or name.
     """
     if isinstance(index, str):
         index = item.ChannelLookup(index)
 
     type = self.Type(item, index)
 
     if type == lx.symbol.i_TYPE_FLOAT:
         return self.Double(item, index)
 
     if type == lx.symbol.i_TYPE_INTEGER:
         try:
             return self.EncodedInt(item, index)
         except:
             return self.Integer(item, index)
 
     if type == lx.symbol.i_TYPE_STRING:
         return self.String(item, index)
 
     return self.ValueObj(item, index)

Some additional ways to init the wrapper. Commonly clients have a scene or an item and they want to read channel values.

(10) User Class: ChannelRead method
         bool
 from (
         CLxLoc_Scene            &scene)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (LXs_ACTIONLAYER_EDIT, 0.0, &obj))
            &&  take (obj);
 }
 
         bool
 from (
         CLxLoc_Scene            &scene,
         double                   time)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (0, time, &obj))
            &&  take (obj);
 }
 
         bool
 from (
         CLxLoc_Item             &item)
 {
         LXtObjectID              obj;
         CLxLoc_Scene             scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  from (scene);
 }
 
         bool
 from (
         CLxLoc_Item             &item,
         double                   time)
 {
         LXtObjectID              obj;
         CLxLoc_Scene             scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  from (scene, time);
 }

Some common methods for getting channel types.

(11) Channel type methods
         LXxMETHOD( LxResult,
 Type) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         unsigned                *type);
 
         LXxMETHOD( LxResult,
 TypeName) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         const char             **typeName);

801 methods for type, and getting encoded int values directly.

(12) SDK: ILxChannelRead interface
 ''[[#C11|Channel type methods]]''
 
         LXxMETHOD( LxResult,
 EncodedInt) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         char                    *buf,
         unsigned                 len);

Channel Write Interface

In some cases we can write values to channels. Values written to actions are written as constants when using the Double and Integer methods, note that any animation on the channel will be overwritten.

The DoubleKey and IntegerKey methods will create keys on channels that are already animated, otherwise the constant value for the channel will be set. Passing a value of one to the 'create' argument can be used to always create a key.

The envelope method creates an envelope for the channel if it does not exist. If an envelope is created it will have a key created at the time chosen when the interface was obtained. The key will be created with the value of the constant channel.

(13) SDK: ILxChannelWrite interface
         LXxMETHOD( LxResult,
 ValueObj) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         void                   **ppvObj);
 
         LXxMETHOD( LxResult,
 Integer) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         int                      value);
 
         LXxMETHOD( LxResult,
 Double) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         double                   value);
 
         LXxMETHOD( LxResult,
 String) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         const char              *value);
 
         LXxMETHOD( LxResult,
 Envelope) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         void                   **ppvObj);
 
         LXxMETHOD( LxResult,
 IntegerKey) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         int                      value,
         int                      create);
 
         LXxMETHOD( LxResult,
 DoubleKey) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         double                   value,
         int                      create);
 
         LXxMETHOD( LxResult,
 SetState) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         unsigned int             state);

To cache baked values on a channel this method is called to set the start time and samples per second. The returned ValueArray should be filled with the samples.

(14) SDK: ILxChannelWrite interface
         LXxMETHOD( LxResult,
 BakeSamples) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         double                   firstSample,
         double                   spsRate,
         void                   **ppvObj);

(15) SDK: Declarations
 #define LXu_CHANNELWRITE        "91BFE3B8-16C6-4195-BFE5-3F0E3C0C5C57"

Nicer methods allow us to use localized objects and use argument types to handle the different intrinsic type cases.

(16) User Class: ChannelWrite method
         bool
 Set (
         ILxUnknownID             item,
         unsigned                 channel,
         int                      value)
 {
         return LXx_OK (Integer (item, channel, value));
 }
 
         bool
 SetEncoded (
         ILxUnknownID             item,
         unsigned                 channel,
         const char              *encoded)
 {
         CLxLoc_Item              user (item);
         CLxLoc_ValueService      valueSvc;
         const LXtTextValueHint  *hints;
         int                      value;
 
         if (  LXx_FAIL (user.ChannelIntHint (channel, &hints))
           ||  LXx_FAIL (valueSvc.TextHintDecode (encoded, hints, &value)) )
                 return false;
 
         return LXx_OK (Integer (item, channel, value));
 }
 
         bool
 Set (
         ILxUnknownID             item,
         unsigned                 channel,
         double                   value)
 {
         return LXx_OK (Double (item, channel, value));
 }
 
         bool
 Set (
         ILxUnknownID             item,
         unsigned                 channel,
         const char              *value)
 {
         return LXx_OK (String (item, channel, value));
 }
 
         bool
 Object (
         ILxUnknownID             item,
         unsigned                 channel,
         CLxLocalizedObject      &loc)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (ValueObj (item, channel, &obj)))
                 return false;
 
         return loc.take (obj);
 }
 
         bool
 AddEnv (
         ILxUnknownID             item,
         unsigned                 channel,
         CLxLoc_Envelope         &env)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (Envelope (item, channel, &obj)))
                 return false;
 
         return env.take (obj);
 }
 
         bool
 WriteBake (
         ILxUnknownID             item,
         unsigned                 channel,
         double                   firstSample,
         double                   spsRate,
         CLxLoc_ValueArray       &array)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (BakeSamples (item, channel, firstSample, spsRate, &obj)))
                 return false;
 
         return array.take (obj);
 }

And the same again, this time taking a string channel name and getting the channel index from the item.

(17) User Class: ChannelWrite method
         bool
 Set (
         CLxLoc_Item             &item,
         const char              *channel,
         int                      value)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return Set (item, index, value);
 }
 
         bool
 SetEncoded (
         CLxLoc_Item             &item,
         const char              *channel,
         const char              *encoded)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return SetEncoded (item, index, encoded);
 }
 
         bool
 Set (
         CLxLoc_Item             &item,
         const char              *channel,
         double                   value)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return Set (item, index, value);
 }
 
         bool
 Set (
         CLxLoc_Item             &item,
         const char              *channel,
         const char              *value)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return Set (item, index, value);
 }
 
         bool
 Object (
         CLxLoc_Item             &item,
         const char              *channel,
         CLxLocalizedObject      &loc)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return Object (item, index, loc);
 }
 
         bool
 AddEnv (
         CLxLoc_Item             &item,
         const char              *channel,
         CLxLoc_Envelope         &env)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return AddEnv (item, index, env);
 }

Directly set the contents of an OBJREF channel.

(18) User Class: ChannelWrite method
         bool
 SetRef (
         ILxUnknownID             item,
         unsigned                 channel,
         ILxUnknownID             obj)
 {
         CLxLoc_ValueReference    ref;
 
         if (!Object (item, channel, ref))
                 return false;
 
         return LXx_OK (ref.SetObject (obj));
 }
 
         bool
 SetRef (
         CLxLoc_Item             &item,
         const char              *channel,
         ILxUnknownID             obj)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return false;
 
         return SetRef (item, index, obj);
 }

Python user method does all conversions this direction as well.

(19) PY: ChannelWrite method
 def Set(self, item, index, value, key=False):
     """Set(item, channel, value, <key>)
     Channel can be given by index or name. Set key to True to make a keyframe.
     """
     if isinstance(index, str):
         index = item.ChannelLookup(index)
 
     type = self.Type(item, index)
 
     if type == lx.symbol.i_TYPE_FLOAT:
         if key:
             self.DoubleKey(item, index, float(value), True)
         else:
             self.Double(item, index, float(value))
 
     elif type == lx.symbol.i_TYPE_INTEGER:
         if isinstance(value, str):
             try:
                 if key:
                     self.EncodedIntKey(item, index, value, True)
                 else:
                     self.EncodedInt(item, index, value)
 
             # If the channel has no integer hint, an exception is raised and
             # we revert to using a simple string to integer conversion
             except RuntimeError:
                 if key:
                     self.IntegerKey(item, index, int(value), True)
                 else:
                     self.Integer(item, index, int(value))       
         else:
             if key:
                 self.IntegerKey(item, index, int(value), True)
             else:
                 self.Integer(item, index, int(value))
 
     elif type == lx.symbol.i_TYPE_STRING:
         if key:
            raise RuntimeError("Can't keyfame string channels.")
 
         self.String(item, index, str(value))
 
     else:
         raise RuntimeError("Can't set object channels.")

User class methods to initialize a write context from scene or item.

(20) User Class: ChannelWrite method
         bool
 from (
         CLxLoc_Scene            &scene,
         double                   time = 0.0)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (LXs_ACTIONLAYER_EDIT, time, &obj))
            &&  take (obj);
 }
 
         bool
 from (
         CLxLoc_Item             &item,
         double                   time = 0.0)
 {
         LXtObjectID              obj;
         CLxLoc_Scene             scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  from (scene, time);
 }
 
         bool
 setupFrom (
         CLxLoc_Scene            &scene)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (LXs_ACTIONLAYER_SETUP, 0.0, &obj))
            &&  take (obj);
 }
 
         bool
 setupFrom (
         CLxLoc_Item             &item)
 {
         LXtObjectID              obj;
         CLxLoc_Scene             scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  setupFrom (scene);
 }

(21) SDK: Declarations
 #define LXs_ACTIONLAYER_SETUP   "setup"
 #define LXs_ACTIONLAYER_ANIM    "scene"
 #define LXs_ACTIONLAYER_EDIT    "edit"

801 methods for type and encoded ints. For writing we have a choice of setting the int as a constant or a keyframe.

(22) SDK: ILxChannelWrite interface
 ''[[#C11|Channel type methods]]''
 
         LXxMETHOD( LxResult,
 EncodedInt) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         const char              *value);
 
         LXxMETHOD( LxResult,
 EncodedIntKey) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned int             channel,
         const char              *value);

Modifiers

Animation modifiers are the processing steps in the animation pipeline. The start of the pipe is the Action which initializes channel values from the action at the evaluation time. Each modifier alters channel values based on the value of those and other channels. When all modifiers have been evaluated, the final channel values are returned from the evaluation context.

For example, the animation pipeline contains small modifiers, the most common of which is the channel link modifier that just copies one channel value to another. A medium-scale modifier might be one like IK, which reads the world transform of a parent item and the local transform of an item, and writes the world transform of the second. Effects like dynamics require large-scale modifiers that reads and many channels at once.

Modifiers are independent of items, although they can be associated with them. Modifiers can read any set of channels in the scene and write any other channels. The evaluation engine automatically orders them so that output channels are computed before they are needed as input, although cycles can be introduced.

Modifier Interface

Modifiers have two forms. The modifier class is a plug-in server of type ILxEvalModifier. This provides methods for creating modifier instances which operate on specific channels. The instance is an object that caches specific references to input and output channels and can perform computations over them. For example there is only one "IK" ILxEvalModifier server, but it will create instances to evaluate IK for all locators with parents.

Each instance is identified by a key channel. The class interface provides methods for enumerating through key channels and creating and testing instances.

Reset
Set the modifier class to the given scene, and reset the count of key channels for the Next() method.
Next
Returns the item and index for the next key channel for this modifier in the current scene. If the last one has already been read, this method returns null.
Alloc
Create a new instance for the given key channel. The eval object allows the instance to create references to the channels it wants to be able to read and write, and to cache the attributes interface needed to read their values during evaluation.

(23) SDK: ILxEvalModifier interface
         LXxMETHOD( LxResult,
 Reset) (
         LXtObjectID              self,
         LXtObjectID              scene);
 
         LXxMETHOD( LXtObjectID,
 Next) (
         LXtObjectID              self,
         unsigned                *index);
 
         LXxMETHOD( LxResult,
 Alloc) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned                 index,
         LXtObjectID              eval,
         void                   **ppvObj);

Empty EvalModifier Python user class.

(24) PY: EvalModifier method
 pass

The modifier class can describe itself with server tags.

TYPELIST
A whitespace-delimited list of item types. The modifier will only be made active if one of these types is present in the cinema. Also if items of this type are added or removed the modifier will be re-validated.
GRAPHLIST
A whitespace-delimited list of graph names. The modifier will be re-validated any time links in these graphs change.

(25) SDK: Declarations
 #define LXu_EVALMODIFIER        "30AAAF24-9699-4737-8E3B-E264AA4B7A3E"
 #define LXa_EVALMODIFIER        "evalModifier" #define LXsMOD_TYPELIST         "modifier.typeList"
 #define LXsMOD_GRAPHLIST        "modifier.graphList"
 #define LXsMOD_GUIDLIST         "modifier.guidList"
 #define LXsMOD_REQUIREDTYPE     "modifier.required"

The modifier instance, allocated for a specific key channel in the scene, is given by an ILxModifier interface.

Evaluate
Evaluate a modifier instance given an attribute interfaces for reading and writing channel values. The result code for the evaluation can be read using the ILxEvaluation interface, and can contain information about changes since the last invalidation. General codes allow for identical result, and the default is totally different.

(26) SDK: Declarations

Test
Test an instance to see if its still valid for the given key channel. If it returns LXe_FALSE or an error the instance will be removed.
Invalidate
Check to see if a change to the given input channel should invalidate the current state of the modifier. LXe_OK should be returned in most cases, but LXe_FALSE can be returned if the given channel has no effect on the modifier.
RequiredCount, Required
Enumerates the required channels for this modifier, getting the key item for an input channel.
Free
Free cache data for this modifier.

(27) SDK: ILxModifier interface
         LXxMETHOD( LxResult,
 Evaluate) (
         LXtObjectID              self);
 
         LXxMETHOD( LxResult,
 Test) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned                 index);
 
         LXxMETHOD( LxResult,
 Invalidate) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned                 index);
 
         LXxMETHOD( LxResult,
 Validate) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned                 index,
         LxResult                 rc);
 
         LXxMETHOD( unsigned,
 RequiredCount) (
         LXtObjectID              self);
 
         LXxMETHOD( LxResult,
 Required) (
         LXtObjectID              self,
         unsigned                 index,
         unsigned                *attr,
         void                   **ppvObj);
 
         LXxMETHOD( void,
 Free) (
         LXtObjectID              self,
         void                    *cache);

(28) SDK: Declarations
 #define LXu_MODIFIER            "8D3BEC86-E10B-426A-8BA3-846250E25AF4"

There is a special case where some modifiers can have read-write channels, where the modifier takes their inputs and modifies them for output. For these channels to be recognized as intenally modifier, a special tag can be placed on these packages. For now, the mere presence of the tag is enough to flag these channels as potentially modified.

(29) SDK: Declarations
 #define LXs_MODIFIER_OPTIONS            "modifierOptions"

Evaluation Interface

The ILxEvaluation interface is passed to the ILxEvalModifier::Alloc() method as the way for instances to declare their inputs and outputs. It is also passed to any other subsystems that need to be able to register their own inputs, although they are generally not allowed to specify outputs.

Clients should not read channel values at this point. This setup assumes that the inputs can have any possible value, so all contingencies should be considered. During actual evaluation the client will be passed an ILxAttributes interface from which to read their inputs.

AddChannel
This adds a channel by item and index for input, output or both. The returned index in 'attr' is the index in the attributes interface provided during evaluation.
AddChannelName
Same as above but takes the channel name instead of the index.
ReadTime
Adds the time of the evaluation as an input attribute for those modifier nodes that need it.

(30) SDK: ILxEvaluation interface
         LXxMETHOD( LxResult,
 AddChannel) (
         LXtObjectID              self,
         LXtObjectID              item,
         unsigned                 index,
         unsigned                 type,
         unsigned                *attr);
 
         LXxMETHOD( LxResult,
 AddChannelName) (
         LXtObjectID              self,
         LXtObjectID              item,
         const char              *name,
         unsigned                 type,
         unsigned                *attr);
 
         LXxMETHOD( LxResult,
 ReadTime) (
         LXtObjectID              self,
         unsigned                *attr);

The channel type can be READ, WRITE, or both. The modifier should also set the SETUP flag if a channel will be read in setup mode. The FORCE flag is used in conjunction with the READ flag, and specifies an input channel that may not always be read by the modifier. If the FORCE flag is set, the modifier will always be invalidated when the channel changes, even if the modifier doesn't explicitly read the the input channel.

(31) SDK: Declarations
 #define LXfECHAN_READ            0x01
 #define LXfECHAN_WRITE           0x02
 #define LXfECHAN_SETUP           0x04
 #define LXfECHAN_FORCE           0x08
 
 #define LXfSIM_NOCACHE           0x01
 #define LXfSIM_CONTINUOUS        0x02
 
 #define LXu_EVALUATION          "FB552E5F-746E-4d74-885C-40A931B82B84"

(32) User Class: Evaluation method
         unsigned
 AddChan (
         ILxUnknownID             item,
         unsigned                 index,
         unsigned                 type = LXfECHAN_READ)
 {
         LxResult                 rc;
         unsigned                 idx;
 
         rc = AddChannel (item, index, type, &idx);
         if (LXx_FAIL (rc))
                 throw (rc);
 
         return idx;
 }
 
         unsigned
 AddChan (
         ILxUnknownID             item,
         const char              *name,
         unsigned                 type = LXfECHAN_READ)
 {
         LxResult                 rc;
         unsigned                 idx;
 
         rc = AddChannelName (item, name, type, &idx);
         if (LXx_FAIL (rc))
                 throw (rc);
 
         return idx;
 }
 
         unsigned
 AddTime ()
 {
         LxResult                 rc;
         unsigned                 idx;
 
         rc = ReadTime (&idx);
         if (LXx_FAIL (rc))
                 throw (rc);
 
         return idx;
 }

Modifiers can also perform alternate evaluation. The "set" methods are called during modifier evaluation to set all inputs to the modifier to come from an alternate evaluation -- essentially an alternate reality. The modifier can set an alternate time, or they can set alternate channel values. The latter is done by calling SetAlternate() and writing alternate values using the ILxChannelWrite interface that is returned. Alternate evaluation can also be cleared so that inputs come from the reality being altered.

(33) SDK: ILxEvaluation interface
         LXxMETHOD( LxResult,
 SetAlternateTime) (
         LXtObjectID              self,
         double                   time);
 
         LXxMETHOD( LxResult,
 SetAlternate) (
         LXtObjectID              self,
         void                   **ppvObj);
 
         LXxMETHOD( LxResult,
 ClearAlternate) (
         LXtObjectID              self);

User method returns a channel write object.

(34) User Class: Evaluation method
         bool
 Alternate (
         CLxLoc_ChannelWrite     &cw)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (SetAlternate (&obj)))
                 return false;
 
         return cw.take (obj);
 }

Modifiers can set and get cache data objects to remember their last evaluation state. These are freed by ILxModifier::Free().

(35) SDK: ILxEvaluation interface
         LXxMETHOD( LxResult,
 SetCache) (
         LXtObjectID              self,
         void                    *cache);
 
         LXxMETHOD( void *,
 GetCache) (
         LXtObjectID              self);

One more useful alternate is the setup action.

(36) SDK: ILxEvaluation interface
         LXxMETHOD( LxResult,
 SetAlternateSetup) (
         LXtObjectID              self);

For baked channels, this method allows the client to read the baked sample before and after the current evaluation time. For 'bracket' of zero the sample is returned at or just after the current time. The returned 'fraction' is a value from 0 to 1 indicating how close this sample is to the next one. If the fraction is non-zero then calling this again with 'bracket' of 1 returns the sample just after the current time. This allows values from baked samples to be exactly interpolated.

(37) SDK: ILxEvaluation interface
         LXxMETHOD( LxResult,
 GetBakedSample) (
         LXtObjectID              self,
         unsigned                 index,
         unsigned                 bracket,
         double                  *fraction,
         void                   **ppvObj);

The user method returns the fraction directly or -1 for errors.

(38) User Class: Evaluation method
         double
 GetBaked (
         unsigned                 index,
         bool                     after,
         CLxLocalizedObject      &loc)
 {
         LXtObjectID              obj;
         double                   f;
 
         if (LXx_FAIL (GetBakedSample (index, (after ? 1 : 0), &f, &obj)))
                 return -1.0;
 
         return (loc.take (obj) ? f : -1.0);
 }

Get an approximate timestep for evaluating nearby values. During a simulation this is the stepsize of the sim which may be less than a frame. In normal circumstances this is just the frame interval.

(39) SDK: ILxEvaluation interface
         LXxMETHOD( double,
 GetDT) (
         LXtObjectID              self);

Get information about simulation. The LXfSIM_ state flags indicate if the simulation is just a fake preview and if so, if it's advancing over time. The range is only valid for simulations that are being cached.

(40) SDK: ILxEvaluation interface
         LXxMETHOD( LxResult,
 SimulationState) (
         LXtObjectID              self,
         unsigned                *flags);
 
         LXxMETHOD( LxResult,
 SimulationRange) (
         LXtObjectID              self,
         double                  *start,
         double                  *end);

Empty Evaluation Python user class.

(41) PY: Evaluation method
 pass