action (lx_action.hpp)

From The Foundry MODO SDK wiki
(Redirected from LXu CHANNELREAD (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: ChannelRead::ValueObj, etc.
         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: ChannelRead::IsAnimated
         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: ChannelRead::IsBaked, etc.
         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: ChannelRead::SetTime
         LXxMETHOD( LxResult,
 SetTime) (
         LXtObjectID              self,
         double                   time);

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

(6) SDK: CLxUser_ChannelRead::IValue method
         int
 IValue (
         ILxUnknownID             item,
         unsigned                 channel)
 {
         int                      value = 0;
 
         Integer (item, channel, &value);
         return value;
 }
 
         bool
 Encoded (
         CLxUser_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,
         CLxUser_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,
         CLxUser_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) SDK: CLxUser_ChannelRead::IValue method
         int
 IValue (
         CLxUser_Item            &item,
         const char              *channel)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return 0;
 
         return IValue (item, index);
 }
 
         bool
 Encoded (
         CLxUser_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 (
         CLxUser_Item            &item,
         const char              *channel)
 {
         unsigned int             index;
 
         if (LXx_FAIL (item.ChannelLookup (channel, &index)))
                 return 0.0;
 
         return FValue (item, index);
 }
 
         bool
 GetString (
         CLxUser_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 (
         CLxUser_Item            &item,
         const char              *channel,
         CLxUser_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) SDK: CLxUser_ChannelRead::GetRef method
         LxResult
 GetRef (
         ILxUnknownID             item,
         unsigned                 channel,
         void                   **ppvObj)
 {
         CLxUser_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 (
         CLxUser_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 (
         CLxUser_Item            &item,
         T                        channel,
         CLxLocalizedObject      &loc)
 {
         CLxUser_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.Value 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)

The general Get() method takes the value as writable argument.

(10) SDK: CLxUser_ChannelRead::Get method
         bool
 Get (
         ILxUnknownID             item,
         unsigned                 channel,
         int                     *value)
 {
         return LXx_OK (Integer (item, channel, value));
 }
 
         bool
 Get (
         ILxUnknownID             item,
         unsigned                 channel,
         unsigned                *value)
 {
         int                      ival;
         bool                     res;
 
         res = Get (item, channel, &ival);
         if (ival < 0)
                 return false;
 
         if (res)
                 *value = ival;
 
         return res;
 }
 
         bool
 Get (
         ILxUnknownID             item,
         unsigned                 channel,
         double                  *value)
 {
         return LXx_OK (Double (item, channel, value));
 }
 
         bool
 Get (
         ILxUnknownID             item,
         unsigned                 channel,
         float                   *value)
 {
         double                   dval;
         bool                     res;
 
         res = Get (item, channel, &dval);
         if (res)
                 *value = dval;
 
         return res;
 }
 
         bool
 Get (
         ILxUnknownID             item,
         unsigned                 channel,
         const char             **value)
 {
         return LXx_OK (String (item, channel, value));
 }
 
         bool
 Get (
         ILxUnknownID             item,
         unsigned                 channel,
         std::string             &value)
 {
         const char              *sval;
         bool                     res;
 
         res = Get (item, channel, &sval);
         if (res)
                 value = sval;
 
         return res;
 }
 
         bool
 Get (
         CLxUser_Item            &item,
         const char              *channel,
         std::string             &value)
 {
         unsigned int             index;
 
         return LXx_OK (item.ChannelLookup (channel, &index))
                     && Get ((ILxUnknownID) item, index, value);
 }
 
         template <class T>
         bool
 Get (
         CLxUser_Item            &item,
         const char              *channel,
         T                        value)
 {
         unsigned int             index;
 
         return LXx_OK (item.ChannelLookup (channel, &index))
                     && Get ((ILxUnknownID) item, index, value);
 }

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

(11) SDK: CLxUser_ChannelRead::from method
         bool
 from (
         CLxUser_Scene           &scene)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (LXs_ACTIONLAYER_EDIT, 0.0, &obj))
            &&  take (obj);
 }
 
         bool
 from (
         CLxUser_Scene           &scene,
         double                   time)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (0, time, &obj))
            &&  take (obj);
 }
 
         bool
 from (
         CLxUser_Item            &item)
 {
         LXtObjectID              obj;
         CLxUser_Scene            scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  from (scene);
 }
 
         bool
 from (
         CLxUser_Item            &item,
         double                   time)
 {
         LXtObjectID              obj;
         CLxUser_Scene            scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  from (scene, time);
 }

Some common methods for getting channel types.

(12) SDK Common: 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.

(13) SDK: ChannelRead::EncodedInt
 ''[[#C12|SDK Common: 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.

(14) SDK: ChannelWrite::ValueObj, etc.
         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.

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

(16) SDK: LXu_CHANNELWRITE define
 #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.

(17) SDK: CLxUser_ChannelWrite::Set method
         bool
 Set (
         ILxUnknownID             item,
         unsigned                 channel,
         int                      value)
 {
         return LXx_OK (Integer (item, channel, value));
 }
 
         bool
 Set (
         ILxUnknownID             item,
         unsigned                 channel,
         unsigned                 value)
 {
         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
 Set (
         ILxUnknownID             item,
         unsigned                 channel,
         std::string             &value)
 {
         return LXx_OK (String (item, channel, value.c_str()));
 }

Setting an encoded int takes a string as argument and converts that to an int. Requires the item wrapper.

(18) SDK: CLxUser_ChannelWrite::SetEncoded method
         bool
 SetEncoded (
         CLxUser_Item            &item,
         unsigned                 channel,
         const char              *encoded)
 {
         CLxUser_ValueService     valueSvc;
         const LXtTextValueHint  *hints;
         int                      value;
 
         if (  LXx_FAIL (item.ChannelIntHint (channel, &hints))
           ||  LXx_FAIL (valueSvc.TextHintDecode (encoded, hints, &value)) )
                 return false;
 
         return LXx_OK (Integer (item, channel, value));
 }

(19) SDK: CLxUser_ChannelWrite::Object method
         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,
         CLxUser_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,
         CLxUser_ValueArray      &array)
 {
         LXtObjectID              obj;
 
         if (LXx_FAIL (BakeSamples (item, channel, firstSample, spsRate, &obj)))
                 return false;
 
         return array.take (obj);
 }

These classes permit access using a channel string rather than index, although they require an item wrapper.

(20) SDK: CLxUser_ChannelWrite::Set method
         template <class T>
         bool
 Set (
         CLxUser_Item            &item,
         const char              *channel,
         T                        value)
 {
         unsigned int             index;
 
         return LXx_OK (item.ChannelLookup (channel, &index))
                     && Set ((ILxUnknownID) item, index, value);
 }
 
         bool
 SetEncoded (
         CLxUser_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
 Object (
         CLxUser_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 (
         CLxUser_Item            &item,
         const char              *channel,
         CLxUser_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.

(21) SDK: CLxUser_ChannelWrite::SetRef method
         bool
 SetRef (
         ILxUnknownID             item,
         unsigned                 channel,
         ILxUnknownID             obj)
 {
         CLxUser_ValueReference   ref;
 
         if (!Object (item, channel, ref))
                 return false;
 
         return LXx_OK (ref.SetObject (obj));
 }
 
         bool
 SetRef (
         CLxUser_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.

(22) PY: ChannelWrite.Set 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.

(23) SDK: CLxUser_ChannelWrite::from method
         bool
 from (
         CLxUser_Scene           &scene,
         double                   time = 0.0)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (LXs_ACTIONLAYER_EDIT, time, &obj))
            &&  take (obj);
 }
 
         bool
 from (
         CLxUser_Item            &item,
         double                   time = 0.0)
 {
         LXtObjectID              obj;
         CLxUser_Scene            scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  from (scene, time);
 }
 
         bool
 setupFrom (
         CLxUser_Scene           &scene)
 {
         LXtObjectID              obj;
 
         return LXx_OK (scene.Channels (LXs_ACTIONLAYER_SETUP, 0.0, &obj))
            &&  take (obj);
 }
 
         bool
 setupFrom (
         CLxUser_Item            &item)
 {
         LXtObjectID              obj;
         CLxUser_Scene            scene;
 
         return LXx_OK (item.Context (&obj))
            &&  scene.take (obj)
            &&  setupFrom (scene);
 }

(24) SDK: LXs_ACTIONLAYER_SETUP, etc. defines
 #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.

(25) SDK: ChannelWrite::EncodedInt, etc.
 ''[[#C12|SDK Common: 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.
(26) SDK: EvalModifier::Reset, etc.
         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.

(27) PY: empty EvalModifier user class
 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.
(28) SDK: LXu_EVALMODIFIER, etc. defines
 #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.
(29) SDK: LXeEVAL_IDENTICAL, etc. defines

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.
(30) SDK: Modifier::Evaluate, etc.
         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);

(31) SDK: LXu_MODIFIER define
 #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.

(32) SDK: LXs_MODIFIER_OPTIONS define
 #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.
(33) SDK: Evaluation::AddChannel, etc.
         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.

(34) SDK: LXfECHAN_READ, etc. defines
 #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"

(35) SDK: CLxUser_Evaluation::AddChan 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.

(36) SDK: Evaluation::SetAlternateTime, etc.
         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.

(37) SDK: CLxUser_Evaluation::Alternate method
         bool
 Alternate (
         CLxUser_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().

(38) SDK: Evaluation::SetCache, etc.
         LXxMETHOD( LxResult,
 SetCache) (
         LXtObjectID              self,
         void                    *cache);
 
         LXxMETHOD( void *,
 GetCache) (
         LXtObjectID              self);

One more useful alternate is the setup action.

(39) SDK: Evaluation::SetAlternateSetup
         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.

(40) SDK: Evaluation::GetBakedSample
         LXxMETHOD( LxResult,
 GetBakedSample) (
         LXtObjectID              self,
         unsigned                 index,
         unsigned                 bracket,
         double                  *fraction,
         void                   **ppvObj);

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

(41) SDK: CLxUser_Evaluation::GetBaked 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.

(42) SDK: Evaluation::GetDT
         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.

(43) SDK: Evaluation::SimulationState, etc.
         LXxMETHOD( LxResult,
 SimulationState) (
         LXtObjectID              self,
         unsigned                *flags);
 
         LXxMETHOD( LxResult,
 SimulationRange) (
         LXtObjectID              self,
         double                  *start,
         double                  *end);

Empty Evaluation Python user class.

(44) PY: empty Evaluation user class
 pass