mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-25 13:30:08 +00:00
Discounted occupied flower columns
- Converted patch decorators also lose the share of surface columns that earlier surface patches in the declaring biomes already occupy, estimated from those patches' expected coverage in the catalog - Canopy and surface occupancy are combined into one vegetation occlusion estimate per decorated feature
This commit is contained in:
6 files changed
+94
-42
No files matched your search
@@ -45,7 +45,7 @@ final class ModFeatureIngester {
|
|||||||
private final IngestPackModel model;
|
private final IngestPackModel model;
|
||||||
private final WorldgenCatalogIndex index;
|
private final WorldgenCatalogIndex index;
|
||||||
private final TreeFeatures trees;
|
private final TreeFeatures trees;
|
||||||
private final CanopyEstimate canopy;
|
private final VegetationOcclusion occlusion;
|
||||||
private final Map<String, FeaturePlan> plans = new TreeMap<>();
|
private final Map<String, FeaturePlan> plans = new TreeMap<>();
|
||||||
private final Set<String> nativeKeys = new TreeSet<>();
|
private final Set<String> nativeKeys = new TreeSet<>();
|
||||||
private final Set<String> vanillaScopedKeys = new TreeSet<>();
|
private final Set<String> vanillaScopedKeys = new TreeSet<>();
|
||||||
@@ -54,7 +54,7 @@ final class ModFeatureIngester {
|
|||||||
this.model = model;
|
this.model = model;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.trees = new TreeFeatures(index);
|
this.trees = new TreeFeatures(index);
|
||||||
this.canopy = new CanopyEstimate(index);
|
this.occlusion = new VegetationOcclusion(index);
|
||||||
Map<String, Set<String>> declaring = new TreeMap<>();
|
Map<String, Set<String>> declaring = new TreeMap<>();
|
||||||
Map<String, Set<Integer>> steps = new TreeMap<>();
|
Map<String, Set<Integer>> steps = new TreeMap<>();
|
||||||
for (CatalogBiome biome : index.catalog().biomes()) {
|
for (CatalogBiome biome : index.catalog().biomes()) {
|
||||||
@@ -210,7 +210,7 @@ final class ModFeatureIngester {
|
|||||||
}
|
}
|
||||||
if (PatchDecoratorConverter.handles(placed)) {
|
if (PatchDecoratorConverter.handles(placed)) {
|
||||||
return new FeaturePlan(key, declaring, steps, null,
|
return new FeaturePlan(key, declaring, steps, null,
|
||||||
PatchDecoratorConverter.convert(placed, index, canopy.occlusion(key, declaring)), "");
|
PatchDecoratorConverter.convert(placed, index, occlusion.occlusion(key, declaring)), "");
|
||||||
}
|
}
|
||||||
} catch (IngestConversionException e) {
|
} catch (IngestConversionException e) {
|
||||||
return FeaturePlan.passthrough(key, declaring, steps, "generated natively: " + e.getMessage());
|
return FeaturePlan.passthrough(key, declaring, steps, "generated natively: " + e.getMessage());
|
||||||
|
|||||||
@@ -45,30 +45,23 @@ final class PatchDecoratorConverter {
|
|||||||
return type.equals(SIMPLE_BLOCK) || LEGACY_PATCH_TYPES.contains(type);
|
return type.equals(SIMPLE_BLOCK) || LEGACY_PATCH_TYPES.contains(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonObject convert(CatalogPlacedFeature feature, WorldgenCatalogIndex index, double canopy) throws IngestConversionException {
|
static JsonObject convert(CatalogPlacedFeature feature, WorldgenCatalogIndex index,
|
||||||
|
VegetationOcclusion.Occlusion occlusion) throws IngestConversionException {
|
||||||
String type = VanillaJson.namespaced(feature.featureType());
|
String type = VanillaJson.namespaced(feature.featureType());
|
||||||
JsonObject config = VanillaJson.featureConfig(feature.config());
|
JsonObject config = VanillaJson.featureConfig(feature.config());
|
||||||
VanillaPlacementChain outer = VanillaPlacementChain.surfaceChain(VanillaJson.placement(feature.placement()));
|
VanillaPlacementChain outer = VanillaPlacementChain.surfaceChain(VanillaJson.placement(feature.placement()));
|
||||||
Set<String> whitelist = new LinkedHashSet<>(outer.whitelist());
|
Set<String> whitelist = new LinkedHashSet<>(outer.whitelist());
|
||||||
double expected;
|
|
||||||
JsonElement provider;
|
JsonElement provider;
|
||||||
if (LEGACY_PATCH_TYPES.contains(type)) {
|
if (LEGACY_PATCH_TYPES.contains(type)) {
|
||||||
int tries = VanillaJson.integer(config, "tries", LEGACY_DEFAULT_TRIES, "patch");
|
|
||||||
int verticalSpread = Math.abs(VanillaJson.integer(config, "y_spread", LEGACY_DEFAULT_Y_SPREAD, "patch"));
|
|
||||||
double surfaceHitChance = outer.surfaceHitChance() / (verticalSpread + 1D);
|
|
||||||
NestedBlock nested = nestedBlock(config.get("feature"), index);
|
NestedBlock nested = nestedBlock(config.get("feature"), index);
|
||||||
whitelist.addAll(nested.whitelist());
|
whitelist.addAll(nested.whitelist());
|
||||||
provider = nested.provider();
|
provider = nested.provider();
|
||||||
expected = outer.expectedPlacements() * tries * surfaceHitChance;
|
|
||||||
} else {
|
} else {
|
||||||
provider = config.get("to_place");
|
provider = config.get("to_place");
|
||||||
expected = outer.expectedPlacements() * outer.surfaceHitChance();
|
|
||||||
}
|
|
||||||
if (expected <= 0D) {
|
|
||||||
throw new IngestConversionException("patch placement never places");
|
|
||||||
}
|
}
|
||||||
|
double expected = expectedPlacements(feature, index) * (1D - clamp(occlusion.occupancy()));
|
||||||
if (outer.leafBlocked()) {
|
if (outer.leafBlocked()) {
|
||||||
expected *= 1D - Math.max(0D, Math.min(1D, canopy));
|
expected *= 1D - clamp(occlusion.canopy());
|
||||||
}
|
}
|
||||||
List<JsonObject> palette = VanillaBlockStates.palette(provider);
|
List<JsonObject> palette = VanillaBlockStates.palette(provider);
|
||||||
JsonObject decorator = new JsonObject();
|
JsonObject decorator = new JsonObject();
|
||||||
@@ -90,6 +83,27 @@ final class PatchDecoratorConverter {
|
|||||||
return decorator;
|
return decorator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double expectedPlacements(CatalogPlacedFeature feature, WorldgenCatalogIndex index) throws IngestConversionException {
|
||||||
|
JsonObject config = VanillaJson.featureConfig(feature.config());
|
||||||
|
VanillaPlacementChain outer = VanillaPlacementChain.surfaceChain(VanillaJson.placement(feature.placement()));
|
||||||
|
double expected;
|
||||||
|
if (LEGACY_PATCH_TYPES.contains(VanillaJson.namespaced(feature.featureType()))) {
|
||||||
|
int tries = VanillaJson.integer(config, "tries", LEGACY_DEFAULT_TRIES, "patch");
|
||||||
|
int verticalSpread = Math.abs(VanillaJson.integer(config, "y_spread", LEGACY_DEFAULT_Y_SPREAD, "patch"));
|
||||||
|
expected = outer.expectedPlacements() * tries * outer.surfaceHitChance() / (verticalSpread + 1D);
|
||||||
|
} else {
|
||||||
|
expected = outer.expectedPlacements() * outer.surfaceHitChance();
|
||||||
|
}
|
||||||
|
if (expected <= 0D) {
|
||||||
|
throw new IngestConversionException("patch placement never places");
|
||||||
|
}
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double clamp(double share) {
|
||||||
|
return Math.max(0D, Math.min(1D, share));
|
||||||
|
}
|
||||||
|
|
||||||
static double columnCoverage(double expectedPlacements) {
|
static double columnCoverage(double expectedPlacements) {
|
||||||
return 1D - Math.exp(-expectedPlacements / COLUMNS_PER_CHUNK);
|
return 1D - Math.exp(-expectedPlacements / COLUMNS_PER_CHUNK);
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-14
@@ -27,7 +27,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
final class CanopyEstimate {
|
final class VegetationOcclusion {
|
||||||
static final double CROWN_COLUMNS = 20D;
|
static final double CROWN_COLUMNS = 20D;
|
||||||
private static final double UNKNOWN_TREE_COUNT = 1D;
|
private static final double UNKNOWN_TREE_COUNT = 1D;
|
||||||
private static final double COLUMNS_PER_CHUNK = 256D;
|
private static final double COLUMNS_PER_CHUNK = 256D;
|
||||||
@@ -36,39 +36,55 @@ final class CanopyEstimate {
|
|||||||
private final WorldgenCatalogIndex index;
|
private final WorldgenCatalogIndex index;
|
||||||
private final TreeFeatures trees;
|
private final TreeFeatures trees;
|
||||||
|
|
||||||
CanopyEstimate(WorldgenCatalogIndex index) {
|
VegetationOcclusion(WorldgenCatalogIndex index) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.trees = new TreeFeatures(index);
|
this.trees = new TreeFeatures(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
double occlusion(String featureKey, Set<String> declaringBiomes) {
|
Occlusion occlusion(String featureKey, Set<String> declaringBiomes) {
|
||||||
if (declaringBiomes.isEmpty()) {
|
if (declaringBiomes.isEmpty()) {
|
||||||
return 0D;
|
return Occlusion.NONE;
|
||||||
}
|
}
|
||||||
double total = 0D;
|
double canopy = 0D;
|
||||||
|
double occupancy = 0D;
|
||||||
for (String biomeKey : declaringBiomes) {
|
for (String biomeKey : declaringBiomes) {
|
||||||
Optional<CatalogBiome> biome = index.biome(biomeKey);
|
Optional<CatalogBiome> biome = index.biome(biomeKey);
|
||||||
total += biome.map((CatalogBiome value) -> canopyBefore(value, featureKey)).orElse(0D);
|
if (biome.isEmpty() || biome.get().featureSteps().size() <= VEGETAL_STEP) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Occlusion before = before(biome.get().featureSteps().get(VEGETAL_STEP), featureKey);
|
||||||
|
canopy += before.canopy();
|
||||||
|
occupancy += before.occupancy();
|
||||||
}
|
}
|
||||||
return total / declaringBiomes.size();
|
return new Occlusion(canopy / declaringBiomes.size(), occupancy / declaringBiomes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private double canopyBefore(CatalogBiome biome, String featureKey) {
|
private Occlusion before(List<String> vegetal, String featureKey) {
|
||||||
if (biome.featureSteps().size() <= VEGETAL_STEP) {
|
|
||||||
return 0D;
|
|
||||||
}
|
|
||||||
List<String> vegetal = biome.featureSteps().get(VEGETAL_STEP);
|
|
||||||
double treesPerChunk = 0D;
|
double treesPerChunk = 0D;
|
||||||
|
double open = 1D;
|
||||||
for (String candidate : vegetal) {
|
for (String candidate : vegetal) {
|
||||||
if (candidate.equals(featureKey)) {
|
if (candidate.equals(featureKey)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Optional<CatalogPlacedFeature> feature = index.placedFeature(candidate);
|
Optional<CatalogPlacedFeature> feature = index.placedFeature(candidate);
|
||||||
if (feature.isPresent() && trees.isTree(feature.get())) {
|
if (feature.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trees.isTree(feature.get())) {
|
||||||
treesPerChunk += treesPerChunk(feature.get());
|
treesPerChunk += treesPerChunk(feature.get());
|
||||||
|
} else if (PatchDecoratorConverter.handles(feature.get())) {
|
||||||
|
open *= 1D - surfaceCoverage(feature.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1D - Math.exp(-treesPerChunk * CROWN_COLUMNS / COLUMNS_PER_CHUNK);
|
return new Occlusion(1D - Math.exp(-treesPerChunk * CROWN_COLUMNS / COLUMNS_PER_CHUNK), 1D - open);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double surfaceCoverage(CatalogPlacedFeature patch) {
|
||||||
|
try {
|
||||||
|
return PatchDecoratorConverter.columnCoverage(PatchDecoratorConverter.expectedPlacements(patch, index));
|
||||||
|
} catch (IngestConversionException unknownDensity) {
|
||||||
|
return 0D;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double treesPerChunk(CatalogPlacedFeature tree) {
|
private static double treesPerChunk(CatalogPlacedFeature tree) {
|
||||||
@@ -78,4 +94,8 @@ final class CanopyEstimate {
|
|||||||
return UNKNOWN_TREE_COUNT;
|
return UNKNOWN_TREE_COUNT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record Occlusion(double canopy, double occupancy) {
|
||||||
|
static final Occlusion NONE = new Occlusion(0D, 0D);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ public class FeatureConverterFormatTest {
|
|||||||
+ "{\"type\": \"minecraft:heightmap\", \"heightmap\": \"WORLD_SURFACE_WG\"}, {\"type\": \"minecraft:biome\"},"
|
+ "{\"type\": \"minecraft:heightmap\", \"heightmap\": \"WORLD_SURFACE_WG\"}, {\"type\": \"minecraft:biome\"},"
|
||||||
+ "{\"type\": \"minecraft:count\", \"count\": 96}," + PATCH_TAIL + "]");
|
+ "{\"type\": \"minecraft:count\", \"count\": 96}," + PATCH_TAIL + "]");
|
||||||
|
|
||||||
JsonObject decorator = PatchDecoratorConverter.convert(lavender, index(), 0D);
|
JsonObject decorator = PatchDecoratorConverter.convert(lavender, index(), VegetationOcclusion.Occlusion.NONE);
|
||||||
|
|
||||||
assertEquals(1.0D - Math.exp(-(15.0D * 96.0D * 0.25D) / 256.0D), decorator.get("chance").getAsDouble(), 0.000001D);
|
assertEquals(1.0D - Math.exp(-(15.0D * 96.0D * 0.25D) / 256.0D), decorator.get("chance").getAsDouble(), 0.000001D);
|
||||||
assertTrue(decorator.get("chance").getAsDouble() < 1.0D);
|
assertTrue(decorator.get("chance").getAsDouble() < 1.0D);
|
||||||
@@ -133,9 +133,11 @@ public class FeatureConverterFormatTest {
|
|||||||
+ "{\"type\": \"minecraft:heightmap\", \"heightmap\": \"MOTION_BLOCKING_NO_LEAVES\"}, {\"type\": \"minecraft:biome\"}]");
|
+ "{\"type\": \"minecraft:heightmap\", \"heightmap\": \"MOTION_BLOCKING_NO_LEAVES\"}, {\"type\": \"minecraft:biome\"}]");
|
||||||
|
|
||||||
assertEquals(1.0D - Math.exp(-(4.0D * 0.5D) / 256.0D),
|
assertEquals(1.0D - Math.exp(-(4.0D * 0.5D) / 256.0D),
|
||||||
PatchDecoratorConverter.convert(lavender, index(), 0.5D).get("chance").getAsDouble(), 0.000001D);
|
PatchDecoratorConverter.convert(lavender, index(), new VegetationOcclusion.Occlusion(0.5D, 0D)).get("chance").getAsDouble(), 0.000001D);
|
||||||
assertEquals(1.0D - Math.exp(-4.0D / 256.0D),
|
assertEquals(1.0D - Math.exp(-4.0D / 256.0D),
|
||||||
PatchDecoratorConverter.convert(underLeaves, index(), 0.5D).get("chance").getAsDouble(), 0.000001D);
|
PatchDecoratorConverter.convert(underLeaves, index(), new VegetationOcclusion.Occlusion(0.5D, 0D)).get("chance").getAsDouble(), 0.000001D);
|
||||||
|
assertEquals(1.0D - Math.exp(-(4.0D * 0.25D) / 256.0D),
|
||||||
|
PatchDecoratorConverter.convert(underLeaves, index(), new VegetationOcclusion.Occlusion(0D, 0.75D)).get("chance").getAsDouble(), 0.000001D);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -152,7 +154,7 @@ public class FeatureConverterFormatTest {
|
|||||||
{"type": "minecraft:biome"}]
|
{"type": "minecraft:biome"}]
|
||||||
""");
|
""");
|
||||||
|
|
||||||
JsonObject decorator = PatchDecoratorConverter.convert(flowers, index(), 0D);
|
JsonObject decorator = PatchDecoratorConverter.convert(flowers, index(), VegetationOcclusion.Occlusion.NONE);
|
||||||
|
|
||||||
assertEquals(1.0D - Math.exp(-(0.5D * 0.2D * 0.25D) / 256.0D), decorator.get("chance").getAsDouble(), 0.000001D);
|
assertEquals(1.0D - Math.exp(-(0.5D * 0.2D * 0.25D) / 256.0D), decorator.get("chance").getAsDouble(), 0.000001D);
|
||||||
assertEquals("minecraft:grass_block", decorator.getAsJsonArray("whitelist").get(0).getAsJsonObject().get("block").getAsString());
|
assertEquals("minecraft:grass_block", decorator.getAsJsonArray("whitelist").get(0).getAsJsonObject().get("block").getAsString());
|
||||||
@@ -167,8 +169,8 @@ public class FeatureConverterFormatTest {
|
|||||||
"{\"type\": \"minecraft:simple_block\", \"to_place\": \"minecraft:flower_plain\"}",
|
"{\"type\": \"minecraft:simple_block\", \"to_place\": \"minecraft:flower_plain\"}",
|
||||||
"[{\"type\": \"minecraft:heightmap\", \"heightmap\": \"MOTION_BLOCKING\"}]");
|
"[{\"type\": \"minecraft:heightmap\", \"heightmap\": \"MOTION_BLOCKING\"}]");
|
||||||
|
|
||||||
assertThrows(IngestConversionException.class, () -> PatchDecoratorConverter.convert(seagrass, index(), 0D));
|
assertThrows(IngestConversionException.class, () -> PatchDecoratorConverter.convert(seagrass, index(), VegetationOcclusion.Occlusion.NONE));
|
||||||
IngestConversionException reference = assertThrows(IngestConversionException.class, () -> PatchDecoratorConverter.convert(plains, index(), 0D));
|
IngestConversionException reference = assertThrows(IngestConversionException.class, () -> PatchDecoratorConverter.convert(plains, index(), VegetationOcclusion.Occlusion.NONE));
|
||||||
assertTrue(reference.getMessage(), reference.getMessage().contains("minecraft:flower_plain"));
|
assertTrue(reference.getMessage(), reference.getMessage().contains("minecraft:flower_plain"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ public class ModIngesterFeatureTest {
|
|||||||
assertNotNull(mapleFlowers);
|
assertNotNull(mapleFlowers);
|
||||||
assertNotNull(forestFlowers);
|
assertNotNull(forestFlowers);
|
||||||
assertEquals(2, forest.getAsJsonArray("decorators").size());
|
assertEquals(2, forest.getAsJsonArray("decorators").size());
|
||||||
double mapleCanopy = 1.0D - Math.exp(-CanopyEstimate.CROWN_COLUMNS / 256.0D);
|
double mapleCanopy = 1.0D - Math.exp(-VegetationOcclusion.CROWN_COLUMNS / 256.0D);
|
||||||
double openShare = 1.0D - mapleCanopy / 2.0D;
|
double openShare = 1.0D - mapleCanopy / 2.0D;
|
||||||
assertEquals(1.0D - Math.exp(-(1.0D / 4.0D * 32.0D * 0.25D * openShare) / 256.0D), forestFlowers.get("chance").getAsDouble(), 0.000001D);
|
assertEquals(1.0D - Math.exp(-(1.0D / 4.0D * 32.0D * 0.25D * openShare) / 256.0D), forestFlowers.get("chance").getAsDouble(), 0.000001D);
|
||||||
JsonArray palette = forestFlowers.getAsJsonArray("palette");
|
JsonArray palette = forestFlowers.getAsJsonArray("palette");
|
||||||
|
|||||||
+23
-7
@@ -33,26 +33,42 @@ import java.util.Set;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class CanopyEstimateTest {
|
public class VegetationOcclusionTest {
|
||||||
private static final double CANOPY = 1D - Math.exp(-10D * CanopyEstimate.CROWN_COLUMNS / 256D);
|
private static final double CANOPY = 1D - Math.exp(-10D * VegetationOcclusion.CROWN_COLUMNS / 256D);
|
||||||
|
private static final double GRASS_COVERAGE = 1D - Math.exp(-(24D * 32D * 0.25D) / 256D);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void flowersPlacedAfterTreesSeeTheTreeCanopy() {
|
public void flowersPlacedAfterTreesSeeTheTreeCanopy() {
|
||||||
CanopyEstimate estimate = new CanopyEstimate(WorldgenCatalogIndex.of(catalog()));
|
VegetationOcclusion estimate = new VegetationOcclusion(WorldgenCatalogIndex.of(catalog()));
|
||||||
|
|
||||||
assertEquals(CANOPY, estimate.occlusion("mod:flowers_after", Set.of("mod:woods")), 0.000001D);
|
assertEquals(CANOPY, estimate.occlusion("mod:flowers_after", Set.of("mod:woods")).canopy(), 0.000001D);
|
||||||
assertEquals(0D, estimate.occlusion("mod:flowers_before", Set.of("mod:woods")), 0.000001D);
|
assertEquals(0D, estimate.occlusion("mod:flowers_before", Set.of("mod:woods")).canopy(), 0.000001D);
|
||||||
assertEquals(CANOPY / 2D, estimate.occlusion("mod:flowers_after", Set.of("mod:woods", "mod:meadow")), 0.000001D);
|
assertEquals(CANOPY / 2D, estimate.occlusion("mod:flowers_after", Set.of("mod:woods", "mod:meadow")).canopy(), 0.000001D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void surfacePatchesPlacedEarlierOccupyTheirShareOfColumns() {
|
||||||
|
VegetationOcclusion estimate = new VegetationOcclusion(WorldgenCatalogIndex.of(catalog()));
|
||||||
|
|
||||||
|
assertEquals(GRASS_COVERAGE, estimate.occlusion("mod:flowers_after", Set.of("mod:woods")).occupancy(), 0.000001D);
|
||||||
|
assertEquals(0D, estimate.occlusion("mod:flowers_before", Set.of("mod:woods")).occupancy(), 0.000001D);
|
||||||
|
assertEquals(GRASS_COVERAGE / 2D, estimate.occlusion("mod:flowers_after", Set.of("mod:woods", "mod:meadow")).occupancy(), 0.000001D);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static WorldgenCatalog catalog() {
|
private static WorldgenCatalog catalog() {
|
||||||
List<CatalogBiome> biomes = List.of(
|
List<CatalogBiome> biomes = List.of(
|
||||||
biome("mod:woods", List.of("mod:flowers_before", "mod:oak_trees", "mod:flowers_after")),
|
biome("mod:woods", List.of("mod:flowers_before", "mod:oak_trees", "mod:grass_patch", "mod:flowers_after")),
|
||||||
biome("mod:meadow", List.of("mod:flowers_after")));
|
biome("mod:meadow", List.of("mod:flowers_after")));
|
||||||
List<CatalogPlacedFeature> features = new ArrayList<>();
|
List<CatalogPlacedFeature> features = new ArrayList<>();
|
||||||
features.add(new CatalogPlacedFeature("mod:oak_trees", "mod:oak_trees", "minecraft:tree", JsonParser.parseString("{}"),
|
features.add(new CatalogPlacedFeature("mod:oak_trees", "mod:oak_trees", "minecraft:tree", JsonParser.parseString("{}"),
|
||||||
JsonParser.parseString("[{\"type\": \"minecraft:count\", \"count\": 10}, {\"type\": \"minecraft:in_square\"},"
|
JsonParser.parseString("[{\"type\": \"minecraft:count\", \"count\": 10}, {\"type\": \"minecraft:in_square\"},"
|
||||||
+ "{\"type\": \"minecraft:surface_water_depth_filter\", \"max_water_depth\": 0}, {\"type\": \"minecraft:biome\"}]")));
|
+ "{\"type\": \"minecraft:surface_water_depth_filter\", \"max_water_depth\": 0}, {\"type\": \"minecraft:biome\"}]")));
|
||||||
|
features.add(new CatalogPlacedFeature("mod:grass_patch", "mod:grass_patch", "minecraft:simple_block",
|
||||||
|
JsonParser.parseString("{\"to_place\": {\"id\": \"minecraft:short_grass\"}}"),
|
||||||
|
JsonParser.parseString("[{\"type\": \"minecraft:count\", \"count\": 24}, {\"type\": \"minecraft:in_square\"},"
|
||||||
|
+ "{\"type\": \"minecraft:heightmap\", \"heightmap\": \"WORLD_SURFACE_WG\"}, {\"type\": \"minecraft:biome\"},"
|
||||||
|
+ "{\"type\": \"minecraft:count\", \"count\": 32},"
|
||||||
|
+ "{\"type\": \"minecraft:offset\", \"y\": {\"type\": \"minecraft:trapezoid\", \"min\": -3, \"max\": 3, \"plateau\": 0}}]")));
|
||||||
return new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1L, "canopy", List.of(new CatalogNamespace("mod", "Mod", "1")),
|
return new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1L, "canopy", List.of(new CatalogNamespace("mod", "Mod", "1")),
|
||||||
biomes, features, List.of(), List.of(), List.of(), Map.of());
|
biomes, features, List.of(), List.of(), List.of(), Map.of());
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user