Animation

_images/icon_animation.webp

He has the bends.

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): For example, in the object {"a": 5}, "a" is the key and 5 is the value.

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

Essentially, we are writing equations in JSON: head.rx = swing_progress * 3 will be transformed into a key-value pair of "head.rx": "swing_progress * 3". In this, head.rx is the destination or key, and swing_process * 3 is the source or value or expression.

Note

Some headers use {a, b, c}. This means it may be any one of these options (e.x. pos_{x, y, z} means pos_x or pos_y or pos_z).

Key: destination

The keys of the objects in the "animations" list are the targets for their value's expression. They are strings that refer to different values that control an entity.

Model, arbitrary, and render variables are all valid options.

Model variables

Model variables are specified in the format MODEL_TARGET.MODEL_VARIABLE. They refer to specific parts of the model.

Model target names

The first model found by part name or ID is used if there are duplicates.

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.

Name

Description

this

The current custom model.

part

The original part model to which the custom model is attached.

<part name>

Placeholder; a part name.

<id>

Placeholder; the custom model by ID.

<part name>:<id>:...

Placeholders; hierarchical query for nested parts by their names and sub-IDs. There may be any number of <id>. The intermediate parents in the hierarchical specification can be skipped. The first deep nested match is used.

<id>:<id>:...

Placeholders; hierarchical query for nested parts by their ID and sub-IDs. There may be any number of later <id>. The intermediate parents in the hierarchical specification can be skipped. The first deep nested match is used.

Model variable names

tx, ty, tz

Type: Float

Default: 0.0, 0.0, 0.0

Optional

Translation in X, Y, Z coordinates (movement).

sx, sy, sz

Type: Float

Default: 1.0, 1.0, 1.0

Optional

Scale in X, Y, Z coordinates (size).

rx, ry, rz

Type: Float

Default: 0.0, 0.0, 0.0

Optional

Rotation in X, Y, Z coordinates (spin, yaw, pitch).

visible

Type: Boolean

Default: true

Optional

Whether to show model and submodels.

visible_boxes

Type: Boolean

Default: true

Optional

Whether to show this model box only and not affect submodels; boolean.

Arbitrary variables

Warning

Arbitrary variables are not supported for block entities.

Arbitrary 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.

They may be useful for storing animation data between frames or storing constants for animation configuration.

The placeholder NAME may be any valid string.

var.NAME

Type: Float

Default: 0.0

Optional

Arbitrary variable name that stores floats.

Uninitialized value is 0. Therefore, The first calculation of "var.xyz": "var.xyz + 1" is 1 (var.xyz = 0; var.xyz + 1 => 0 + 1 = 1).

varb.NAME

Type: Boolean

Default: false

Optional

Arbitrary variable name that stores booleans.

Render variables

render.shadow_size

Type: Float

Values: Positive float

Default: 1.0

Optional

The size of the entity's shadow.

render.shadow_opacity

Type: Float

Values: From 0.0 to 1.0

Default: 1.0

Optional

The opacity (alpha) of the entity's shadow.

render.shadow_offset_{x, z}

Type: Float

Default: 0.0, 0.0

Optional

An offset of the entity's shadow position.

render.leash_offset_{x, y, z}

Type: Float

Default: 0.0, 0.0, 0.0

Optional

When leashed, where the leash on the entity attachs to.

Values: source

The values of the objects in the "animations" list are expressions that are evaluated and stored in their corresponding keys. They are strings of general mathematical expressions with brackets, constants, variables, operators, parameters, and functions.

For example, sin(35) + max(5, 50, 500) evaluates to 500.5735764.

Hint

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

Constants

Constants never change and always evaluate to the same value.

Note

A literal (such as 5) is also a constant, but has no special meaning apart from its value.

pi

Type: Float

Optional

Value of pi; 3.1415926.

true

Type: Boolean

Optional

True value.

false

Type: Boolean

Optional

False value.

Variables

Variables are globally accessible names that can change.

Not to be confused with

Keys; these are to be used in expressions.

MODEL.VAR

Type: Any

Optional

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

Important

MODEL and VAR are placeholders.

age

Type: Integer

Values: Positive integer

Optional

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

anger_time

Type: Float

Values: Positive float

Optional

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

day_count

Type: Integer

Values: Positive integer

Optional

How many days have passed in this world.

day_time

Type: Integer

Values: From 0 to 24000

Optional

The current day time.

death_time

Type: Float

Values: From 0.0 to 20.0

Optional

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

dimension

Type: Integer

Optional

Dimension ID.

ID

Dimension

1

The End

0

Overworld

-1

Nether

frame_counter

Type: Integer

Values: From 0 to 27719

Optional

Index of the current frame, wraps around at 10 minutes (0 to 27719).

frame_time

Type: Integer

Values: Positive integer

Optional

Time in seconds since the last frame.

head_yaw

Type: Integer

Values: Positive integer

Optional

Head yaw; X rotation.

head_pitch

Type: Integer

Values: Positive integer

Optional

Head pitch; Y rotation.

health

Type: Float

Optional

Current health.

hurt_time

Type: Float

Values: From 10.0 to 0.0

Optional

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

id

Type: Float

Optional

A unique numeric identifier.

is_aggressive

Type: Boolean

Optional

If the entity is aggressive towards another entity.

is_alive

Type: Boolean

Optional

If the entity is alive; not dead.

is_burning

Type: Boolean

Optional

If the entity is burning.

is_child

Type: Boolean

Optional

If the entity is a child.

is_glowing

Type: Boolean

Optional

If the entity has the Glowing status effect.

is_hurt

Type: Boolean

Optional

If the entity is taking damage.

is_in_hand

Type: Boolean

Optional

Item: if the entity is being held in the player's hand.

is_in_item_frame

Type: Boolean

Optional

Item: if the entity is in an item frame.

is_in_ground

Type: Boolean

Optional

Arrow, trident: if the entity is embedded into a block.

is_in_gui

Type: Boolean

Optional

If the entity is inside the GUI

is_in_lava

Type: Boolean

Optional

If the entity is touching lava.

is_in_water

Type: Boolean

Optional

If the entity is touching water.

is_invisible

Type: Boolean

Optional

If the entity has the Invisibility status effect.

is_on_ground

Type: Boolean

Optional

If the entity is on the ground; not flying.

is_on_head

Type: Boolean

Optional

Item: if the entity is on an armor head slot.

is_on_shoulder

Type: Boolean

Optional

Parrot: if the entity is on a player's shoulder.

is_ridden

Type: Boolean

Optional

If the entity is being ridden by another entity.

is_riding

Type: Boolean

Optional

If the entity is riding atop of another entity.

is_sitting

Type: Boolean

Optional

Cat, wolf, parrot: if the entity is sitting.

is_sneaking

Type: Boolean

Optional

Cat, ocelot: if the entity crouching.

is_sprinting

Type: Boolean

Optional

Cat, ocelot: if the entity is sprinting.

is_tamed

Type: Boolean

Optional

Cat, wolf, parrot: if the entity is tamed.

is_wet

Type: Boolean

Optional

If the entity is under rain or is touching water.

limb_swing

Type: Integer

Values: Positive integer

Optional

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

limb_speed

Type: Float

Values: From 0.0 to 1.0

Optional

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

max_health

Type: Float

Values: Positive float

Optional

Entity's maximum health.

player_pos_{x, y, z}

Type: Float

Optional

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

player_rot_{x, y}

Type: Float

Optional

Player's yaw (left-right) and pitch (up-down), respectively.

pos_{x, y, z}

Type: Float

Optional

Entity's X, Y, Z position.

rot_{x, y}

Type: Float

Optional

Entity's yaw (left-right) and pitch (up-down), respectively.

rule_index

Type: Integer

Default: 0

Optional

The index of the current matching random models rule.

swing_progress

Optional

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

time

Type: Integer

Values: From 0 to 27720

Optional

The total game time in ticks; not related to the daylight cycle.

Operators

Operators are symbols that operate on numbers. They are infix functions.

  • +: Add

  • -: Subtract

  • *: Multiply

  • /: Divide

  • %: Modulo

  • !: Negate

  • &&: Logical AND

  • ||: Logical OR

  • <: Less than

  • <=: Less than or equal to

  • >: Greater than

  • >=: Greater than or equalt o

  • ==: Equality

  • !=: Inequality

Functions

sin(degrees: number) number

Sine of degrees.

>>> sin(35)
0.5735764364...
cos(degrees: number) number

Cosine of degrees.

>>> cos(100)
−0.1736481777...
tan(degrees: number) number

Tangent of degrees.

>>> tan(50)
1.191753595...
asin(x: number) number

Arcsine of x.

>>> asin(0.5735)
34.99465382...
acos(x: number) number

Arccosine of x.

>>> acos(-0.1736)
99.99719705...
atan(x: number) number

Arctangent of x.

>>> atan(1.1917)
49.99873126...
atan2(y: number, x: number) number

Two-argument arctangent of y and x.

>>> atan2(1, 3)
0.3217505544...
torad(degrees: number) number

Convert degrees to radians.

>>> torad(90)
1.570796327...
todeg(radians: number) number

Convert radians to degrees.

>>> todeg(1.5707)
89.99448088...
min(*items: number) number

The minimum of all given parameters.

>>> min(1, 4, 3, -5, -100)
-100
max(*items: number) number

The maximum of all given parameters.

>>> max(1, 4, 3, -5, -100)
4
clamp(x: number, min: number, max: number) number

The clamped value of x, guaranteed to be between min and max values:

  • If x > max, return x = max

  • If x < min, return x = min

>>> clamp(6, 1, 5)
5
>>> clamp(4, -8, 10)
4
abs(x: number) number

Absolute value of x.

  • If x < 0, return -x

  • If x >= 0, return `x`

>>> abs(-25.3)
25.3
floor(x: number) int

The floor of x.

>>> floor(2.4)
2
>>> floor(-2.4)
-3
ceil(x: number) int

The ceiling of x.

>>> ceil(2.1)
3
>>> ceil(-2.1)
2
exp(x: number) number

Euler's constant (e) raised to the power of x; \(e^x\)

>>> exp(4)
54.59815003...
frac(x: number) number

The fractional part (decimal) of x.

>>> frac(3.25)
0.25
>>> frac(-9.99992)
-0.99992
log(x: number) number

The logarithm of x, base 10.

>>> log(50)
3.912023005428146...
pow(base: number, power: number) number

Raise base to the power power; \(\mathit{base}^{\mathit{power}}\)

>>> pow(4, 5)
1024
>>> pow(-53, 3)
-148877
random(seed: number | None) number

A random number between 0.0 and 1.0. Parameter seed is optional and if given, randomizer is seeded with this value.

>>> random()
0.53235...
round(x: number) int

Rounding of x.

>>> round(5.4)
5
signum(x: number) int

The sign of x.

>>> signum(0)
0
>>> signum(-5253)
-1
>>> signum(5)
1
sqrt(x: number) number

The square root of positive x.

>>> sqrt(25)
5
>>> sqrt(27.5)
5.244044241...
fmod(x: number, y: number) number

Java's floorMod function of x and y. See linked Javadoc.

>>> fmod(4, 3)
1
>>> fmod(4, -3)
-2
>>> fmod(-4, -3)
-1
lerp(k: number, x: number, y: number) number

The linear interpolation of k between the interval [x, y]:

\(\left(\left(1 - k\right) \times x\right) + \left(k \times y\right)\)

if(cond: predicate, val: number, **conds: predicate, val_else: number) number

Select a value based on one of more conditions (predicates):

  • Return val if cond is true, else...

  • Return val2 if cond2 is true, else...

  • exhaust all condition pairs, else...

  • Return val_else if all of cond are false

Additional isolated conditions may be specified as additional two-pair of predicate, value. The first condition pair that is true is returned. If no pairs are true, val_else is returned.

A predicate is an expression that evaluates to a boolean (true or false). For example, limb_speed > 0.7.

>>> if(limb_speed > 0.7, 5, 0)
5
>>> if(true, 9, false, 3, 0)
9
>>> if(false, 0, true, 1, 2)
1
>>> if(false, 0, false, 1, false, 2, 3)
3
ifb(cond: predicate, val: boolean, **conds: predicate, val_else: boolean) boolean

Select a boolean value based on one of more conditions (predicates):

  • Return val if cond is true, else...

  • Return val2 if cond2 is true, else...

  • exhaust all condition pairs, else...

  • Return val_else if all of cond are false

Additional isolated conditions may be specified as additional two-pair of predicate, value. The first condition pair that is true is returned. If no pairs are true, val_else is returned.

A predicate is an expression that evaluates to a boolean (true or false). For example, limb_speed > 0.7.

>>> ifb(limb_speed > 0.7, true, false)
true
>>> ifb(true, false, true)
false
>>> ifb(1 > 0, false, true)
false
>>> ifb(0 == -1, false, 1 == -1, true, -1 == -1, true, false)
true
print(id: number, n: int, x: number) None:

Print number x to the output log every n -th frame, qualified with id.

printb(id: int, n: int, x: boolean) None:

Print boolean x to the output log every n -th frame, qualified with id.

between(x: number, min: number, max: number) boolean

Is x between min and max; \(\mathit{min} <= x <= \mathit{max}\)

>>> between(4, 0, 10)
true
>>> between(5, 1, 3)
false
equals(x: number, y: number, margin: number) boolean

Is the difference of x and y within error margin margin; \(|x - y| < \mathit{margin}\)

>>> equals(3, 6, 4)
true
>>> equals(0.05, 10, 10)
true
>>> equals(0.83, 0.06, 0.2)
false
in(x: number, *val: number) boolean

Is x equal to any of val?

>>> in(1, 2, 3, 4, 1)
true
>>> in(7, 6, 4, 0)
false

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)"

Warning

These resources are not related to OptiDocs, but may provide some help.


Assumes the latest OptiFine & Minecraft versions.
Updated to commit 83d482c3.