Animation

_images/icon_animation.webp

Bendy creeper.

This is the reference configuration for animating OptiFine's Custom Entity Models (CEMA).

Important

These apply inside of Models and this document is kept separate for organizational purposes only. It is not a separate file.

Each model variable which is to be animated is assigned a mathematical expression. The expression is evaluated every time the model is rendered and its value is assigned to the variable. This value controls the positions, scales, rotations, etc. of the parts of the model.

Important

Animations must be in the parent bone, not any sub-bone.

The variables and expressions are defined in the "animations" section of the JSON entity model (JEM):

{
    "animations": [
        {
            "variable1": "expression1",
            "variable2": "expression2"
        }
    ]
}

Keys

Variables make up the keys of the items in the objects in the "animations" list. They are strings that refer to different values that control an entity.

For example, in the object {"a": 5}, "a" is the key and 5 is the value.

Model variables

Model variables are specified in the format <model>.<variable_name>.

The <model> can be one of:

  • this: the current custom model.

  • part: the original part model to which the custom model is attached.

  • <part>: the original model by part name.

  • <id>: the custom model by an assigned ID.

  • <part>:<sub_id>:<sub_sub_id>:...: (hierarchical) start with the original model by part name, then find its children by ID.

  • <id>:<sub_id>:<sub_sub_id>:...: (hierarchical) start with the original model by ID, then find children its by ID.

The first model found by part name or ID is used if there are duplicates. The model search by ID is deep, and is also deep when used in a hierarchical specification.

The hierarchical specification allows model groups (JSON part models) to be reused for different parts. For example, one hand model (shoulder:upper_arm:elbow:forearm:palm:finger[1.5]) can be used for both left and right hands; left_hand:finger1 can be for the left thumb and right_hand:finger1 for the right thumb.

The intermediate parents in the hierarchical specification can be skipped.

Model variable names

  • tx, ty, tz: translation x, y, z (movement).

  • rx, ry, rz: rotation x, y, z (spin, yaw, pitch).

  • sx, sy, sz: scale x, y, z (size).

  • visible: show model and submodels (boolean).

  • visible_boxes: show model only; this does not affect submodels (boolean).

Added in version H9: visible and visible_boxes

Entity variables

Warning

Entity variables are not supported for block entities.

Entity variables are user-defined variables that contain a formula that is calculated every frame. These variables are associated with the rendered entity in-memory. They are not "stored" with the entity in the game itself; unloading and reloading the world will reset them.

Entity variables can be specified in 2 formats:

  • var.<name> for floats (decimals, numbers).

  • varb.<name> for booleans (true, false).

Where <name> may be any string; var.xyz, var.last_rx, var.cookies_in_the_cookie_jar are all valid entity variables.

Their default value when first calculated is 0 or false for var and varb, respectively. For instance, the first calculation of "var.xyz": "var.xyz + 1" is 1 (var.xyz = 0; var.xyz + 1 = 0 + 1 = 1.

Entity variables are useful for storing animation data between frames or storing constants for animation configuration.

Added in version H9: varb

Render variables

Added in version H9.

  • render.shadow_size: The size of the entity's shadow; this is a float from 1.0 (opaque) to 0.0 (invisible).

  • render.shadow_opacity: The opacity (solidness) of the entity's shadow.

  • render.leash_offset_x: When leashed, where the leash on the entity attachs to.

  • render.leash_offset_y

  • render.leash_offset_z

  • render.shadow_offset_x: An offset of the entity's shadow position.

  • render.shadow_offset_z

Values

Expressions make up the values of the items in the objects in the "animations" list. They are general mathematical expressions with brackets, constants, variables, operators, parameters, and functions.

Hint

They most closely resemble the AsciiMath format, although they are not identical.

Optionally-grouped variables, constants, parameters, and functions are separated by operators and evaluated as normal math expressions. For example, sin(35)+max(5,50,500) evaluates to 500.5735764.

Constants

Constants never change and always evaluate to the same value.

Name

Type

Meaning

pi

Float, constant

Floating point, equal to 3.1415926.

true

Boolean, constant

Truthy boolean.

false

Boolean, constant

False boolean.

Variables

Variables change and are most often used to change something with time.

Not to be confused with

Keys; these are expression variables.

Name

Type

Meaning

<model>.<var>

Any

A model variable's value, see the Model variables section.

time

Integer, ticks

The total game time in ticks (0..720720); not related to the daylight cycle.

day_time

Integer, ticks

The current day time in ticks (0..24000).

Added in version I3.

day_count

Integer

The current day count.

Added in version I3.

Render parameters

Render parameters are variables whose values are generally independent of the entity being rendered, but not always.

Name

Type

Meaning

limb_swing

Float

Counts up in ticks from 0 as the entity continues to move.

limb_speed

Float

The current speed of the entity's limbs. Ranges from 0.0 (still) to 1.0 (sprinting).

age

Float

How long the entity has existed in the world, in ticks.

head_yaw

Float

Head yaw; x rotation.

head_pitch

Float

Head pitch; y rotation.

player_pos_x

Float

Player's X position (not necessarily the same entity).

Added in version H8.

player_pos_y

Float

Player's Y position (not necessarily the same entity).

Added in version H8.

player_pos_z

Float

Player's Z position (not necessarily the same entity).

Added in version H8.

player_rot_x

Float

Player's yaw (left-right).

Added in version H8.

player_rot_y

Float

Player's pitch (up-down).

Added in version H8.

frame_time

Float

Time in seconds since the last frame.

Added in version H9.

dimension

Integer

Dimension ID, -1 = Nether, 0 = Overworld, 1 = End.

Added in version H9.

rule_index

Integer

The index of the current matching random models rule. Defaults to 0.

Added in version I1.

Entity parameters

Entity parameters are variables that are generally unique to the entity itself: is the entity glowing, is it angry, etc.

Name

Type

Meaning

Floats

health

Float

Entity's current health.

hurt_time

Float

Time stage of when entity has been hurt once. Counts down from 10 to 0.

death_time

Float

Time stage on entity's death. Counts up from 0 to 20.

Added in version H8.

anger_time

Float

The time the entity has been angry. 0 while neutral, 720 while aggressive, counts down to 0 when the target is lost.

Added in version H9.

max_health

Float

Entity's maximum health.

move_forward

Float

How much entity is moving forwards-backwards, currently broken.

move_strafing

Float

How much entity is moving left-right, currently broken.

pos_x

Float

Entity's X position.

pos_y

Float

Entity's Y position.

pos_z

Float

Entity's Z position.

rot_x

Float

Entity's X-axis rotation.

Added in version H8.

rot_y

Float

Entity's Y-axis rotation.

Added in version H8.

swing_progress

Float

How far through the attack animation the entity is; counts up from 0.0 to 1.0.

id

Float

A unique numeric identifier.

Added in version H8.

Booleans

is_aggressive

Boolean

If the entity is aggressive towards another entity.

Added in version H9.

is_alive

Boolean

If the entity is alive; not dead.

is_burning

Boolean

If the entity is burning.

is_child

Boolean

If the entity is a child.

is_glowing

Boolean

If the entity has the Glowing status effect.

is_hurt

Boolean

If the entity is taking damage.

is_in_hand

Boolean

If the entity (items) is being held in your hand.

is_in_item_frame

Boolean

If the entity (items) is in an item frame.

Added in version H7.

is_in_ground

Boolean

If the entity is embedded into a block (arrows, tridents).

is_in_gui

Boolean

If the entity is inside the GUI

is_in_lava

Boolean

If the entity is touching lava.

is_in_water

Boolean

If the entity is touching water.

is_invisible

Boolean

If the entity has the Invisibility status effect.

is_on_ground

Boolean

If the entity is on the ground; not flying.

is_on_head

Boolean

If the entity (items) is on an armor head slot.

Added in version H7.

is_on_shoulder

Boolean

If the entity is on a player's shoulder (parrots).

Added in version H9.

is_ridden

Boolean

If the entity is being ridden by another entity.

is_riding

Boolean

If the entity is riding atop of another entity.

is_sitting

Boolean

If the entity is sitting (cat, wolf, parrot).

Added in version H9.

is_sneaking

Boolean

If the entity (cats) is crouching/sneaking.

is_sprinting

Boolean

If the entity (cats) is sprinting.

is_tamed

Boolean

If the entity is tamed (dogs, cats).

Added in version H9.

is_wet

Boolean

If the entity is under rain or is inside a water block.

Operators

Name

Meaning

+, -, *, /, %

add, subtract, multiply, divide, modulo

!, &&, ||

negate, logical AND, logical OR

>, >=, <, <=, ==, !=

greater than, greater than or equal to, less than, less than or equal to, is equal to, is not equal to

Numerical functions

These functions return a number.

Name

Parameters

Return

sin(x)

x: any number

Sine of degrees x.

cos(x)

x: any number

Cosine of degrees x.

asin(x)

x: any number

Arcsine of degrees x.

acos(x)

x: any number

Arccosine of degrees x.

tan(x)

x: any number

Tangent of degrees x.

atan(x)

x: any number

Arctangent of degrees x.

atan2(y, x)

y: any number, x: any number

Two-argument arctangent of y and x.

torad(deg)

deg: any degree

Convert degrees to radians.

todeg(rad)

rad: any radian

Convert radians to degrees.

min(x[, y...]

x...: any number

The minimum of all given parameters.

max(x[, y...]

x...: any number

The maximum of all given parameters.

clamp(x, min, max)

x, min, max: any number

x, guaranteed to be between min and max values; if x > max, x = max, x < min, x = min.

abs(x)

x: any number

The absolute value of x; abs(-5) == 5.

floor(x)

x: any number

The floor of x; floor(2.9) == 2.

ceil(x)

x: any number

The ceiling of x; ceil(2.1) == 3.

exp(x)

x: any number

e (Euler's constant) raised to the power of x; exp(4) == 54.598150033144236.

frac(x)

x: any decimal

The decimal of x; frac(11.4) == 0.4.

log(x)

x: any number

The logarithm of x; log(50) == 3.912023005428146.

pow(x, y)

x: any number, y: any positive number

Raise base x to the power y; pow(5, 2) == 25

random(seed)

seed: any integer

A random number between 0.0 and 1.0. seed is optional and if given, this returns the same result.

Added in version H8.

round(x)

x: any decimal

Rounded x; round(5.4) == 5.

signum(x)

x: any number

The sign of x; signum(0) == 0, signum(-5253) == -1.

sqrt(x)

x: any positive number

The square root of x; sqrt(25) == 5.

fmod(x, y)

x, y: any number

Similar to Java's floorMod function, x - (floorDiv(x, y) * y); if the signs of the arguments are the same, the results of fmod and the % operator are the same, but if the signs of the arguments are different, the results differ: floorMod(+4, -3) == -2, (+4 % -3) == +1

lerp(k, x, y)

k, x, y: any number

The linear interpolation of x and y; (1 - k) * x + k * y.

Added in version H9.

if(cond, val[, cond2, val2, ...], val_else)

cond: condition string, val: any value, val_else: any value

Select a value based on one of more conditions: return val if cond is true, return val_else if cond is false

print(id, n, x)

id: any number, n: frame interval, x: value to print

Prints x in the log every n-th frame, under id.

Added in version H8.

printb(id, n, x)

id: any number, n: frame interval, x: boolean to print

Prints x boolean in the log every n-th frame, under id.

Added in version H9.

Boolean functions

These functions return either true or false.

Name

Parameters

Tests

between(x, min, max)

x, min, max: any number

Is x between min and max?

equals(x, y, epsilon)

x, y, epsilon: any number

Is the difference of x and y within error margin epsilon? abs(x-y) < epsilon

in(x, val1[, val2...])

x, val1...: any number

Is x equivalent to any of of val1...?

Examples

Basic structure

{
    "animations": [
            {
                    "this.rx": "clamp(-0.5 * part.rx, 0, 90)",
                    "this.tx": "3 * sin(limb_swing / 4) - 2",
                    "this:Hoof.rx": "if(leg4:Hoof.rx > 90, leg4:Hoof.rx - 90, 0)"
            }
    ]
}

Walking animation

x is a multipler to control how fast the leg swings back and forth, and y is a multiplier to control how far it swings back and forth.

"left_leg.rx": "sin(limb_swing * x) * limb_speed * y"

Attack animation

x is a multipler for how much it rotates.

"head.rx": "sin(swing_progress * pi) * x"

Hurt animation

x is a multipler for how much it rotates.

"head.rx": "-sin(hurt_time / pi) * x"

Custom counter

This is a counter that will count up while an entity is in water, and count down again when it leaves.

"var.counter": "if(is_in_water, min(20, var.counter + 0.1 * frame_time * 20), max(0, var.counter - 0.1 * frame_time * 20))"

If statements

The leg will rotate by 45 degrees when the entity is not on the ground, otherwise it will stay at 0 deg.

"left_leg.rx": "if(!is_on_ground, torad(45), 0)"

The body will tilt forwards once the entity hits a certain movement speed.

"body.rx": "if(limb_speed > 0.7, torad(20), 0)"

Tutorial

_images/cem_ewan.webp

JSON schema

{
	"$schema": "http://json-schema.org/draft/2020-12/schema",
	"$id": "https://gitlab.com/whoatemybutter/optifinedocs/-/blob/master/schemas/cem_anim.schema.json",
	"title": "Custom Entity Models Animation",
	"description": "CEM Animations change how a custom entity model walks, swims, idles, etc.",
	"type": "object",
	"properties": {
		"animations": {
			"type": "array",
			"items": {
				"type": "object",
				"patternProperties": {
					"^(this|part|[0-9a-zA-Z_]*((:[0-9a-z-A-Z_])*)?)\\.(t[xyz]|r[xyz]|s[xyz])$": {
						"$ref": "#/$defs/expression"
					},
					"^(this|part|[0-9a-zA-Z_]*((:[0-9a-z-A-Z_])*)?)\\.(render\\.(shadow_size|shadow_opacity|leash_offset_[xyz]|shadow_offset_[xz]))$": {
						"$ref": "#/$defs/expression"
					},
					"^(this|part|[0-9a-zA-Z_]*((:[0-9a-z-A-Z_])*)?)\\.(visible|visible_boxes)$": {
						"type": "boolean"
					},
					"^varb?\\.(.+)$": {
						"$ref": "#/$defs/expression"
					}
				}
			}
		}
	},
	"$defs": {
		"expression": {
			"type": "string",
			"minLength": 1,
			"anyOf": [
				{
					"pattern": "pi|true|false|time|day_time|day_count"
				},
				{
					"pattern": "(this|part|[0-9a-zA-Z_]*((:[0-9a-z-A-Z_])*)?)\\.(.*?)"
				},
				{
					"pattern": "limb_swing|limb_speed|age|head_yaw|head_pitch|player_pos_[xyz]|player_rot_[xyz]|frame_time|dimension|rule_index"
				},
				{
					"pattern": "(max_)?health|(hurt|death|anger)_time|move_(forward|strafing)|pos_[xyz]|rot_[xy]|swing_progress|id"
				},
				{
					"pattern": "is_(aggressive|alive|burning|child|glowing|hurt|in_(hand|item_frame|ground|gui|lava|water)|invisible|on_(head|shoulder|ground)|ridden|riding|sitting|sneaking|sprinting|tamed|wet)"
				},
				{
					"pattern": "\\+|-|\\*|\/|%|add|subtract|multiply|divide|!|&&|\\|\\||[<>]=?|[!=]="
				},
				{
					"pattern": "(sin|cos|asin|acos|tan|atan|to(rad|deg)|abs|floor|ceil|exp|frac|log|random|round|signum|sqrt)\\((-?\\d+(.\\d+)?)\\)"
				},
				{
					"pattern": "(atan2|pow|fmod)\\((-?\\d+(.\\d+)?), (-?\\d+(.\\d+)?)\\)"
				},
				{
					"pattern": "printb?\\((.*?), ?(-?\\d+(.\\d+)?), ?(.*?)\\)"
				},
				{
					"pattern": "i[fn]\\(((.*?), ?(.*?))+\\)"
				},
				{
					"pattern": "lerp|clamp|between|equals\\((-?\\d+(.\\d+)?), ?(-?\\d+(.\\d+)?), ?(-?\\d+(.\\d+)?)\\)"
				},
				{
					"pattern": "m(in|ax)\\(((-?\\d+(.\\d+)?)(?:,\\s*(-?\\d+(.\\d+)?))?)\\)"
				},
				{
					"pattern": "-?\\d+(.\\d+)?"
				}
			]
		}
	},
	"additionalProperties": false
}

Assumes the latest OptiFine version.
Updated to commit 15ef3106.

Last update: 2024 April 30