|
| 1 | +--- |
| 2 | +title: Feature Generation |
| 3 | +description: A guide to generating features in the world with datagen. |
| 4 | +authors: |
| 5 | + - cassiancc |
| 6 | + - its-miroma |
| 7 | + - Wind292 |
| 8 | +--- |
| 9 | + |
| 10 | +<!----> |
| 11 | + |
| 12 | +::: info PREREQUISITES |
| 13 | + |
| 14 | +Make sure you've completed the [datagen setup](./setup) process first. |
| 15 | + |
| 16 | +::: |
| 17 | + |
| 18 | +The generation for features of Minecraft worlds is broken down into 3 parts: |
| 19 | + |
| 20 | +- **Configured Features**: this defines what a feature is; for example, a single tree |
| 21 | +- **Placement Features**: this defines how the features should be laid out, in which direction, relative location, and so on; for example, the placement of trees in a forest |
| 22 | +- **Biome Modifications**: this defines where the features are placed in the world; for example, the coordinates of the whole forest |
| 23 | + |
| 24 | +::: info |
| 25 | + |
| 26 | +Features in Minecraft are natural or generated patterns in the world, like trees, flowers, ores, or lakes. Features are different from structures (for example villages, temples...), which can be found with the `/locate` command. |
| 27 | + |
| 28 | +::: |
| 29 | + |
| 30 | +## Setup {#setup} |
| 31 | + |
| 32 | +First, we need to make our provider. Create a class that extends `FabricDynamicRegistryProvider` and fill out the base methods: |
| 33 | + |
| 34 | +@[code lang=java transcludeWith=:::datagen-world:provider](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldgenProvider.java) |
| 35 | + |
| 36 | +Then add this provider to your `DataGeneratorEntrypoint` class within the `onInitializeDataGenerator` method: |
| 37 | + |
| 38 | +@[code lang=java transclude={67-67}](@/reference/latest/src/client/java/com/example/docs/datagen/ExampleModDataGenerator.java) |
| 39 | + |
| 40 | +Next, make a class for your configured features and a class for your placed features. These don't need to extend anything. |
| 41 | + |
| 42 | +The configured feature class and placed feature class should both have a public method to register and define your features. Its argument, that we called `context`, should be a `BootstrapContext<ConfiguredFeature<?, ?>>` for the configured feature, or a `BootstrapContext<PlacedFeature>` for the placed feature. |
| 43 | + |
| 44 | +In your `DataGeneratorEntrypoint` class, add the lines below to your `buildRegistry` method, replacing the method name with what you chose: |
| 45 | + |
| 46 | +@[code lang=java transcludeWith=:::datagen-world:registries](@/reference/latest/src/client/java/com/example/docs/datagen/ExampleModDataGenerator.java) |
| 47 | + |
| 48 | +If you don't already have the `buildRegistry` method, create it and annotate it with an `@Override`. |
| 49 | + |
| 50 | +## Configured Features {#configured-features} |
| 51 | + |
| 52 | +To make a feature naturally spawn in our world, we should start by defining a configured feature in our configured features class. Let's add a custom configured feature for a Diamond Ore vein. |
| 53 | + |
| 54 | +First, register the key for the `ConfiguredFeature` in your configured feature class: |
| 55 | + |
| 56 | +@[code lang=java transcludeWith=:::datagen-world:configured-key](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldConfiguredFeatures.java) |
| 57 | + |
| 58 | +::: tip |
| 59 | + |
| 60 | +The second argument to the `Identifier` (`diamond_block_vein` in this example) is what you would use to spawn in the structure with the `/place` command, which is helpful for debugging. |
| 61 | + |
| 62 | +::: |
| 63 | + |
| 64 | +### Ores {#ores} |
| 65 | + |
| 66 | +Next, we'll make a `RuleTest` that controls which blocks your feature can replace. For example, this `RuleTest` allows the replacement of every block with the tag `DEEPSLATE_ORE_REPLACEABLES`: |
| 67 | + |
| 68 | +@[code lang=java transcludeWith=:::datagen-world:ruletest](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldConfiguredFeatures.java) |
| 69 | + |
| 70 | +Next, we need to create the `OreConfiguration`, which tells the game what to replace blocks with. |
| 71 | + |
| 72 | +@[code lang=java transcludeWith=:::datagen-world:ore-feature-config](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldConfiguredFeatures.java) |
| 73 | + |
| 74 | +You can have multiple cases in the list for different variants. For example, let's set a different variant for stone and deepslate: |
| 75 | + |
| 76 | +@[code lang=java transcludeWith=:::datagen-world:multi-ore-feature-config](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldConfiguredFeatures.java) |
| 77 | + |
| 78 | +Lastly, we need to register our configured feature to our game! |
| 79 | + |
| 80 | +@[code lang=java transcludeWith=:::datagen-world:conf-feature-register](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldConfiguredFeatures.java) |
| 81 | + |
| 82 | +### Trees {#trees} |
| 83 | + |
| 84 | +To make a custom tree, you need to first create a `TreeConfiguration`: |
| 85 | + |
| 86 | +@[code lang=java transcludeWith=:::datagen-world:tree-feature-config](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldConfiguredFeatures.java) |
| 87 | + |
| 88 | +This is what each argument does: |
| 89 | + |
| 90 | +1. Specifies the block type for the tree trunk; for example, diamond blocks |
| 91 | +2. Configures the trunk shape and height behavior using a trunk placer |
| 92 | +3. Specifies the block type for the tree leaves; for example, gold blocks |
| 93 | +4. Defines the foliage's shape and size using a foliage placer |
| 94 | +5. Controls how the tree trunk tapers at different heights, primarily for larger trunks |
| 95 | + |
| 96 | +::: tip |
| 97 | + |
| 98 | +We _highly_ recommend that you play around with these values to create a custom tree that **you** are happy with! |
| 99 | + |
| 100 | +You can use the built-in placers for the Trunk and Foliage from the vanilla trees as a reference. |
| 101 | + |
| 102 | +::: |
| 103 | + |
| 104 | +Next, we need to register our tree by adding the following line to the `configure` method of `ExampleModWorldConfiguredFeatures`. |
| 105 | + |
| 106 | +@[code lang=java transcludeWith=:::datagen-world:tree-register](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldConfiguredFeatures.java) |
| 107 | + |
| 108 | +## Placement Features {#placement-features} |
| 109 | + |
| 110 | +The next step in adding a feature to the game is creating its Placement Feature. |
| 111 | + |
| 112 | +In your placed features class's `configure` method, create a variable like the one below: |
| 113 | + |
| 114 | +@[code lang=java transcludeWith=:::datagen-world:conf-feature-register](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldPlacedFeatures.java) |
| 115 | + |
| 116 | +In your placed features class, define the key for your placed feature. |
| 117 | + |
| 118 | +@[code lang=java transcludeWith=:::datagen-world:placed-key](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldPlacedFeatures.java) |
| 119 | + |
| 120 | +### Placement Modifiers {#placement-modifiers} |
| 121 | + |
| 122 | +Next, we need to define our Placement Modifiers, which are attributes that you set when spawning the feature. These can be anything: from the spawn frequency, to the starting `y` level. You can have as few or as many modifiers as you like. |
| 123 | + |
| 124 | +@[code lang=java transcludeWith=:::datagen-world:placement-modifier](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldPlacedFeatures.java) |
| 125 | + |
| 126 | +The function of each modifier listed is as follows: |
| 127 | + |
| 128 | +- **CountPlacement**: Roughly the amount of instances of this feature (in this case veins) per chunk |
| 129 | +- **BiomeFilter**: Allows us to control what biomes/dimensions it spawns (we'll do more with this later) |
| 130 | +- **InSquarePlacement**: Spreads the features more pseudo-randomly |
| 131 | +- **HeightRangePlacement**: Specifies the range of `y` coordinates where a feature can spawn; it supports three main types of distributions: |
| 132 | + 1. **Uniform**: |
| 133 | + All `y` values within the range are equally likely to contain the feature. If you're unsure, just use this one. |
| 134 | + |
| 135 | + 2. **Trapezoid**: |
| 136 | + `y` values closer to the median `y` value have a higher probability of containing the feature. |
| 137 | + |
| 138 | + 3. **Biased-Bottom**: |
| 139 | + Uses a logarithmic scale where lower `y` values are more likely to get the feature. It receives a starting `y` coordinate, below which the feature never spawns. The second argument is the maximum height where the feature can spawn. The third argument defines a range in blocks over which the maximum probability is extended. |
| 140 | + |
| 141 | +::: tip |
| 142 | + |
| 143 | +Trees and other surface structures should include the modifier `PlacedFeatures.WORLD_SURFACE_WG_HEIGHTMAP` instead of `HeightRangePlacement`, to make sure the tree spawns on the surface. |
| 144 | + |
| 145 | +::: |
| 146 | + |
| 147 | +Now that we have the modifiers, we can register our placed feature: |
| 148 | + |
| 149 | +@[code lang=java transcludeWith=:::datagen-world:register-placed-feature](@/reference/latest/src/main/java/com/example/docs/worldgen/ExampleModWorldPlacedFeatures.java) |
| 150 | + |
| 151 | +## Biome Modifications {#biome-modifications} |
| 152 | + |
| 153 | +Lastly, we need to add our placed feature to `BiomeModifications` during mod initialization. We can do this by adding the following to our mod initiializer: |
| 154 | + |
| 155 | +@[code lang=java transcludeWith=:::datagen-world:biome-modifications](@/reference/latest/src/main/java/com/example/docs/ExampleMod.java) |
| 156 | + |
| 157 | +::: tip |
| 158 | + |
| 159 | +For trees, the second parameter should be set to `GenerationStep.Decoration.VEGETAL_DECORATION,` |
| 160 | + |
| 161 | +::: |
| 162 | + |
| 163 | +### Biome Specific Generation {#biome-specific-generation} |
| 164 | + |
| 165 | +By changing the `BiomeSelectors` argument, we can have our feature spawn only in a specific type of biome: |
| 166 | + |
| 167 | +@[code lang=java transcludeWith=:::datagen-world:selective-biome-modifications](@/reference/latest/src/main/java/com/example/docs/ExampleMod.java) |
| 168 | + |
| 169 | +This would only spawn in biomes tagged with the `minecraft:is_forest` biome tag. |
| 170 | + |
| 171 | +## Running Datagen {#running-datagen} |
| 172 | + |
| 173 | +Now, when you run datagen, you should see a `.json` file under `src/main/generated/data/example-mod/worldgen/configured_feature` for each configured feature you added, and a file under `src/main/generated/data/example-mod/worldgen/placed_feature` for each placed feature as well! |
| 174 | + |
| 175 | +::: details Generated File for the Configured Feature |
| 176 | + |
| 177 | +@[code lang=json](@/reference/latest/src/main/generated/data/example-mod/worldgen/configured_feature/diamond_block_vein.json) |
| 178 | + |
| 179 | +::: |
| 180 | + |
| 181 | +::: details Generated File for the Placed Feature |
| 182 | + |
| 183 | +@[code lang=json](@/reference/latest/src/main/generated/data/example-mod/worldgen/placed_feature/diamond_block_ore_placed.json) |
| 184 | + |
| 185 | +::: |
| 186 | + |
| 187 | +<!----> |
0 commit comments