tutorial:datagen_model
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorial:datagen_model [2023/02/20 05:13] – [Adding Block Models] solidblock | tutorial:datagen_model [2025/04/18 14:59] (current) – [Model Generation] solidblock | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Model Generation ====== | ====== Model Generation ====== | ||
+ | :!: The page is translated from [[zh_cn: | ||
- | You can generate block (with automatic block states) and item models easily using the '' | + | 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**, |
- | To get started, create | + | In data generator, models and definitions are generated together. Usually, when generation |
+ | > :!: **Note:** In the tutorials created previously, there may be some JSON files already created in the '' | ||
- | <code java> | + | > :!: **Note:** Since 1.21.4, data generation are divided into client and server. In vanilla, classes related to models will be annotated '' |
- | private static class MyModelGenerator extends FabricModelProvider { | + | |
- | private MyModelGenerator(FabricDataGenerator generator) { | + | |
- | super(generator); | + | |
- | } | + | |
- | @Override | + | ===== Preparation ===== |
- | public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { | + | |
- | // ... | + | |
- | } | + | |
- | @Override | + | First, create a class that extends '' |
- | public | + | <code java TutorialModelGenerator.java> |
- | // ... | + | public static class TutorialModelGenerator extends FabricModelProvider { |
- | } | + | |
- | } | + | |
+ | } | ||
- | // ... | + | |
- | + | @Override | |
- | @Override | + | public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { |
- | public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { | + | |
// ... | // ... | ||
- | fabricDataGenerator.addProvider(MyModelGenerator:: | + | } |
+ | |||
+ | |||
+ | @Override | ||
+ | public void generateItemModels(ItemModelGenerator itemModelGenerator) { | ||
+ | // ... | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code java ExampleModDataGenerator.java> | ||
+ | public class ExampleModDataGenerator implements DataGeneratorEntrypoint { | ||
+ | @Override | ||
+ | public void onInitializeDataGenerator(FabricDataGenerator generator) { | ||
// ... | // ... | ||
+ | | ||
+ | pack.addProvider(TutorialModelGenerator:: | ||
+ | } | ||
} | } | ||
</ | </ | ||
- | ===== Adding Block Models | + | ===== 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: | ||
+ | <code java 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: '' | ||
- | The '' | + | You can also write like this (the following code is also in the '' |
+ | <code java> | ||
+ | final Identifier exampleBlockModelId = TexturedModel.CUBE_ALL.upload(TutorialBlocks.EXAMPLE_BLOCK, | ||
+ | blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.EXAMPLE_BLOCK, | ||
+ | </ | ||
+ | To specify a different texture, you can create a block model manually (for example, all six faces using the mangrove log's top texture): | ||
<code java> | <code java> | ||
- | public static Block SIMPLE_BLOCK | + | final Identifier exampleBlockModelId |
- | public static BlockItem SIMPLE_BLOCK_ITEM = Registry.register(Registries.ITEM, ..., new BlockItem(SIMPLE_BLOCK, | + | |
- | // ... | + | </code> |
- | @Override | + | ===== Simple item model (since 1.21.4) ===== |
- | public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { | + | Generating an item model is also quite simple: |
- | blockStateModelGenerator.registerSimpleCubeAll(SIMPLE_BLOCK); | + | <code java TutorialModelGenerator.java> |
- | } | + | |
+ | public void generateItemModels(ItemModelGenerator itemModelGenerator) { | ||
+ | | ||
+ | } | ||
</ | </ | ||
- | Since a '' | + | The item model will use the simplest item models mappings, with item model '' |
- | ==== Strict Validation ==== | + | > If you do not provide |
- | By default, data generation will throw an exception if the run did not generate blockstates for all blocks belonging to the processed mods. | + | |
- | Fabric API allows disabling this. To do so, edit your '' | + | |
- | ===== Adding Item Models | + | 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 '' |
+ | <code java TutorialModelGenerator.java> | ||
+ | itemModelGenerator.register(TutorialItems.CUSTOM_ITEM, | ||
+ | final Identifier modelId | ||
+ | itemModelGenerator.output.accept(TutorialItems.CUSTOM_ITEM, | ||
- | The '' | + | </ |
- | In this example, we will override the item model generated from the '' | ||
- | <code java> | + | ===== Common vanilla block models |
- | public static Block SIMPLE_BLOCK | + | |
- | public static BlockItem SIMPLE_BLOCK_ITEM | + | |
- | // ... | + | |
- | @Override | + | 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: |
- | public | + | |
- | itemModelGenerator.register(SIMPLE_BLOCK_ITEM, Models.GENERATED); | + | For versions after 1.21.2: |
- | } | + | <code java TutorialBlocks.json> |
+ | public | ||
+ | settings -> new StairsBlock(Blocks.DIAMOND_BLOCK.getDefaultState(), settings), | ||
+ | | ||
+ | public static final Block DIAMOND_SLAB = register(" | ||
+ | SlabBlock:: | ||
+ | AbstractBlock.Settings.copy(Blocks.DIORITE_SLAB)); | ||
</ | </ | ||
- | FIXME //**This is not done yet!!**// | ||
- | ==== Addin Data Generation For a Directional Block ==== | ||
- | **QUICK WARNING**: This is very complicated as heck!!! | ||
- | In this, example, we will generate directional blockstates for our '' | + | For versions before 1.21.2: |
+ | <code java TutorialBlocks.json> | ||
+ | public static final Block DIAMOND_STAIRS = register(" | ||
+ | new StairsBlock(Blocks.DIAMOND_BLOCK.getDefaultState(), | ||
+ | AbstractBlock.Settings.copy(Blocks.DIAMOND_BLOCK))); | ||
+ | public static final Block DIAMOND_SLAB = register(" | ||
+ | new SlabBlock(AbstractBlock.Settings.copy(Blocks.DIORITE_SLAB))); | ||
+ | </ | ||
- | Firstly, we add the block itself | + | 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 |
- | <code java> | + | |
- | // In the Tutorial class (or your mod initializer class) | + | |
- | public static final Block MACHINE_BLOCK = new Block(FabricBlockSettings.copy(Blocks.BLAST_FURNACE)); | + | |
- | @Override | + | <code java TutorialModelGenerator.json> |
- | public void onInitialize() | + | final TextureMap diamondTexture = TextureMap.all(Identifier.ofVanilla(" |
- | { | + | |
- | | + | |
- | } | + | final Identifier |
+ | final Identifier outerStairsModelId = Models.OUTER_STAIRS.upload(TutorialBlocks.DIAMOND_STAIRS, | ||
+ | blockStateModelGenerator.blockStateCollector.accept( | ||
+ | BlockStateModelGenerator.createStairsBlockState(TutorialBlocks.DIAMOND_STAIRS, | ||
+ | BlockStateModelGenerator.createWeightedVariant(innerStairsModelId), | ||
+ | BlockStateModelGenerator.createWeightedVariant(stairsModelId), | ||
+ | BlockStateModelGenerator.createWeightedVariant(outerStairsModelId))); | ||
+ | blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.DIAMOND_STAIRS, | ||
+ | |||
+ | final Identifier slabBottomModelId = Models.SLAB.upload(TutorialBlocks.DIAMOND_SLAB, | ||
+ | final Identifier slabTopModelId = Models.SLAB_TOP.upload(TutorialBlocks.DIAMOND_SLAB, | ||
+ | blockStateModelGenerator.blockStateCollector.accept( | ||
+ | BlockStateModelGenerator.createSlabBlockState(TutorialBlocks.DIAMOND_SLAB, | ||
+ | BlockStateModelGenerator.createWeightedVariant(slabBottomModelId), | ||
+ | BlockStateModelGenerator.createWeightedVariant(slabTopModelId), | ||
+ | BlockStateModelGenerator.createWeightedVariant(Identifier.ofVanilla("block/ | ||
+ | | ||
+ | | ||
</ | </ | ||
- | Now that we have successfully registered our block, let's get to the good stuff! | + | > In versions before 1.21.4, we do not need to call '' |
- | <code java> | + | Well done! We successfully added all models, block states definitions and item models definitions needed with several lines of code. |
- | private static class MyModelGenerator extends FabricModelProvider { | + | |
- | private MyModelGenerator(FabricDataGenerator generator) { | + | ===== Directional blocks |
- | super(generator); | + | |
- | } | + | A directional block usually uses one block model, but in the block states definition, different model variants will be mapped into, such as different x-rotation, y-rotation and uvlock. |
- | + | ||
- | @Override | + | Taking a vertical slab block we created in the [[directionalblock]] tutorial as an example, we generate models and block states definitions with data generator. |
- | public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { | + | |
- | // ... | + | ==== Custom model ==== |
- | | + | |
- | .with(When.create().set(Properties.HORIZONTAL_FACING, Direction.NORTH), | + | We have created a '' |
- | | + | |
- | } | + | To inherit this template model in the data generator, we create a '' |
- | + | ||
- | @Override | + | <code java TutorialModelGenerator.java> |
- | public void generateItemModels(ItemModelGenerator itemModelGenerator) { | + | public class TutorialModelGenerator extends FabricModelProvider { |
- | // ... | + | public static final Model VERTICAL_SLAB = new Model( |
- | } | + | Optional.of(Identifier.of(" |
+ | | ||
+ | TextureKey.BOTTOM, TextureKey.TOP, TextureKey.SIDE); | ||
+ | |||
+ | // ... | ||
} | } | ||
</ | </ | ||
+ | |||
+ | Then we call the '' | ||
+ | <code java TutorialModelGenerator.java> | ||
+ | @Override | ||
+ | public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { | ||
+ | // ... | ||
+ | | ||
+ | final Identifier verticalSlabModelId = VERTICAL_SLAB.upload(TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB, | ||
+ | blockStateModelGenerator.registerParentedItemModel(TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Custom block states definition (since 1.21.5) ==== | ||
+ | It's now an important part — now we create a block states definition for a vertical slab block. Actually it's not so complicated, | ||
+ | |||
+ | In 1.21.5, a block states definition is '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | For '' | ||
+ | * **Method one**: Specify one model variant, and modify the variant according to block states, such as modifying x-rotation, y-rotation and uvlock. All block states use the same model id, with only possible different variants. | ||
+ | * **Method two**: Allocating model variants for different block states directly, and then you can also continue to modify variants. In this case, the block states may use different model ids. | ||
+ | |||
+ | Our vertical slab has two block state properties: '' | ||
+ | <code java TutorialModelGenerator.java> | ||
+ | @Override | ||
+ | public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { | ||
+ | // ... | ||
+ | | ||
+ | blockStateModelGenerator.blockStateCollector.accept( | ||
+ | VariantsBlockModelDefinitionCreator.of(TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB, | ||
+ | BlockStateModelGenerator.createWeightedVariant(verticalSlabModelId)) | ||
+ | .apply(BlockStateModelGenerator.UV_LOCK) | ||
+ | .coordinate(BlockStateVariantMap.operations(VerticalSlabBlock.FACING) | ||
+ | .register(Direction.NORTH, | ||
+ | .register(Direction.EAST, | ||
+ | .register(Direction.SOUTH, | ||
+ | .register(Direction.WEST, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The '' | ||
+ | |||
+ | > Can we use method two? Of course yes! The code is the following: | ||
+ | > <code java TutorialModelGenerator.java> | ||
+ | blockStateModelGenerator.blockStateCollector.accept( | ||
+ | VariantsBlockModelDefinitionCreator.of( | ||
+ | TutorialBlocks.POLISHED_ANDESITE_VERTICAL_SLAB) | ||
+ | .with(BlockStateVariantMap.models(VerticalSlabBlock.FACING) | ||
+ | .register(Direction.NORTH, | ||
+ | .register(Direction.EAST, | ||
+ | .register(Direction.SOUTH, | ||
+ | .register(Direction.WEST, | ||
+ | ) | ||
+ | .apply(BlockStateModelGenerator.UV_LOCK) | ||
+ | ); | ||
+ | </ | ||
+ | > We find that in method two, when calling '' | ||
+ | |||
+ | In the method one and method two described above, apart from registering model operations or model variants one by one with the '' | ||
+ | |||
+ | If a block state has multiple properties that affect model variants, you can provide multiple properties in '' |
tutorial/datagen_model.1676870018.txt.gz · Last modified: 2023/02/20 05:13 by solidblock