Channels¶
How do I read the transform channels for a locator type item?¶
Unlike other 3D applications, modo allows you create multiple transforms (position, rotation and scale) on a single item, and allows you to reorder them to control how those transforms are applied. This results in an extremely flexible rigging workflow, allowing you to create complex transforms on a single item, without requiring a hierarchy of multiple items.
These transforms are stored as a separate item, connected to the locator type item that they want to manipulate, via a graph. This can be somewhat confusing, as you may try and search for the transform channels on the locator item and be unable to find them.
There are two ways to access and manipulate these transforms. Firstly, you can use the Locator Interface, this is an interface on the locator type item and has various methods for manipulating transforms. For example, if you want to get the main transform item for either rotation, scale or translation, you use the GetTransformItem method and pass it the type of transform you want. You can then read the channels on that item directly. There are also some helpful methods for getting and setting both the world and local transforms.
C++
//CLxUser_Item item; - given
CLxUser_Scene scene;
CLxUser_ChannelRead chan_read;
CLxUser_Locator locator (item);
LXtMatrix xfrm;
LXtVector pos;
if (scene.from (item))
{
if (scene.GetChannels (chan_read, 0.0))
locator.WorldTransform (chan_read, xfrm, pos);
}
Python
#item = lx.object.Item () # Given
locator = lx.object.Locator (item)
scene = item.Context ()
chan_read = lx.object.ChannelRead (scene.Channels (None, 0.0))
transforms = locator.WorldTransform (chan_read)
xfrm = transforms[0]
pos = transforms[1]
The alternative method for getting transform items, is to walk the graph and find the transform item directly. This is a little more in-depth, but allows greater control over the values returned, and also allows you to manipulate the transform items themselves.
The transform items are linked to the locator item they provide transforms for, in the XfrmCore graph, with the transform items connecting into the locator item. A single locator type item could potentially have multiple transforms connected through this graph. The order they are connected is the reverse order of the order they appear in the channel list inside of modo.
C++
//CLxUser_Item item; - given
CLxUser_Scene scene;
CLxUser_Item transform;
CLxUser_ItemGraph graph;
unsigned count = 0;
if (scene.from (item))
{
if (scene.GetGraph (LXsGRAPH_XFRMCORE, graph))
{
count = graph.Reverse (item);
for (unsigned i=0; i<count; i++)
{
graph.Reverse (item, i, transform);
}
}
}
Python
#item = lx.object.Item () # Given
scene = item.Context ()
graph = lx.object.ItemGraph (scene.GraphLookup (lx.symbol.sGRAPH_XFRMCORE))
count = graph.RevCount (item)
for i in range (count):
transform = graph.RevByIndex (item, i)
How do I read an item’s channel value?¶
It depends on the context. Mostly you use a ChannelRead Interface, unless you are in a modifier of some kind, in which case a different API should be used.
The normal case also has two forms. The first allows you to access channel values stored in the base (edit) action. Given a scene, an item in the scene and the channel index, you can get a channel read object from the scene and use that to access the value of channels in the action. It should be noted that in python, a time needs to be provided even if it’s not necessarily used.
C++
CLxUser_Item item; // given
CLxUser_Scene scene; // given
unsigned index; // given
CLxUser_ChannelRead chan_read;
scene.GetChannels (chan_read, LXs_ACTIONLAYER_EDIT);
fval = chan_read.FValue (item, index);
Python
item = lxu.object.Item() # given
scene = lxu.object.Scene() # given
index = int(channelIndex) # given
chan_read = scene.Channels(lx.symbol.s_ACTIONLAYER_EDIT, 0.0)
fval = chan_read.Double(item, index)
There are alternate methods for reading different channel types. Channels can be read given their name rather than index, as well. In python, this is built into the Value user method, which can read float, integer, or string channels without needing to specify the type in advance.
C++
ival = chan_read.IValue (item, LXsICHAN_TEXTURELAYER_ENABLE);
Python
ival = chan_read.Value(item, lx.symbol.sICHAN_TEXTURELAYER_ENABLE)
The other form is for reading evaluated channels, like the various matricies which are computed from the transform channels. In that case you provide the time for the evaluation rather than the action name:
C++
CLxUser_ChannelRead chan_read;
CLxUser_Matrix xfrm;
chan_read.from (item, 0.0);
chan_read.Object (item, index, xfrm);
Python
chan_read = scene.Channels(None, 0.0)
xfrm = chan_read.ValueObj(item, index)
If all you have is an item you can get a scene from that, and if you want to keep the index for faster access you can look it up from the item.
C++
scene.from (item);
index = item.ChannelIndex ("ChannelName");
Python
scene = item.Context()
index = item.ChannelLookup("ChannelName")
How do I set the default value of a string channel?¶
After adding the channel with the AddChannel Interface, you can obtain the default value object and call the methods of its Value Interface to set the string. The storage type of the channel has to be set to string.
C++
CLxUser_AddChannel ac; // given
LXtObjectID obj;
CLxUser_Value val;
ac.SetStorage (LXsTYPE_STRING);
ac.SetDefaultObj (&obj);
val.take (obj);
val.SetString ("Default Value");
The somewhat misleadingly named SetDefaultObj() returns an object on which you can call its Value Interface methods to set the default value, in this case a string.
Example item plugin: Setting a string channels default value
How do I write a value to an item’s channel?¶
Get a ChannelWrite Object, initialize it and set the channel’s value. The channel argument can be the channel index or the channel name: C++ will pick the right method for you. value can be integer, float or string.
C++
CLxUser_ChannelWrite chan;
chan.from (item);
chan.Set (item, channel, value);
How do I read a gradient channel?¶
A gradient channel is read using the ILxGradientFilter interface.
The evalulate method on the GradientFilter interface takes an input value (the X axis) and returns the corresponding output value or Y axis. In Python, gradient channels can be read into Value Objects, which are then read through a Gradient Filter Object just like C++.
C++
CLxUser_GradientFilter grad_filt;
CLxUser_Attributes attr(attr_obj);
double yAxis, xAxis = 0.0;
unsigned channelIndex;
attr.ObjectRO(channelIndex, grad_filt);
yAxis = grad_filt.Evaluate(xAxis);
Python
scene = meshItem.Context()
chanRead = scene.Channels(None, 0.0)
valueObj = chanRead.ValueObj(meshItem, meshItem.ChannelLookup('radGrad'))
grad_filt = lx.object.GradientFilter(valueObj)
yAxis = grad_filt.Evaluate(xAxis)
yAxis now holds the gradient channel value at the position on the gradient defined by xAxis.