This is an old revision of the document!
Table of Contents
Model Generation
We know that almost every item needs a corresponding item baked model (or item model for short), and an item models definition since 1.21.4. Each block needs a block baked model (or block model for short) and a block states definition, and a corresponding item baked model or item models definition. A simble block requires so much complicated JSON files! Obviously, it's too hard to build each file manually. Therefore, we use data generator to generate models for blocks and items.
In data generator, models and definitions are generated together. Usually, when generation a model, a model id is returned, which will be used in the block states definition or item models definition. We will go into that from simple ones to details.
Note: In the tutorials created previously, there may be some JSON files already created in the
resources
folder. When the data generator generates a JSON file with a same filename, the file will conflict with our manually-created JSONs. Therefore, before running data generators, please delete them.
Note: Since 1.21.4, data generation are divided into client and server. In vanilla, classes related to models will be annotated
@Environment(EnvType.CLIENT)
which will be available only in client environment. If you find errors due to server environments when you run data generator, please refer to datagen_setup, and modify thefabricApi
block in thebuild.gradle
to enable client environment, and regenerate IDE's run configs.
Preparation
First, create a class that extends FabricModelProvider
and register it in the datagen entrypoint:
- TutorialModelGenerator.java
public static class TutorialModelGenerator extends FabricModelProvider { public TutorialModelGenerator(FabricDataOutput output) { super(output); } @Override public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { // ... } @Override public void generateItemModels(ItemModelGenerator itemModelGenerator) { // ... } }
- ExampleModDataGenerator.java
public class ExampleModDataGenerator implements DataGeneratorEntrypoint { @Override public void onInitializeDataGenerator(FabricDataGenerator generator) { // ... pack.addProvider(TutorialModelGenerator::new); } }
Simple block model
We have already created an example block in the blocks tutorial. We just use several short codes to create block states definition and block model:
- TutorialModelGenerator.java
@Override public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { blockStateModelGenerator.registerSimpleCubeAll(TutorialBlocks.EXAMPLE_BLOCK); }
The line of code creates a simplest block model, which is a full block, in which all sides use the texture identical to its id: tutorial:block/example_block
. Note that the texture is a picture and is usually not generated with data generator. It will also create a simplest block states definition which directly uses its block model.
You can also write like this (the following code is also in the generateBlockStateModels
method), which allows you to specify a different block model type. The returned value in the first line is the model id, which will be used when generating block states definitions:
final Identifier exampleBlockModelId = TexturedModel.CUBE_ALL.upload(TutorialBlocks.EXAMPLE_BLOCK, blockStateModelGenerator.modelCollector); blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.EXAMPLE_BLOCK, exampleBlockModelId);
To specify a different texture, you can create a block model manually (for example, all six faces using the mangrove log's top texture):
final Identifier exampleBlockModelId = Models.CUBE_ALL.upload(TutorialBlocks.EXAMPLE_BLOCK, TextureMap.all(Identifier.ofVanilla("block/mangrove_log_top")), blockStateModelGenerator.modelCollector); blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.EXAMPLE_BLOCK, exampleBlockModelId);
Simple item model (since 1.21.4)
Generating an item model is also quite simple:
- TutorialModelGenerator.java
@Override public void generateItemModels(ItemModelGenerator itemModelGenerator) { itemModelGenerator.register(TutorialItems.CUSTOM_ITEM, Models.GENERATED); }
The item model will use the simplest item models mappings, with item model item/generated
and a texture identical to its id (tutorial:item/custom_item
). Of course, you can also change Models.GENERATED
to other values according to your needs.
If you do not provide the second parameter initemModelGenerator.register
, only the item models definition will be generated, without an item model.
To specify a different texture (in this example, directly use vanilla white wool texture), you can also manually create models and models definitions (the following code is also in the generateItemModels
method).
- TutorialModelGenerator.java
itemModelGenerator.register(TutorialItems.CUSTOM_ITEM, Models.GENERATED); final Identifier modelId = Models.GENERATED.upload(TutorialItems.CUSTOM_ITEM, TextureMap.layer0(Identifier.of("block/white_wool")), itemModelGenerator.modelCollector); itemModelGenerator.output.accept(TutorialItems.CUSTOM_ITEM, ItemModels.basic(modelId));
Common vanilla block models (taking stairs and slabs for example)
Sometimes a block model is not so simple, because it is related to complicated block states. For example, stairs can have multiple facings, can be facing E/S/W/N, can be placed upright or upside, and can even be corner. It will definitely be troublesome to create models and block states definitions for these blocks. Luckily Minecraft has written for these common blocks well, and what we need to do is directly use them. Here we take stairs and slabs as an example, and create stairs and slabs for diamond blocks. First, let's quickly create the blocks:
For versions after 1.21.2:
- TutorialBlocks.json
public static final Block DIAMOND_STAIRS = register("diamond_stairs", settings -> new StairsBlock(Blocks.DIAMOND_BLOCK.getDefaultState(), settings), AbstractBlock.Settings.copy(Blocks.DIAMOND_BLOCK)); public static final Block DIAMOND_SLAB = register("diamond_slab", SlabBlock::new, AbstractBlock.Settings.copy(Blocks.DIORITE_SLAB));
For versions before 1.21.2:
- TutorialBlocks.json
public static final Block DIAMOND_STAIRS = register("diamond_stairs", new StairsBlock(Blocks.DIAMOND_BLOCK.getDefaultState(), AbstractBlock.Settings.copy(Blocks.DIAMOND_BLOCK))); public static final Block DIAMOND_SLAB = register("diamond_slab", new SlabBlock(AbstractBlock.Settings.copy(Blocks.DIORITE_SLAB)));
And then we create regular stairs model, inner corner stairs model, outer corner stairs model, bottom slab model and top slab model. For double slab model we directly use vanilla diamond block model. When creating models for these blocks, the id of the models will be returned, and we create block states definitions with the utility methods in BlockStateModelGenerator
, using the model ids that are returned before. We also need to use the regular stairs model and bottom slab model for the item model.
- TutorialModelGenerator.json
final TextureMap diamondTexture = TextureMap.all(Identifier.ofVanilla("block/diamond_block")); final Identifier stairsModelId = Models.STAIRS.upload(TutorialBlocks.DIAMOND_STAIRS, diamondTexture, blockStateModelGenerator.modelCollector); final Identifier innerStairsModelId = Models.INNER_STAIRS.upload(TutorialBlocks.DIAMOND_STAIRS, diamondTexture, blockStateModelGenerator.modelCollector); final Identifier outerStairsModelId = Models.OUTER_STAIRS.upload(TutorialBlocks.DIAMOND_STAIRS, diamondTexture, blockStateModelGenerator.modelCollector); blockStateModelGenerator.blockStateCollector.accept( BlockStateModelGenerator.createStairsBlockState(TutorialBlocks.DIAMOND_STAIRS, BlockStateModelGenerator.createWeightedVariant(innerStairsModelId), BlockStateModelGenerator.createWeightedVariant(stairsModelId), BlockStateModelGenerator.createWeightedVariant(outerStairsModelId))); blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.DIAMOND_STAIRS, stairsModelId); final Identifier slabBottomModelId = Models.SLAB.upload(TutorialBlocks.DIAMOND_SLAB, diamondTexture, blockStateModelGenerator.modelCollector); final Identifier slabTopModelId = Models.SLAB_TOP.upload(TutorialBlocks.DIAMOND_SLAB, diamondTexture, blockStateModelGenerator.modelCollector); blockStateModelGenerator.blockStateCollector.accept( BlockStateModelGenerator.createSlabBlockState(TutorialBlocks.DIAMOND_SLAB, BlockStateModelGenerator.createWeightedVariant(slabBottomModelId), BlockStateModelGenerator.createWeightedVariant(slabTopModelId), BlockStateModelGenerator.createWeightedVariant(Identifier.ofVanilla("block/diamond_block"))) ); blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.DIAMOND_SLAB, slabBottomModelId);
In versions before 1.21.4, we do not need to callBlockStateModelGenerator.createWeightedVariant
, but pass ids as parameters directly instead.
Well done! We successfully added all models, block states definitions and item models definitions needed with several lines of code.