Custom Model Render (Power Type)
Changes how a player or any mob looks. Texture mode re-skins the entity's own model with a texture, and geometry mode draws a separate 3D model made in Blockbench that follows the entity's pose. Geometry mode is a feature renderer defined in JSON, so it needs no Java and no extra mods.
Type ID: apoli:custom_model_render
apoli:entity_texture_overlay isn't a registered type. Rename it to apoli:custom_model_render in any JSON that still uses it. The mode: texture fields below are direct equivalents of its fields, except that there is no layers field, so use one power per layer.
This is a client-side rendering power. Both modes work on every living entity that holds the power (players, mobs, armor stands and the minions summoned by apoli:summon_minion), and geometry also renders on projectiles. See On mobs for how textures and models map onto a mob. Textures and models must be in every viewer's resource pack.
Fields for both modes
| Field | Type | Default | Description |
|---|---|---|---|
mode | String | texture | texture re-skins the vanilla model, and geometry draws a custom Blockbench model. |
render_type | Render Type | translucent | The draw style: translucent, cutout, cutout_no_cull, solid, emissive (or glow), eyes or energy_swirl. |
scroll_speed | Float | 0.0 | With render_type: energy_swirl, how fast the texture scrolls, in texture heights per tick. 0.0 holds the texture still. See Energy swirl. |
body_parts | Body Part or Array of Body Part | whole model | In texture mode, limits the overlay to these parts: limbs, layers or groups such as arms and upper on a humanoid model, main, flat2 and flat3 on a minion, and on any other mob the names of its own model parts, such as a spider's head or right_front_leg. In geometry mode, renders only the bones with these names, which can be any bone name in your own model. A group name shows every bone bound to its limbs. Every bone you don't name is hidden, and hiding a bone hides everything nested under it. |
red, green, blue, alpha | Float | 1.0 | Colour and opacity multipliers, from 0.0 to 1.0. |
scale | Float | 1.0 | Scales the drawn geometry outward from the model origin. Above 1.0 it gives an aura or shell effect. |
hidden_slots | Array of Equipment Slot | none | Hides this render whenever any listed slot is occupied. ["head"] hides a custom hat model when a real helmet is worn. |
hide_cape | Boolean | false | Hides the holder's cape while the power is active. |
show_first_person | Boolean | false | Also draws on the holder's own arm in first person. In texture mode that is the overlay texture. In geometry mode it is the model's right_arm and left_arm bones (and everything nested under them), posed onto the vanilla first person arm. |
Texture-mode fields (mode: texture)
| Field | Type | Default | Description |
|---|---|---|---|
wide_texture_location | Identifier or keyword | the value of texture_location | Texture for the wide (Steve) model, and for every entity that isn't a player. Also takes a live keyword. |
slim_texture_location | Identifier or keyword | the wide texture | Texture for the slim (Alex) model. |
texture_location | Identifier or keyword | optional | One texture for every model, used when wide_texture_location is left out. One of the two is required. |
render_as_overlay | Boolean | false | false replaces the skin, or a mob's own texture. true draws the texture as an overlay layer that uses render_type, body_parts and the tint. |
Geometry-mode fields (mode: geometry)
| Field | Type | Default | Description |
|---|---|---|---|
model_location | Identifier | required | The Blockbench model. mymod:cape resolves to assets/mymod/geo/cape.geo.json (the standard Blockbench and GeckoLib folder), and assets/mymod/models/apoli/cape.geo.json also works. Export it from Blockbench as Bedrock geometry (.geo.json). |
texture_location | Identifier or keyword | required | The texture that UV-maps onto the model, such as mymod:textures/entity/cape.png. Also takes a live keyword. |
render_as_overlay | Boolean | false | On anything other than a player, false replaces the entity's own model with yours and true draws yours on top of it. Ignored on players, where geometry is always drawn over the player model. |
animations | Model Animation or Array of them | none | Bedrock animations to play on the model. The first entry whose condition passes is the one that plays. |
bind_body_parts | Boolean | true | Whether bones named after body parts follow the holder's limbs. false leaves the model to its own animations (see Rigs that animate themselves). |
offset | Vector or Array of 3 Floats | [0, 0, 0] | Moves the whole model, in blocks, before scale is applied. The axes turn with the holder, like local Space: x to its left, y up and z forward, which on a projectile is along the flight path. The first person arm isn't moved. |
Live textures
Every texture field here takes a keyword instead of a texture ID, resolved every frame against the entity being drawn:
| Keyword | Resolves to |
|---|---|
player | That entity's skin file, if it is a player. |
player_cape | That entity's cape. |
entity | Whatever texture that entity's renderer normally uses, so a model can be skinned with the mob it is attached to. |
{
"type": "apoli:custom_model_render",
"mode": "geometry",
"model_location": "example:wings",
"texture_location": "player"
}That draws the wings model UV-mapped onto the wearer's own skin, so it takes each player's colours without a texture per player.
Keywords resolve on the client, from the entity being rendered. held_item and offhand_item are item models, not textures, so they do nothing here. Use them on apoli:overlay or apoli:fire_projectile instead.
Geometry on the player
When resources load or reload, the log prints Loaded N custom model(s) for custom_model_render. If your model isn't drawing, check N and confirm the file sits at one of the two paths above with the .geo.json extension.
The model is placed exactly where Blockbench shows it, relative to the player's feet. A bone at Bedrock pivot [0, 0, 0] sits on the ground, and one at [0, 24, 0] sits at the head pivot. Per-cube rotations, bone rotations, bone parenting, inflate and mirror all carry over unchanged, and Box UV is packed the way Blockbench's Bedrock preview packs it. That includes cubes with fractional sizes, where Blockbench rounds each dimension down before laying the six faces out.
The UV grid is sized by the texture_width and texture_height in the .geo.json, which is your Blockbench project's UV size, not the pixel size of the PNG. A 64×64 PNG on a 32×32 UV project is fine (it draws at 2×), but a PNG painted for a 64×64 UV grid on a 32×32 project is half a texture out. Set the project's UV size before you paint.
Bones named head, body, right_arm, left_arm, right_leg, left_leg and hat pick up the matching vanilla body part's animation, so the model bends with the arms and turns with the head. Any other bone (a cape, a backpack, a tail) rides along with its parent bone.
Put your body part bones on the vanilla pivots. This rule decides whether a model animates convincingly, and it is the first thing to check if a limb seems to swing in the wrong direction. Apoli rotates each bone about its own pivot, by the angle the vanilla part is turning through. The bone gets the player's motion but swings around the point you chose in Blockbench, and if that point isn't where the player's joint is, the limb sweeps through an arc the player's limb never takes. Name the bone correctly and place its pivot here:
| Bone | Bedrock pivot |
|---|---|
head, hat, body | [0, 24, 0] |
right_arm / left_arm | [-5, 22, 0] / [5, 22, 0] |
right_leg / left_leg | [-1.9, 12, 0] / [1.9, 12, 0] |
Only the pivot has to match, and the cubes hanging off the bone can be any shape or size. The easiest way to get this right is to start from the vanilla player template in Blockbench and rebuild the geometry on top of it, leaving the bones where they are.
Bones placed off the pivots on purpose still work, and suit anything that isn't a limb. An aura ring pivoted at the feet, or a floating orb pivoted at the head, orbits the player instead of tracking a joint.
A bone follows the body part as it is posed, so a resource pack that replaces the player model through Entity Model Features (Fresh Animations and the like) carries the model with it.
Names are matched loosely, so you rarely have to rename anything Blockbench gave you. Case, spaces, _ and - are ignored (Head, RightArm and right arm all work), and these spellings bind to the same body part:
| Body part | Also accepted |
|---|---|
head | none |
hat | headwear, hat_layer, head_layer |
body | torso, waist, jacket, body_layer |
right_arm / left_arm | arm_right / arm_left, right_sleeve / left_sleeve, right_arm_layer / left_arm_layer |
right_leg / left_leg | leg_right / leg_left, right_pants / left_pants, right_leg_layer / left_leg_layer |
Every bone whose name matches a body part binds to it, not only the first one. A rig with right_arm and right_sleeve, or head and hat_layer, or body and jacket, animates all of them together, and a second layer bone stays on its limb through poses that move a part as well as rotating it: sneaking, blocking with a shield, winding up a trident.
Nesting is fine. A body part bone animates from the player whether it sits at the top level, inside a wrapper group like bb_main, or under your Body bone. It always swings the way the player's own limb swings, and never twice over.
A body part bone always animates in the player's frame, so it is lifted out of any parent group when the model loads. If that parent group has a rotation of its own in Blockbench, the rotation stops applying to that bone, and the load log names the bone and the group when this happens. Rotate the bone itself, or give the group a name that isn't a body part so it stays a plain group.
Animations
Blockbench exports animations as a .animation.json next to your model. Drop it at assets/<namespace>/animations/<path>.animation.json and name it in animations:
{
"type": "apoli:custom_model_render",
"mode": "geometry",
"model_location": "example:wings",
"texture_location": "example:textures/entity/wings.png",
"render_type": "cutout_no_cull",
"animations": {
"animation": "example:wings",
"name": "animation.wings.idle"
}
}animations also takes an array and works like hud_render does: the entries are read top to bottom and the first one whose condition passes is the one that plays. That drives a model from the holder's state without any Java:
"animations": [
{
"animation": "example:wings",
"name": "animation.wings.flap",
"condition": {
"type": "apoli:fall_flying"
}
},
{
"animation": "example:wings",
"name": "animation.wings.idle"
}
]Playback time restarts whenever the selected entry changes, so an animation that doesn't loop replays each time its condition turns back on. speed scales the playback rate and loop overrides the file's own loop flag. See Model Animation for the full field list.
Conditions here run on the client, every frame, against the state the client knows. Anything the server never sends, such as an apoli:command, apoli:predicate, apoli:scoreboard, apoli:advancement or apoli:stat condition, can't be answered in a render layer, so the entry is skipped with a warning in the log. Everything the client already tracks works: pose, sneaking, sprinting, fall flying, held items, equipment, and apoli:resource on any power holder, including a summoned minion.
The animation is applied on top of the pose the player's body already gives the model, so a bone named right_arm gets the player's arm swing and your keyframes added together. Bones that aren't body parts get the keyframes alone. Position keyframes are in Bedrock units and rotation keyframes in degrees, exactly as Blockbench writes them.
When resources load or reload, the log prints Loaded N custom model animation(s) from M file(s). Check it if nothing moves.
Only the position, rotation and scale channels are read, with linear interpolation between keyframes. pre and post values on a keyframe are honoured, which is how Blockbench's stepped keyframes come across. Both keyframe spellings work: a bare [x, y, z], which Blockbench's Bedrock exporter writes, and the {"vector": [x, y, z]} wrapper the GeckoLib plugin writes. Keyframe easing is honoured too. lerp_mode: "catmullrom" (Blockbench's smooth keyframes) interpolates along a Catmull-Rom spline, and GeckoLib's easing and easingArgs (the full easeInOutSine, easeOutBack, easeOutElastic, easeOutBounce family) shape the curve between keyframes. An easing sits on the keyframe you are moving toward. Molang expressions aren't evaluated, so a keyframe whose value is a formula reads as 0. Sound and particle effect keyframes are ignored.
Rigs that animate themselves
Some models aren't meant to move like a player at all: a creature with its own walk cycle, a floating spirit, a mech. Set bind_body_parts to false and the model is drawn exactly as its animations pose it:
- Bones named
head,right_armand the rest keep your hierarchy. They aren't lifted out of their groups, and they don't pick up the player's limb swing, head turn or sneak. Only your keyframes move them. - The model still turns with the holder's body and is still placed relative to their feet.
body_partsstill hides and shows bones by name.- With
show_first_person, theright_armandleft_armbones (and everything under them) are drawn where the vanilla first person arm is, with the rotation your animation gives them, so the held item and the hand line up.
Drive the model from the holder's state with conditional animations entries, and hide the player underneath with apoli:modify_model_parts:
{
"type": "apoli:custom_model_render",
"mode": "geometry",
"model_location": "example:golem",
"texture_location": "example:textures/entity/golem.png",
"bind_body_parts": false,
"show_first_person": true,
"hide_cape": true,
"animations": [
{
"animation": "example:golem",
"name": "animation.golem.walk",
"condition": {
"type": "apoli:moving"
}
},
{
"animation": "example:golem",
"name": "animation.golem.idle"
}
]
}Geometry mode on minions
A power on a minion is held by the minion. Every condition in it, including the ones on animations entries, is tested against the minion and never its summoner, so apoli:sneaking asks whether the minion is sneaking. Wrap it in apoli:owner to ask about the player who summoned it.
Give the power to an apoli:summon_minion minion through that action's powers list and the minion is drawn as your Blockbench model instead of the default orb. That gives a summon any shape you like from a data pack alone.
The minion skeleton is main (the root bone, Bedrock pivot [0, 4, 0]) with flat2 and flat3 under it. A bone named main turns to face wherever the minion is looking, which is the minion's only vanilla animation. flat2 and flat3 hold fixed poses, so a bone named after them inherits nothing. Every other bone keeps the pose you gave it in Blockbench and rides along with the root.
The minion's texture field still applies to the minion's own model, so it only matters when render_as_overlay is true. Your geometry always uses texture_location. scale on the summon still applies, and the custom model is scaled with the minion.
{
"type": "apoli:summon_minion",
"follow_owner": true,
"follow_offset": [
0,
1,
-1
],
"max_life_ticks": 0,
"powers": [
"example:minion_wisp_model"
]
}{
"type": "apoli:custom_model_render",
"mode": "geometry",
"model_location": "example:wisp",
"texture_location": "example:textures/entity/wisp.png",
"render_type": "cutout_no_cull"
}custom_model_render is a client-side render power, so the minion's behaviour stays the same and only its appearance changes.
Energy swirl
render_type: energy_swirl draws the texture the way a charged creeper's aura and a wither's armour are drawn: additively, so it glows over the model, with the texture sliding across it. The sideways drift is fixed. scroll_speed adds a steady scroll along the model on top, and 0.0 holds the texture completely still. Pair it with a scale above 1.0 to lift the swirl off the body into a shell.
{
"type": "apoli:custom_model_render",
"texture_location": "minecraft:textures/entity/creeper/creeper_armor.png",
"render_as_overlay": true,
"render_type": "energy_swirl",
"scroll_speed": 0.01,
"scale": 1.1,
"red": 0.5,
"green": 0.5,
"blue": 0.5
}This puts a charged creeper aura on whoever holds the power, whether a player, a zombie or anything else alive.
The type ID apoli:energy_swirl loads as this power with the swirl already set up. It reads texture_location, size (as scale) and speed (as scroll_speed), and fills in whatever is left out with render_as_overlay: true, render_type: energy_swirl, the wither's armour texture as texture_location, scroll_speed: 0.01 and a 0.5 grey tint:
{
"type": "apoli:energy_swirl",
"texture_location": "example:textures/entity/rewind.png",
"size": 1.05,
"speed": 0.02
}Behaviour and limits
- Several active
custom_model_renderpowers stack, and each is drawn. On a minion, one geometry power that isn't an overlay is enough to hide the base model, and the rest still draw. - Conditions on the power switch the whole render on and off as they change. Combine them with
hidden_slotsto hide it based on equipment. - Geometry mode draws in first person with
show_first_person: true, but only the arm bones, because first person only draws an arm. A bound arm bone is posed onto the vanilla arm, and an unbound one is drawn at it with its animated rotation. Geometry does draw in full wherever the game renders a whole player body, including the inventory preview and other entity preview screens, whatever camera mode you are in, and when your body is visible in first person (while sleeping, for instance). - The first person arm is drawn from the same bones as the third person model, so the vanilla arm underneath is still there. Hide it with apoli:modify_model_parts if your model should replace it instead of sitting over it.
- If
model_locationfails to load, the minion falls back to its normal model instead of turning invisible. - The Bedrock parser supports box UV and per-face UV (including both in one model, cube by cube), per-cube and per-bone
pivotandrotation,inflate,mirrorand bone parenting. Onlyminecraft:geometry[0]is read.uv_rotation(Bedrock format 1.21.0 and later) isn't supported, so a model using it logs a warning and draws that face unrotated. - A cube with a zero-size axis is a flat plane in Bedrock too, and both of its faces land on the same plane. Blockbench shows this as z-fighting and so does the game. Give the cube a small
inflateor a thickness above zero if it flickers.
Examples
Replace the skin in third person (the replace mode of entity_texture_overlay):
{
"type": "apoli:custom_model_render",
"mode": "texture",
"wide_texture_location": "example:textures/entity/husk_wide.png",
"slim_texture_location": "example:textures/entity/husk_slim.png"
}Glowing eyes on the head only, over the normal skin:
{
"type": "apoli:custom_model_render",
"mode": "texture",
"render_as_overlay": true,
"render_type": "eyes",
"body_parts": "head",
"wide_texture_location": "example:textures/entity/glowing_eyes.png"
}A custom cape model that follows the body, hidden when a chestplate is worn:
{
"type": "apoli:custom_model_render",
"mode": "geometry",
"model_location": "example:cape",
"texture_location": "example:textures/entity/cape.png",
"render_type": "cutout_no_cull",
"hidden_slots": [
"chest"
]
}Clawed gauntlets you can also see on your own hands:
{
"type": "apoli:custom_model_render",
"mode": "geometry",
"model_location": "example:gauntlets",
"texture_location": "example:textures/entity/gauntlets.png",
"render_type": "cutout_no_cull",
"show_first_person": true
}The model's right_arm and left_arm bones sit on the vanilla arm pivots, so they follow the arm in third person and are drawn onto the first person hand as well.
On mobs
Grant the power to a mob with apoli:grant_power, the /apoli:power grant command or a global power set, and it renders on that mob. That is how you make mob variants such as a red zombie, a crystal spider or a ghast with a crown.
Texture mode paints onto the mob's own model, through that model's own UV layout. A texture made for it is the mob's vanilla texture repainted: open minecraft:textures/entity/zombie/zombie.png (or the spider's, or the ghast's) from the vanilla resources, paint over it, and point texture_location at your copy. It fits whatever shape the model has, at any resolution, as long as it keeps the layout, so a 128×128 repaint of a 64×64 texture works.
render_as_overlay: falseswaps the mob's texture outright. The mob's own extra layers that reuse its texture, such as a slime's translucent outer shell, take the new texture too.render_as_overlay: truedraws your texture over the mob's, withrender_type, the tint andscale, for glowing markings, an energy shell or a coat of frost.
{
"type": "apoli:custom_model_render",
"texture_location": "example:textures/entity/zombie/infernal_zombie.png"
}Geometry mode draws your Blockbench model on the mob. Bones named after the mob's own model parts follow those parts, the same way head and right_arm follow a player:
| Mob | Some of its part names |
|---|---|
| Zombie, skeleton, enderman and other humanoids | head, hat, body, right_arm, left_arm, right_leg, left_leg |
| Spider | head, body0, body1, right_front_leg, left_hind_leg, right_middle_front_leg and the other legs |
| Creeper | head, body, right_front_leg, left_front_leg, right_hind_leg, left_hind_leg |
| Ghast | body, tentacle0 to tentacle8 |
| Slime | cube, right_eye, left_eye, mouth |
| Vex | head, body, and under the body right_arm, left_arm, right_wing, left_wing |
The names are the ones Minecraft's own model code gives the parts, with case, spaces, _ and - ignored. Every bone follows its part relative to where that part rests, so put each bone's pivot where the vanilla part's pivot is. Nest the bones the way the mob's parts are nested (a vex's right_arm under its body), so a parent's movement reaches its children once and not twice. Bones that name no part ride along with their parent.
With render_as_overlay: false (the default), your model replaces the mob's and the vanilla model isn't drawn. The mob's other layers still are (held items, armour, a spider's glowing eyes), so hide the ones you don't want with apoli:prevent_feature_render. With render_as_overlay: true your model is drawn on top of the mob, which is how you give it a hat, horns or wings.
{
"type": "apoli:custom_model_render",
"mode": "geometry",
"model_location": "example:crystal_spider",
"texture_location": "example:textures/entity/crystal_spider.png",
"render_type": "cutout_no_cull"
}The power is drawn on the client of everyone who can see the mob, so its condition is evaluated there and can only use what clients know: the mob's pose, health, equipment, resources and other synced values.
On something other than a player or a mob
The power renders on whatever entity holds it, so granting it to a summon, a clone or a projectile puts the model on that entity instead of on you:
- A minion or a clone that holds the power renders with the geometry, with its humanoid bones driven by the entity's own pose.
- A projectile fired by
apoli:fire_projectilewith atexture_locationrenders the geometry instead of its flat texture, pointed along its flight path. Grant the power from that power'sprojectile_action. With Flywheel installed, the projectile is drawn with instancing (see Performance Mods).
Minions and projectiles draw the model the way bind_body_parts: false does, so bones named after player body parts stay where you nested them. On a minion, bind_body_parts: false also stops the main bone from turning with the minion's gaze. offset applies on both, which is the easy way to centre a model that was built off the origin.
hidden_slots only applies to entities that wear equipment, and is ignored on a projectile.
While disguised
An apoli:disguise_as disguise swaps out the whole player renderer, so the power has to be drawn onto the disguise instead. It is, whenever the disguise's model is humanoid: zombie, husk, drowned, skeleton, stray, wither skeleton, piglin and the rest of the two-arms-two-legs family. Texture overlays paint onto the disguise's limbs, and geometry bound to head, body, right_arm, left_arm, right_leg or left_leg tracks the disguise's live pose.
{
"type": "apoli:multiple",
"skeleton_body": {
"type": "apoli:action_on_callback",
"entity_action_added": {
"type": "apoli:disguise_as",
"entity_type": "minecraft:skeleton"
},
"entity_action_removed": {
"type": "apoli:remove_disguise"
}
},
"armor": {
"type": "apoli:custom_model_render",
"mode": "texture",
"render_as_overlay": true,
"wide_texture_location": "example:textures/models/armor/plate_wide.png",
"slim_texture_location": "example:textures/models/armor/plate_slim.png"
}
}The armour is painted over the skeleton. Which of the two textures is used still follows the player's own skin model and not the disguise's, so a slim-armed player gets the slim sheet.
A disguise whose model isn't humanoid, such as a creeper, a bee or a horse, has no limbs to bind to, and the power is skipped for as long as that disguise is up. A disguise as another player is already drawn by the player renderer, so the power works exactly as it does undisguised.