-
Notifications
You must be signed in to change notification settings - Fork 29
Schema
The Custom Player Model (CPM) JSON allows you to use bones, boxes, planes, ribbons and particles with the functions of expressions to customize your model appearance. Some examples using existing features can be found here.
Each model pack must contain a json file named "model.json". A typical model json seems like:
{
"modelId": ...,
"modelName": ...,
"version": ..., // optional
"author": ..., // optional
"fpLeft": [ ... ], // optional
"fpRight": [ ... ], // optional
"skeleton": { ... }, // optional, animated
"eyeHeight": { ... }, // optional
"boundingBox": { ... }, // optional
"variables": { ... }, // optional, animated
"tickVars": { ... }, // optional, animated
"hide": [ ... ], // optional
"bones": [ ... ]
}
- modelId: Each model should have an UNIQUE ID, or they will be regard as the same model of different versions, so please avoid confliction when giving IDs. Model ID can only contain alphabets, numbers and underline (_) in lowercase.
- modelName: Model name is what the player see in game when they use the command /custommodel list. Model names can have confliction, and can comprise any characters.
- version: When loading different models with the same model id, the system will choose the one with the largest version (comparing strings under lexicographical order).
- author: Mark your name down to join the hall of fame.
Player models are constructed with bones, which have boxes attached to be rendered. The bones defined the motions of the model. Original player bones are the ones that constructed the vanilla player model, containing following ones:
- head
- body
- left_arm
- right_arm
- left_leg
- right_leg
With their second layers:
- head_overlay
- body_overlay
- left_arm_overlay
- right_arm_overlay
- left_leg_overlay
- right_leg_overlay
The motion of these bones are controlled by the game code, which the mod will not interfere to maintain compatibility. What you can do is to adjust their pivot to change the stature of your model, or hide them and make your new ones.
This mod allows you to chooses which bone to be rendered in first person view. You can add these bones to "fpLeft" or "fpRight", which depends on which is your main hand. Remember to only add bones which are the children of original arm bones ("left_arm" or "right_arm" and their overlays) and do NOT tag "physics" on them, unless you clearly know what your are trying to do.
The skeleton defines the pivot of original player bones, which can be used to change the model stature, like making yourself as tall as an enderman without complex modifications. You can refer to the enderman model as an example.
After changing the model skeleton, in most cases you would like to change the bounding box to fit your new stature. Now you can define them for different player poses:
- standing
- fall_flying
- sleeping
- swimming
- spin_attack
- sneaking
- dying
In most cases defining the bounding box and eye height of three poses (standing, sneaking and dying) is enough. So following is what the json should look like:
{
"eyeHeight": {
"standing": 1.215,
"dying": 1.215,
"sneaking": 0.9525
},
"boundingBox": {
"standing": [0.45, 1.35],
"dying": [0.45, 1.35],
"sneaking": [0.45, 1.125]
}
}
You can hide the original bones or features that would not like to see, e.g. when you want to change their position / appearance etc. Bones or features added to this list will not appear, but their transform matrix will still be calculated, so they are still capable to be parent bones. Each element of the array must be one of the original player bones or one of the original features:
- helmet_head
- helmet_head_overlay
- chestplate_body
- chestplate_left_arm
- chestplate_right_arm
- leggings_body
- leggings_left_leg
- leggings_right_leg
- boots_left_leg
- boots_right_leg
- held_item_left
- held_item_right
- cape
- head_wearing (skull, carved pumpkin)
- elytra
- shoulder_parrot_left
- shoulder_parrot_right
Or the combination of them:
-
head_all: head, head_overlay
-
body_all: body, body_overlay
-
left_arm_all: left_arm, left_arm_overlay
-
right_arm_all: right_arm, right_arm_overlay
-
arms_all: left_arm_all, right_arm_all
-
left_leg_all: left_leg, left_leg_overlay
-
right_leg_all: right_leg, right_leg_overlay
-
legs_all: left_leg_all, right_leg_all
-
model_all: head_all, body_all, arms_all, legs_all
-
helmet_all: helmet_head, helmet_head_overlay
-
chestplate_all: chestplate_body, chestplate_left_arm, chestplate_right_arm
-
leggings_all: leggings_body, leggings_left_leg, leggings_right_leg
-
boots_all: boots_left_leg, boots_right_leg
-
armor_body_all: chestplate_body, leggings_body
-
armor_arms_all: chestplate_left_arm, chestplate_right_arm
-
armor_left_leg_all: leggings_left_leg, boots_left_leg
-
armor_right_leg_all: leggings_right_leg, boots_right_leg
-
armor_legs_all: armor_left_leg_all, armor_right_leg_all
-
armor_all: helmet_all, armor_body_all, armor_arms_all, armor_legs_all
-
held_item_all: held_item_left, held_item_right
-
shoulder_parrot_all: shoulder_parrot_left, shoulder_parrot_right
-
feature_all: armor_all, held_item_all, shoulder_parrot_all, cape, head_wearing, elytra
After hiding them you can attach these features (not bones) to your custom bones to change their original position. The scale / visibility of the bone will also be applied to attached features.
Unlike the original model coordinate system, the one used by CPM is the same as the minecraft world coordinate system, and the player model is facing North, or -Z.
Direction | XYZ |
---|---|
East | +X |
West | -X |
North | -Z |
South | +Z |
Up | +Y |
Down | -Y |
Bones are the base of each model, they form the skeleton of player models, and define the movement of each part. A typical bone json seems like:
{
"id": ...,
"parent": ..., // optional
"texture": ..., // optional, animated
"textureSize": [ ..., ... ], // optional
"position": [ ..., ..., ... ], // optional, animated
"rotation": [ ..., ..., ... ], // optional, animated
"scale": [ ..., ..., ... ], // optional, animated
"visible": ..., // optional, animated
"emissive": ..., // optional, animated
"color": [ ..., ..., ... ], // optional, animated
"alpha": ..., // optional, animated
"physics": [ ... ], // optional
"attached": [ ... ], // optional
"boxes": [ ... ], // optional
"quads": [ ... ], // optional
"particles": [ ... ], // optional
"items": [ ... ] // optional
}
- id: Each bone must have a unique id, and cannot be same as original bones or features (body, head, etc).
- parent: Parent must be one of the original bones, or a custom bone that appears earlier. In other words, parents and children must appears sequentially. Child bones will follow the transformation (texture, position, rotation, scale and visibility) of parent bone, as if it is a part of its parent. If empty, "body" will be chosen as the parent.
- texture: The texture file name with prefix "tex." without path or suffix (e.g. "tex.hair" if your texture file is named "hair.png"). Texture files are stored in the same folder as "model.json". If empty, the original player skin texture will be used if its parent is an original bone, or it will use the same texture as its parent. There are 3 default textures: "tex.skin" for the original skin, "tex.cape" for the player cape if there has one and "tex.elytra" for the elytra texture. For players who do not have capes, use "tex.cape>0" to test whether current player has cape or not.
- textureSize: Will be automatically assigned as the dimension of the texture. In most cases it is the same as the texture resolution so you do not need to change them.
- position: Define the relative position of children bones. Notice that this does not change the position of current bone. Default value [0, 0, 0].
- rotation: Define the Euler angles (yaw, pitch, roll) of current bone in degrees. Default value [0, 0, 0].
- scale: Define the scale of current bone. Default value [1, 1, 1].
- visible: Weather the visible elements (boxes, quads, particles) are rendered or not. If not specified, it will have the same visibility as its parent.
- emissive: Weather this bone is emissive like the spider eyes or enderman eyes.
- color & alpha: Change the default color (255, 255, 255, 255) of the player model.
- physics, boxes, quads, particles & items: See following sections.
- attached: Once you want to change the position of an original feature (armor, held items, etc.), you can hide them and attach them to the new bone. Having the same feature attached to several bones will make the feature render several times.
Most entity models in minecraft are constructed of boxes. A typical box json seems like:
{
"textureOffset": [ ..., ... ], // optional
"coordinates": [ ... ], // optional
"sizeAdd": ... , // optional
"mirror": ... // optional
}
- textureOffset: The top-left corner of the box UV coordinates. Each box contains six faces, and is arranged in the same method as player skins. Default value [0, 0].
- coordinates: Define the offset and the size of the box. First three arguments are the offset, and the next three are the size. Default value [0, 0, 0, 0, 0, 0]. The ratio between box coordinates and actual world coordinates is 16:1.
- sizeAdd: Expand the box just like original skin overlays. Default 0.
- mirror: Some boxes use the same texture but the uv is flipped (like left arm and right arm). True to flip the uv in X axis.
Quads jsons are almost the same as boxes, except they are two-dimensional, so "coordinates" only contains 5 arguments.
Particle system is commonly used in modern 3D games. This mod provides various parameters for particle emitters for you to customize special effects.
{
"posRange": [ ..., ..., ... ], // optional, animated
"dirRange": ..., // optional, animated
"angle": [ ..., ... ], // optional, animated
"speed": [ ..., ... ], // optional, animated
"rotSpeed": [ ..., ... ], // optional, animated
"lifeSpan": [ ..., ... ], // optional, animated
"density": ..., // optional, animated
"animation": [ ..., ... ], // optional
"colorR": [ ..., ... ], // optional, animated
"colorG": [ ..., ... ], // optional, animated
"colorB": [ ..., ... ], // optional, animated
"colorA": [ ..., ... ], // optional, animated
"size": [ ..., ... ], // optional, animated
"gravity": ..., // optional, animated
"collide": ... // optional, animated
}
- posRange: The maximum initial offset for new particles relative to current bone position. Default value [0, 0, 0].
- dirRange: The maximum movement direction angle in degrees when spawning. When 0, new particles will move along the Z-axis of current bone, and will pick up one direction randomly in a circular cone when it is not 0. Default value 0.
- angle: Minimum and maximum value of initial rotation angle in degrees. Default value [0, 0].
- speed: Minimum and maximum value of movement speed in blocks / s. Default value [0, 0].
- rotSpeed: Minimum and maximum value of rotation speed in degrees / s. Default value [0, 0].
- lifeSpan: Minimum and maximum value of how long each particle exists in game ticks. Default value [1, 1].
- density: How many particles are spawned in 1 game tick. Default value 1.
- animation: Textures for particles can be split into sub-images to make animation. This parameter specifies how many sub-images are there each column / row. Default value [1, 1].
- color(R|G|B|A): Minimum and Maximum value of particle color. Default value [1, 1].
- size: Minimum and Maximum value of particle size. Default value [1, 1].
- gravity: If not 0, a constant force with direction -Y will be applied to all particles. Can be negative. Default value 0.
- collide: Whether particles will collide with blocks / entities. Default false.
You can attach item models to your player model.
{
"itemId": "item. ... ", // animated
"enchanted": ... // optional, animated
}
- itemId: The item that you want to attach. Use item constants like item.apple, item.diamond_sword etc.
- enchanted: Whether the item model is enchanted or not.
The "physics" entry in bone json specifies that current bone is a ribbon. A ribbon will not move along its parent like a rigid body, but will follow physical regulations like a spring, and can be used to simulate the movement of hair, scarf, etc. This entry contains 5 arguments:
- elasticity: [0, +inf), strength of the force applied when parent bone moves, pointing to movement direction.
- stiffness: [0, +inf), strength of the force applied constantly to recover the initial direction relative to the parent bone.
- damping1: [0, 1), multiplies to the velocity of current bone every game tick to simulate damping.
- damping2: [0, +inf), strength of the force applied in the opposite direction of the velocity of player entity.
- gravity: [0, +inf), strength of the gravity.
CPM enables players to use expressions to create their own animations. For example, the following bone:
{
"id": "bounce".
"position": [ 0, "sin(age)", 0 ],
"boxes": [ ... ]
}
It will bounce constantly in Y axis.
There are two kinds of expressions: float and boolean. They can be combined with various variables and functions to achieve complexed movements. Animation expressions can be applied to all arguments above that are marked "animated".
You can also add custom variables in the json root:
{
"variables": {
"bounce": "sin(age)",
"should_bounce": "is_sneaking"
}
}
Then you can use "var.bounce" and "var.should_bounce" to access them in following expressions. Note that these variables cannot reference themselves.
There is another kind of variables called tick variables - They are updated every tick, and the amazing part is, they can reference themselves! This means that they are actually turing complete - means that you can do anything you want with these variables. A typical tick variable looks like:
{
"tickVars": {
"selfAdd": ["float", 0, "if(tvl.selfAdd<10,tvl.selfAdd+1,0)]
}
}
"selfAdd" is the variable name, and "float" is variable type. Currently there are only two kinds of variables: floats and booleans. 0 is the initial value of this variable. This variable value will loop between 0 and 10. There are three ways to access these variables:
- tvl.xxx: Get the variable value of the last tick.
- tvc.xxx: Get the variable value of the current tick.
- tvp.xxx: Get a value between the former two, using the game partial variable for interpolation.
There is a tetris game written by these variables at here for reference.
Following are all the variables and functions that can be used to form expressions:
name | type | arguments | description |
---|---|---|---|
limb_swing | float variable | current limb swing angle | |
limb_speed | float variable | current limb swing amount | |
age | float variable | age of current player entity | |
head_yaw | float variable | yaw of head - yaw of body | |
head_pitch | float variable | pitch of head | |
scale | float variable | scale of player entity (0.0625) | |
health | float variable | player health | |
food_level | float variable | player food level | |
hurt_time | float variable | remaining time of being hurt (rendered red) | |
pos_x | float variable | position X | |
pos_y | float variable | position Y | |
pos_z | float variable | position Z | |
speed_x | float variable | velocity X | |
speed_y | float variable | velocity Y | |
speed_z | float variable | velocity Z | |
yaw | float variable | yaw of head | |
body_yaw | float variable | yaw of body | |
pitch | float variable | pitch of head | |
swing_progress | float variable | progress of hand swing [0, 1] (attack, place blocks) | |
is_alive | boolean variable | is player alive | |
is_burning | boolean variable | is player burning | |
is_glowing | boolean variable | is player glowing | |
is_hurt | boolean variable | is in hurt time (rendered red) | |
is_in_lava | boolean variable | is player in lava | |
is_in_water | boolean variable | is player in water | |
is_invisible | boolean variable | is player invisible | |
is_on_ground | boolean variable | is player on ground | |
is_riding | boolean variable | is player riding | |
is_sneaking | boolean variable | is player sneaking | |
is_sprinting | boolean variable | is player sprinting | |
is_wet | boolean variable | is player wet (rain, water) | |
is_first_person | boolean variable | is rendering first person view | |
pi | float constant | 3.1415926 ... | |
time | float variable | current day time [0, 24000) | |
+, -, *, /, % | float function | float x 2 | basic calculations |
sin, asin, cos, acos, tan, atan | float function | float | trigonometric function |
atan2 | float function | float x 2 | trigonometric function |
torad, todeg | float function | float | degree / radian conversion |
min, max | float function | float x n | minimum & maximum |
clamp | float function | float x 3 | max(f2, min(f1, f3)) |
abs, floor, ceil, exp, frac, log, pow, round, signum, sqrt | float function | float | basic math functions |
fmod | float function | float x 2 | floor mod |
random | float function | - | return a float in [0, 1) |
!, &&, || | boolean function | boolean x 2 | boolean operations |
>, >=, <, <=, ==, != | boolean function | float x 2 | float comparison |
equals | boolean function | float x 3 | is abs(f1 - f2) < f3 |
between | boolean function | float x 3 | is f1 between f2 and f3 |
in | boolean function | float x n | is f1 one of {f2, f3, ..., fn} |
if | float function | boolean, float, (boolean, float) x n, float | if (b1) f1 {else if (b2) f2} x n else fn |
(bone name).(t|r|s)(x|y|z) | float variable | get the relative position, rotation and scale of specified bone. e.g. head.rx, body.tz, etc. | |
tex.(texture name) | float variable | get the texture with specified name. | |
inv.(mainhand|offhand| helmet|chestplate|leggings| boots|main(0 - 35)) | float variable | access the inventory of current player. e.g. inv.main4 means the fifth slot of the player inventory. | |
item.(item id) | float variable | get the id of specified item. e.g. use inv.mainhand==item.apple to check if the player is holding an apple. | |
pose.(pose id) | float constant | entity pose index | |
current_pose | float variable | get the current pose | |
effect.(effect id) | float variable | get the current effect level (-1 for no effect) |