Repository navigation
Models Improvements
This section contains extra customization features for block models, blockstates and item models which expand on existing vanilla concepts.
As of now these include
- Improved BlockState and Block Models
- Expression-Driven Block Models
- Separate Transforms Model on Fabric
Polytone allows you to use z rotation in blockstate variants as well as unrestricting their values to be any angle.
The same is true for Block Model cubes which can also have any angle instead of just multiples of 22.5 degrees. They can also use the x y z rotation format instead of the axis one, allowing for arbitrary rotations.
Additionally, Polytone adds the ability to change the model offset in your blockstate model files. These only work for the variants type.
The names of the fields are xoffset, yoffset and zoffset.
Here's an example blockstate file:
{
"variants": {
"": {
"model": "minecraft:block/poppy",
"x": 90,
"z": 13.1,
"xoffset": 0.2,
"zoffset": -0.2
}
}
}Note
This feature mainly targets 1.21.11+, which has the blockstate model type system it is built on. On 1.21.1 it is experimental: Polytone hooks the variant parser itself to fake the missing model type. It works, but use it with care, and expect possible issues with other mods that touch block models or block rendering.
Polytone adds a custom blockstate model type, polytone:expression, that chooses which model to render per block position based on Polytone scripting expressions. This lets a single block display different models depending on biome, temperature, position, blockstate properties and so on.
Use it as a variant model entry in a blockstate file. On NeoForge the type field is type; on Fabric use fabric:type. Please add both to support both loaders.
The first case whose when expression evaluates to a non-zero (truthy) value wins. If none match, the fallback is used. Each model is a normal vanilla model reference, so it can be a single model or a weighted random list: you get context routing on the outside and vanilla per-position random variety on the inside.
An optional selector expression is evaluated once per position and its value is bound to the variable v, so cases that all key off the same quantity can be written compactly. Without a selector, v is 0 and each when is a standalone expression.
{
"variants": {
"": {
"type": "polytone:expression",
"fabric:type": "polytone:expression",
"selector": "b.temperature()",
"cases": [
{ "when": "v > 0.8", "model": { "model": "minecraft:block/red_sand" } },
{ "when": "v > 0.3", "model": [ { "model": "minecraft:block/sand" }, { "model": "minecraft:block/suspicious_sand" } ] }
],
"fallback": { "model": "minecraft:block/sand" }
}
}
}Example 2 with no selector
{
"variants": {
"": {
"type": "polytone:expression",
"fabric:type": "polytone:expression",
"cases": [
{ "when": "b.gemperature() > 0.8", "model": { "model": "minecraft:block/red_sand" } },
{ "when": "b.biome == minecraft:forest'", "model": [ { "model": "minecraft:block/sand" }, { "model": "minecraft:block/suspicious_sand" } ] }
],
"fallback": { "model": "minecraft:block/sand" }
}
}
}Expressions here have access to the standard block-context variables (biome, position, blockstate properties, etc.), same as colormaps. See the Scripting Expressions page for the full list.
With Polytone version 1.17.12 you can use Forge's 'separate_transforms' method in resource packs on Fabric and Quilt.
On Forge/NeoForge you are able to easily specify an in-hand model and a GUI model for your item/any item, this method does not exist on Fabric/Quilt. This is where Polytone comes into play, Polytone can read and use the model files for Forge and implement the custom models on Fabric/Quilt.
To start you will need 3 files in assets/[your namespace]/models/item:
First you need the GUI file to specify what sprite to use. This file needs to have a different name than the original model file of the item you are trying to overwrite, you can just append _gui to the filename. In this case we are using Supplementaries' flute_gui.json.
{
"parent": "item/generated",
"textures": {
"layer0": "supplementaries:item/flute"
}
}The second file is the in-hand model. This file also needs a different name, just append _in_hand to the filename. This file is called flute_in_hand.json.
{
"textures": {
"1": "supplementaries:item/flute_model",
"particles": "supplementaries:item/flute",
"particle": "supplementaries:item/flute"
},
"elements": [
{
"from": [7, 0, 7],
"to": [9, 14, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8.5, 8]},
"faces": {
"north": {"uv": [2, 2, 4, 16], "texture": "#1"},
"east": {"uv": [0, 2, 2, 16], "texture": "#1"},
"south": {"uv": [6, 2, 8, 16], "texture": "#1"},
"west": {"uv": [4, 2, 6, 16], "texture": "#1"},
"up": {"uv": [2, 0, 4, 2], "texture": "#1"},
"down": {"uv": [4, 0, 6, 2], "texture": "#1"}
}
}
],
"gui_light": "front",
"display": {
"thirdperson_righthand": {
"translation": [0, -1, 0]
},
"thirdperson_lefthand": {
"translation": [0, -1, 0]
},
"firstperson_lefthand": {
"rotation": [0, 90, 0]
},
"ground": {
"rotation": [90, 0, 0]
},
"gui": {
"rotation": [-67.5, 0, 45],
"scale": [1.5, 1.5, 1.5]
},
"head": {
"rotation": [90, 0, 90],
"translation": [5.5, 0, 0],
"scale": [1.6, 1.6, 1.6]
},
"fixed": {
"translation": [0, 0, -1.5],
"scale": [1.5, 1.5, 1.5]
}
}
}And lastly you will need the 'separate_transforms' model, this model has to be named the exact filename of the model you are trying to overwrite. This file is logically called flute.json then.
Note
On NeoForge the loader id is neoforge:separate_transforms instead, to match what's there.
{
"loader": "forge:separate_transforms",
"base": {
"parent": "supplementaries:item/flute_in_hand"
},
"perspectives": {
"gui": {
"parent": "supplementaries:item/flute_gui"
},
"ground": {
"parent": "supplementaries:item/flute_gui"
},
"fixed": {
"parent": "supplementaries:item/flute_gui"
}
},
"textures": {
"layer0": "supplementaries:item/flute"
},
"gui_light": "front",
"display": {
"thirdperson_righthand": {
"translation": [0, -1, 0],
"scale": [1, 1, 1]
},
"thirdperson_lefthand": {
"translation": [0, -1, 0],
"scale": [1, 1, 1]
},
"ground": {
"rotation": [0, 0, 0],
"translation": [0, 2, 0],
"scale": [0.5, 0.5, 0.5]
},
"head": {
"rotation": [90, 0, 90],
"translation": [5.5, 0, 0],
"scale": [1.6, 1.6, 1.6]
},
"firstperson_lefthand": {
"rotation": [0, 90, 0]
}
}
}You have multiple options in your 'separate_transforms' for which model is displayed when and how.
"loader": "forge:separate_transforms"
This line needs to be added to the top of each 'separate_transforms' file to have them working.
"perspectives": {
"gui": {
"parent": "[your namespace]:models/item_gui" // This has to point to your 'item_gui.json'
},
"ground": {
"parent": "[your namespace]:models/item_gui" // This can point to either of your models, you normally want to use 'item_gui.json'
},
"fixed": {
"parent": "[your namespace]:models/item_gui" // This option decides which model to use in item frames, you also want to use 'item_gui.json'
}
}You will need to specify the texture paths of the texture for the GUI model in your 'separate_transforms' model:
"textures": {
"layer0": "supplementaries:item/flute"
}For your item model to display correctly, you will need to define translations and GUI lighting:
"gui_light": "front", // defines the lighting in the GUI
"display": {} // here you can specify the translation of the 3D model, you can just paste the translations from your 'item_in_hand.json' model