Schema - Gamepiaynmo/CustomModel GitHub Wiki

Overview

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.

The Main JSON

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": [ ... ]
}

Basic Informations

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

Original Player Bones

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.

First person rendering

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.

Skeleton

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.

Bounding Box and Eye Height

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]
	}
}

Hidden model parts

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.

Coordinates

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

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.

Boxes & Quads

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.

Particles

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.

Items

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.

Physics

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.

Animations

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)