Skip to content

Using Expressions#

Clarisse includes a powerful expression engine to drive attribute values. In addition to direct value input, via the Attribute Editor for instance, or f-curve, attribute values can now be driven using a full featured expression system based on the powerful SeExpr language developed by Walt Disney Animation Studios.

Using expressions in Clarisse, you can for example:

  • Drive attributes of items;
  • Procedurally drive attribute values using mathematical functions;
  • Re-time animation from a f-curve driving an attribute value;
  • Bind attributes together and use other attribute values in expressions;
  • Procedurally get the name of the object which holds the attribute with the expression, and get the path of its context;
  • Find items based on rules, like you would using rules in Groups but at the expression level;
  • Make powerful scripts using the expression language to drive the value of attributes;
  • Control other attributes to create self-contained setups that can be published and reused as procedural assets.

Using Expressions#

Since expressions operate on attributes values, they are managed by the Attribute Editor. Any attributes associated with the ∑ button in the Attribute Editor can be driven by expressions. One important thing we must insist on is that expressions can only drive attribute values. They don't directly drive the attribute.

Indeed, when an attribute has multiple values, you have to bind each value with the same expression to drive all the values of the attribute. For example, let's take the case of the Translate attribute of a Scene Item which has 3 values (X, Y, Z). While each one can be driven by an expression, you can't drive the attribute itself with a single expression that would replace all attribute values.

As a result, it is then possible to have values driven by expressions while others are driven by f-curves for example. For more information on actual use cases and example refer to Examples and real world use cases.

Limitations

While most attributes can be driven using expressions, attributes of type reference/object lists such has the Constraints attribute of a Scene Item, can't be driven by expressions. However, attributes like numeric values (scalar, vectors and colors), list of values (like Rotation Order), boolean (like Inherit Transform) and references (like Parent) can all be driven by expressions.

Binding Expressions#

In order to bind expressions, you must click on the ∑ button. This will highlight the expression button and change the attribute value field into a text input field where you can type your expression.

Note

Expressions are pre-filled using the last value of the attribute.

Once the ∑ button has been clicked, it will be kept highlighted to show that an active expression is now driving the value. In some cases, like attribute of type color, you first need to expand the attribute fields before accessing the ∑ button (you'll note that the ∑ button is greyed out). Please refer to Attribute Color Display for more information on how to expand color attributes.

Editing Expressions#

Once an expression is bound to an attribute value, there are 2 ways for specifying and editing the expression:

  1. use the single line text field directly in the Attribute Editor
  2. click on the ... button at the right of the expression text field to open the attribute script editor.

Removing Expressions#

Expressions can simply be removed from any attribute field by emptying the text field and pressing Enter. This will automatically unbind the expression from the attribute value. Another way is to remove all expressions of an attribute is using the attribute contextual menu: Right Button on the attribute name, in the Attribute Editor, and select Remove Expression.

Note

When removing expressions, the attribute values are not reset to the values they had before binding the expressions. Attribute values keep the last values computed by the expressions.

Expression Display Modes#

The Attribute Editor offers 2 modes when displaying attribute values driven by expressions. It either displays the expression or the resulting value of the expression. Using the ∑ button in the Attribute Editor toolbar, you can switch between these 2 display modes. Please note that in the Value display mode, the edition field is read-only as the attribute value is driven by the expression.

Top mode set to expression default Botton mode set to value

Top: mode set to expression (default) Button: mode set to value

Enabling/Disabling Expressions#

It is possible to disable an expression from an attribute value without removing it. This way the attribute value stops being driven by the expression. To disable an expression, click on the ∑ button.

Note

When the expression is disabled, the attribute value keeps the last value computed by the expression and the value can be then freely modified.

To enable a disabled expression, simply click again on the ∑ button.

Examples and real world use cases#

The following examples and use cases aim at illustrating how expressions can be used to add procedurality in your projects. For more details about the SeExpr language and Clarisse specific functions, please refer to SeExpr Language Reference and SeExpr Clarisse Specifics sections.

Hello World#

Clarisse SeExpr extension provides a convenient log_info function to log values from expressions. This comes at a performance cost as logging during attribute value evaluation is adding computation overhead.

log_info("Hello world")
# Logs "Hello world" via Clarisse's logging system
log_info("Hello float ", 3.14)
# Logs "Hello float 3.14" via Clarisse's logging system

Note

Standard SeExpr printf binding isn't implemented. Instead, you must use the Clarisse specific log_info function.

Simple Expressions#

Because expressions are meant to define the values of attributes, the minimal expression that can be written is the final value itself. SeExpr supports 2 types of values: floating point numbers and character strings. The following examples show minimal expressions for floating point return values written in decimal format, floating point return values written in integer format and string return value.

3.14
# floating point return value in decimal format
10
# floating point return value in integer format
"Once upon a string"
# string return value

Pendulum#

Assuming a pendulum of period 1s and amplitude 50°, its angular equation of motion is:

\[ \theta(t)=50 cos(2\pi t) \]

The expression to bind to rotate[0], for instance, to animate the pendulum is:

50 * cos(2 * PI * T) # cos and PI are SeExpr built-in symbols
# while T is the global time variable from Clarisse.
# Character # is used for comments in SeExpr.

Damped Pendulum#

Assuming the above pendulum has some friction, the equation of motion becomes (with a damping time of 1s):

\[ \theta(t)=50 e^{-t} cos(2\pi t) \]

Which translates to the expression as:

50 * exp(-T) * cos(2 * PI * T)
# exp is another SeExpr built-in function

Shaky Camera#

In order to add shakes to the camera Y position, one can use the snoise function from SeExpr, that creates noise in the [-1;1] range based on time changes. Taking, for instance, a camera height of 1.5m and a maximum noise amplitude of 10cm, the expression in the attribute translate[1] is:

1.5 + snoise(T) * 0.1
# snoise is another SeExpr built-in function

Bouncing Ball#

In this example, we will drive the Y position of a ball with radius R, using the X position to achieve a Y=f(X) relationship. For the motion, we will use this simple equation:

\[ Y = Y_0 | cos(\omega X) | + R \]

Using \(R = 0.5m, Y_0 = 5m\) and \(\omega = 0.3 rad/m\), the expression to bind to translate[1] is:

X = get_double("translate[0]");
# Declaring the X variable to read the value from translate[0]
# using get_double, a Clarisse specific function.
# Using classic = operator to assign the value of translate[0] to X.
# We end up the line using ; because this is an intermediate
# expression, the final value is not computed here.
5 * abs(cos(0.3 * X)) + 0.5
# Using X defined earlier, we compute the final value.
# Note: we do not end up the line using ;

Conditional Execution#

SeExpr is powerful enough to support conditional expressions. For instance, let's say we want to set the value of an attribute to 0 when the frame number is lesser than 25 and to 1 when the frame number is greater or equal to 25.

To query the current frame, we will use F, the global frame variable defined by Clarisse. There are two ways to write this expression:

F < 25 ? 0 : 1
# Using the C-like conditional operator.
# More suitable syntax for single statement execution.
# F is the global frame variable defined by Clarisse.

or

if (F < 25) {
    # Using the C-like if-else statements with block { } separators.
    # More suitable syntax for multiple statements execution.
    value =  0;
    # Note: you can define new variables in conditional blocks (for instance "value").
} else {
    value = 1;
}
value

Controlling Attribute Animation#

Using the fcurve function, one can read the animation f-curve bound to the current attribute value or even the f-curve bound to another attribute in the project:

fcurve(T + 10)
# Offsets the animation by 10 seconds
fcurve(T / 4)
# Runs the animation at 25% of its original speed
fcurve(T % 2)
# Repeat the animation every 2 seconds
fcurve(T, "translate[0]")
# Reads the animation of translate[0] in the same object
fcurve(T, "project://scene/locator.translate[0]")
# Reads the animation of translate[0] of object "locator"
# located in context "project://scene".

Re-timing Alembic Cache#

Timings of Alembic Bundles and Alembic items from Reference contexts can be controlled using the Frame attribute which controls the current frame of the item. By default, the Frame attribute is driven by the expression F so that its value matches the current frame of Clarisse.

You can change the speed of the animation by editing the expression bound to the Frame attribute. For instance, writing F * 2 will double the playback speed of the Alembic cache.

Note

Editing expressions of items in References will create overrides.

Binding Attribute Values#

The expression system can be used to bind attribute values so that the values of some attributes can be used to drive the value of some other attributes. Clarisse provides specific functions to query the value from attributes in your project.

For instance, in our bouncing ball example, we used the function get_double to query the value of translate[0] and use it to compute the value of translate[1].

Clarisse provides 3 functions to query attribute values: get_double, get_string and get_vec3. The following example demonstrate how to create a "tail" rig where all "tail" segments use the same expression to read the rotation value of the parent segments and apply the rotation to the current segment. For instance, on rotate[0]:

Tail rig using getdouble and getstring functions

Tail rig using get_double and get_string functions

parent_item = get_string("parent");
# Get the path to the parent item from the parent attribute
parent_attr = parent_item + "rotate[0]";
# Form the path to the rotate[0] attribute of the parent item
get_double(parent_attr)
# Return the value of the rotate[0] attribute of the parent item

Procedural Item Lookup#

Expressions can be used on Reference attributes and they require a String as return type. For instance, you can parent 2 Scene Items using expressions. Let's imagine we have 2 Locators, locator1 and locator2, and we want to parent a Light to locator1 if the current frame is less than 25 or to locator2 otherwise. You could could the Parent attribute as follows:

F < 25 ? "locator1" : "locator2"
# Parent references "locator1" or "locator2" depending on F

This works great as long as you know the path of the item you want to reference. But when you don't know exactly the name of the item, you may want to search for the item based on a rule, like rule-based Groups work.

Note that find_item uses the same rule syntax as for Groups. Let's say we don't know the names of the Locators but just that they end with 1 and 2. We could rewrite the above expression like this:

find_item(F < 25 ? "*1" : "*2", "Locator")
# Tries to find the corresponding Locator.
# Note: find_item returns an empty string if no item is found.

Scene templates using global variables#

Global variables like System, Built-in or Custom variables are imported as predefined variables and can be directly used in expressions.

For instance the time built-in variable $T can directly be used in expressions as the T variable.

Same applies to user-defined Custom variables. For instance, you can define a $shot variable and use it to control the behavior of your project depending on its value. Doing this will templatize your project by adding procedural control on it.

Let's imagine the following project and the Custom variable $shot. We want to select the light rig (either in context project://lights/shot01 or project://lights/shot02) according to the value of $shot variable.

Example project structure with 1 light rig per shot

Example project structure with 1 light rig per shot

The $shot variable is defined as String and can either take "shot01" or "shot02" as value.

Setting up the shot Custom variable

Setting up the $shot Custom variable

In context "project://lights" we've created a Group called "light_group" that is referenced by the "Background" 3D Layer of image "project://scene/image". We use an expression to drive the Inclusion Rule attribute of light_group:

"./" + shot + "/*"
# Resolves to either "./shot01/*" when $shot = "shot01"
# or to "./shot02/*" when $shot = "shot02".

As you can see below, depending on the value of $shot, the group light_group picks up the correct lights: