# Expressions and Drivers

This article refers to PFM v0.4.3 and newer and may not be representative of older versions.

### Math Expressions

Any animatable actor property (color, radius, position, etc.) can be animated with a **math expression **(Similar to SFM's expression operators). The implementation is based on the high-performance exprtk library, which includes support for:

- Logical operators (and/or/not/etc.)
- Conditions (if/ternary/etc.)
- Loops (for/while/etc.)
- Vectors

##### Examples

As a basic example, let's say you want to animate the position of your actor, so you move it to [0,30,0] at timestamp 0, [0,50,20] at timestamp 1 and [30,0,-20] at timestamp 2. You can then apply the math expression `value *2`

to the property, which will double the values, meaning your actor will move from [0,60,0] to [0,100,40] and then to [60,0,-40] instead.

Here are some other basic examples:

`var newValue[3] := {0,value[1],0}; return [newValue];`

: Returns [0,value.y,0], which means the actor will only move on the y-axis.`var newValue[3] := {time *100,0,0}; return [newValue];`

: The channel value will be ignored, and the actor will move on the x-axis depending on how much time has passed since the start of the animation.`var f := time /duration; var newValue[3] := {sin(f *pi *2) *100,0,cos(f *pi *2) *100}; return [newValue];`

The channel value will be ignored, and the actor will move in a circular motion depending on the time passed.

##### Inputs

These are the variables available for use with math expressions:

**value**: The current value**time**: The current time in seconds**timeIndex**: The index into the`times`

array of the channel for the current timestamp.**startOffset**: The channel's start offset in seconds.**timescale**: The channel's time scale.**duration**: The duration of the channel (i.e. max time value in the`times`

array).

**Constants:**

- pi
- epsilon
- inf

##### Base Functions

- All built-in exprtk functions
`float noise(float v1,float v2,float v3)`

: Perlin noise.`float[X] value_at(float time)`

: Returns the value at the specified timestamp. The return value is either`float`

,`float[3`

] or`float[4]`

, depending on the channel value type.`float sqr(float v)`

: returns v *v`float ramp(float x,float a,float b)`

: returns (a == b) ? a : (x -a) /(b -a)`float cramp(float v1,float v2,float v3)`

: returns clamp(ramp(v1,v2,v3),0,1)`float lerp(float x,float a,float b)`

: returns a +(b -a) *x`float clerp(float 1,float v2,float v3)`

: returns clamp(lerp(v1,v2,v3),v2,v3)`float elerp(float v1,float v2,float v3)`

: returns ramp(3 *v1 *v1 -2 *v1 *v1 *v1,v2,v3)`float rescale(float x,float xa,float xb,float ya,float yb)`

: returns lerp(ramp(x,xa,xb),ya,yb)`float crescale(float v1,float v2,float v3,float v4,float v5)`

: returns clamp(rescale(v1,v2,v3,v4,v5),v4,v5)`print(...)`

: For debugging purposes, prints the specified arguments to the console.

##### Quaternion Functions

`q_from_axis_angle(float[3] axis,float angle,float[4] out)`

`q_forward(float[4] quat,float[3] out)`

`q_right(float[4] quat,float[3] out)`

`q_up(float[4] quat,float[3] out)`

`q_slerp(float[4] q0,float[4] q1,float factor,float[4] out)`

`q_lerp(float[4] q0,float[4] q1,float factor,float[4] out)`

`q_dot(float[4] q0,float[4] q1)`

`q_mul(float[4] q0,float[4] q1,float[4] out)`

`q_inverse(float[4] quatInOut)`

`float q_length(float[4] quat)`

Contrary to SFM's expression operators, PFM's math expressions **cannot** reference anything outside of the animation channel for the property it's assigned to. To create dependencies between properties, you need to use animation drivers instead.

### Animation Drivers

Animation drivers are similar to math expressions, with a few key differences:

- Lua expression instead of a math expression
- Can be used to create dependencies between arbitrary properties (this is their primary purpose)
- Similar to Blender's driver system
- Slower to compute than math expressions, use sparingly

Since animation drivers are Lua-based, that means you have the entire Lua API available, making them much more powerful tools compared to math expressions. Their purpose is to animate a property programmatically with dependencies to other properties. To do so, animation drivers can have input variables, which can be references to actors, components or component properties.

##### Examples

Animation drivers cannot be created through PFM yet, so the following are Lua-based examples. These may seem a little daunting, but once the system is integrated into the interface, drivers will be much easier to use.

- Changing the color of a light source depending on its distance to an actor (red = actor is close, green = actor is far away):

```
local actorId = "327b5bb8-74ae-400e-bbf7-61a2ad2020d3" -- Id of the actor for whom we check the distance
local expr = [[
local dist = trLight:GetDistance(trActor)
return Color.Red:Lerp(Color.Lime,math.clamp((dist -120) /150.0,0.0,1.0)):ToVector()
]]
lightSource:AddDriver(
ents.COMPONENT_COLOR,"color",
game.ValueDriverDescriptor(expr,{
-- 'trActor' variable referencing the transform component of the actor
["trActor"] = "pragma:game/entity/ec/transform?entity_uuid=" .. actorId,
-- 'trLight' variable referencing the transform component of the light
["trLight"] = "pragma:game/entity/ec/transform"
})
)
```

- Making an actor always face the camera:

```
local camId = "6f964743-faa6-4b2b-b781-41a258e2f7d1" -- Id of the camera actor
local expr = [[
local angToCamera = self:GetAngles(trCam)
return EulerAngles(0,angToCamera.y,0)
]]
actor:AddDriver(
ents.COMPONENT_TRANSFORM,"angles",
game.ValueDriverDescriptor(expr,{
-- 'trCam' variable referencing the transform component of the camera actor
["trCam"] = "pragma:game/entity/ec/transform?entity_uuid=" .. camId
})
)
```

## No Comments