Created
January 22, 2024 13:13
-
-
Save hYdos/b51f0fdeacbf7df6bb7d143cf327fead to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package me.hydos.naclmon.world.gen; | |
import com.mojang.serialization.Codec; | |
import com.mojang.serialization.codecs.RecordCodecBuilder; | |
import me.hydos.naclmon.world.gen.noise.OpenSimplexNoise; | |
import me.hydos.naclmon.world.gen.noise.VoronoiNoise; | |
import me.hydos.naclmon.world.gen.settings.RegionSettings; | |
import net.fabricmc.fabric.impl.biome.MultiNoiseSamplerHooks; | |
import net.minecraft.core.Holder; | |
import net.minecraft.util.RandomSource; | |
import net.minecraft.world.level.biome.Biome; | |
import net.minecraft.world.level.biome.BiomeManager; | |
import net.minecraft.world.level.biome.BiomeSource; | |
import net.minecraft.world.level.biome.Climate; | |
import net.minecraft.world.level.levelgen.XoroshiroRandomSource; | |
import org.joml.Vector2d; | |
import org.joml.Vector2i; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.stream.Stream; | |
/** | |
* What idiot at mojang thought limiting biomes to weather conditions was a good idea. Nice way to make me hack Random everywhere... | |
*/ | |
public class RegionBiomeSource extends BiomeSource implements BiomeManager.NoiseBiomeSource { | |
public static final Codec<RegionBiomeSource> CODEC = RecordCodecBuilder.create(instance -> instance.group( | |
Codec.DOUBLE.fieldOf("scale").forGetter(generator -> generator.scale), | |
Biome.CODEC.fieldOf("ocean").forGetter(generator -> generator.oceanBiome), | |
RegionSettings.CODEC.listOf().fieldOf("regions").forGetter(generator -> generator.regions) | |
).apply(instance, instance.stable(RegionBiomeSource::new))); | |
private final double scale; | |
public final Holder<Biome> oceanBiome; | |
private final List<RegionSettings> regions; | |
private OpenSimplexNoise regionNoise; | |
private OpenSimplexNoise commonBiomeNoise; | |
private VoronoiNoise voronoiNoise; | |
public RegionBiomeSource(double scale, Holder<Biome> oceanBiome, List<RegionSettings> regionSettings) { | |
this.scale = scale; | |
this.oceanBiome = oceanBiome; | |
this.regions = regionSettings; | |
} | |
@Override | |
protected Codec<? extends BiomeSource> codec() { | |
return CODEC; | |
} | |
@Override | |
protected Stream<Holder<Biome>> collectPossibleBiomes() { | |
var biomes = new ArrayList<Holder<Biome>>(); | |
for (var region : regions) biomes.addAll(region.commonBiomes()); | |
biomes.add(oceanBiome); | |
return biomes.stream(); | |
} | |
@Override | |
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { | |
// I believe this is called on multiple Threads. In case we don't have voronoi initialized in time initialize it on other threads too | |
if (voronoiNoise == null) { | |
// I can either do this myself, Or I can let FabricAPI do it. | |
//noinspection UnstableApiUsage | |
var random = new XoroshiroRandomSource(((MultiNoiseSamplerHooks) (Object) sampler).fabric_getSeed()); | |
this.regionNoise = new OpenSimplexNoise(random); | |
this.commonBiomeNoise = new OpenSimplexNoise(random); | |
this.voronoiNoise = new VoronoiNoise(random); | |
} | |
var sampleResult = voronoiNoise.distanceFromNearestCellSamplePoint(new Vector2d(x / scale, z / scale)); | |
if (sampleResult.distance() > 0.5) return oceanBiome; | |
else return pickRegionBiome(sampleResult.closestCell(), sampleResult.distance(), x, z); | |
} | |
private static <T> T sampleList(double positivelyBoundRandom, List<T> list) { | |
return list.get((int) (list.size() * positivelyBoundRandom)); | |
} | |
// TODO: include distance in this too for regions that are smaller like island like ones. | |
private Holder<Biome> pickRegionBiome(Vector2i cellPos, double distance, int x, int z) { | |
var region = sampleList(regionNoise.samplePositiveRange(cellPos.x, cellPos.y), regions); | |
if (distance > region.size()) return oceanBiome; | |
return sampleList(commonBiomeNoise.samplePositiveRange(x * 0.01, z * 0.01), region.commonBiomes()); | |
} | |
@Override | |
public Holder<Biome> getNoiseBiome(int x, int y, int z) { | |
// I believe this is called on multiple Threads. In case we don't have voronoi initialized in time initialize it on other threads too | |
if (voronoiNoise == null) throw new RuntimeException("No random available to sample noise"); | |
var sampleResult = voronoiNoise.distanceFromNearestCellSamplePoint(new Vector2d(x / scale, z / scale)); | |
if (sampleResult.distance() > 0.5) return oceanBiome; | |
else return pickRegionBiome(sampleResult.closestCell(), sampleResult.distance(), x, z); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment