Action (lx-action.hpp)
Contents
- 1 Animation Performance
- 1.1 Channel Read Interface
- 1.1.1 (1) SDK: ChannelRead::ValueObj, etc.
- 1.1.2 (2) SDK: ChannelRead::IsAnimated
- 1.1.3 (3) SDK: ChannelRead::IsBaked, etc.
- 1.1.4 (4) SDK: ChannelRead::SetTime
- 1.1.5 (5) SDK: LXu_CHANNELREAD define
- 1.1.6 (6) SDK: CLxUser_ChannelRead::IValue method
- 1.1.7 (7) SDK: CLxUser_ChannelRead::IValue method
- 1.1.8 (8) SDK: CLxUser_ChannelRead::GetRef method
- 1.1.9 (9) PY: ChannelRead.Value method
- 1.1.10 (10) SDK: CLxUser_ChannelRead::Get method
- 1.1.11 (11) SDK: CLxUser_ChannelRead::from method
- 1.1.12 (12) SDK Common: Channel type methods
- 1.1.13 (13) SDK: ChannelRead::EncodedInt
- 1.2 Channel Write Interface
- 1.2.1 (14) SDK: ChannelWrite::ValueObj, etc.
- 1.2.2 (15) SDK: ChannelWrite::BakeSamples
- 1.2.3 (16) SDK: LXu_CHANNELWRITE define
- 1.2.4 (17) SDK: CLxUser_ChannelWrite::Set method
- 1.2.5 (18) SDK: CLxUser_ChannelWrite::SetEncoded method
- 1.2.6 (19) SDK: CLxUser_ChannelWrite::Object method
- 1.2.7 (20) SDK: CLxUser_ChannelWrite::Set method
- 1.2.8 (21) SDK: CLxUser_ChannelWrite::SetRef method
- 1.2.9 (22) PY: ChannelWrite.Set method
- 1.2.10 (23) SDK: CLxUser_ChannelWrite::from method
- 1.2.11 (24) SDK: LXs_ACTIONLAYER_SETUP, etc. defines
- 1.2.12 (25) SDK: ChannelWrite::EncodedInt, etc.
- 1.1 Channel Read Interface
- 2 Modifiers
- 2.1 Modifier Interface
- 2.1.1 (26) SDK: EvalModifier::Reset, etc.
- 2.1.2 (27) PY: empty EvalModifier user class
- 2.1.3 (28) SDK: LXu_EVALMODIFIER, etc. defines
- 2.1.4 (29) SDK: LXeEVAL_IDENTICAL, etc. defines
- 2.1.5 (30) SDK: Modifier::Evaluate, etc.
- 2.1.6 (31) SDK: LXu_MODIFIER define
- 2.1.7 (32) SDK: LXs_MODIFIER_OPTIONS define
- 2.2 Evaluation Interface
- 2.2.1 (33) SDK: Evaluation::AddChannel, etc.
- 2.2.2 (34) SDK: LXfECHAN_READ, etc. defines
- 2.2.3 (35) SDK: CLxUser_Evaluation::AddChan method
- 2.2.4 (36) SDK: Evaluation::SetAlternateTime, etc.
- 2.2.5 (37) SDK: CLxUser_Evaluation::Alternate method
- 2.2.6 (38) SDK: Evaluation::SetCache, etc.
- 2.2.7 (39) SDK: Evaluation::SetAlternateSetup
- 2.2.8 (40) SDK: Evaluation::GetBakedSample
- 2.2.9 (41) SDK: CLxUser_Evaluation::GetBaked method
- 2.2.10 (42) SDK: Evaluation::GetDT
- 2.2.11 (43) SDK: Evaluation::SimulationState, etc.
- 2.2.12 (44) PY: empty Evaluation user class
- 2.1 Modifier Interface
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
#define LXeEVAL_IDENTICAL LXxGOODCODE(LXeSYS_EVAL,1) #define LXeEVAL_DIFFERENT LXe_OK
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