Multi noise support for scarpet (#1176)
* fix: infinite params bug in AnnotationParser feat: scarpet function query_noise() * refactor: remove Method reflection * fix: use BiomeCoords * update: game version to 1.18-pre5 * feat: biome lookup from multinoise fix: noise lookup wrongly scaled * fix: jagged noise. Thanks, @Starmute * fix: cave cheese and layer. Thanks, @Starmute * fix: revert formatting fix: caveLayer and caveCheese feat: add terrain support * more formatting reverts * fix mapping issue * use BlockPos * docs: add documentation for query_noise * docs: add documentation for biome(noise_map)
This commit is contained in:
1 parent
6c7ac72c3e
commit
aa57b586cb
8 files changed
+388
-9
No files matched your search
@@ -0,0 +1,5 @@
|
||||
package carpet.fakes;
|
||||
|
||||
public interface NoiseColumnSamplerInterface {
|
||||
double getNoiseSample(String name, int x, int y, int z);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import net.minecraft.world.gen.NoiseColumnSampler;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
|
||||
@Mixin(NoiseColumnSampler.CaveScaler.class)
|
||||
public interface CaveScalerMixin_scarpetMixin {
|
||||
@Invoker
|
||||
static double invokeScaleTunnels(double d) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.NoiseColumnSamplerInterface;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import net.minecraft.util.math.noise.DoublePerlinNoiseSampler;
|
||||
import net.minecraft.util.math.noise.InterpolatedNoiseSampler;
|
||||
import net.minecraft.util.math.noise.SimplexNoiseSampler;
|
||||
import net.minecraft.world.biome.source.BiomeCoords;
|
||||
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
|
||||
import net.minecraft.world.biome.source.util.TerrainNoisePoint;
|
||||
import net.minecraft.world.gen.NoiseColumnSampler;
|
||||
import net.minecraft.world.gen.NoiseHelper;
|
||||
import net.minecraft.world.gen.chunk.Blender;
|
||||
import net.minecraft.world.gen.chunk.GenerationShapeConfig;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(NoiseColumnSampler.class)
|
||||
public abstract class NoiseColumnSamplerMixin_scarpetMixin implements NoiseColumnSamplerInterface {
|
||||
|
||||
@Shadow @Final
|
||||
private InterpolatedNoiseSampler terrainNoise;
|
||||
|
||||
@Shadow @Final @Nullable
|
||||
private SimplexNoiseSampler islandNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler aquiferBarrierNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler aquiferFluidLevelFloodednessNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler aquiferFluidLevelSpreadNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler aquiferLavaNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler pillarRarenessNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler pillarThicknessNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghetti2dElevationNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghetti2dModulatorNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghetti2dThicknessNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghetti3dFirstNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghetti3dSecondNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghetti3dRarityNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghetti3dThicknessNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler spaghettiRoughnessModulatorNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler caveCheeseNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private DoublePerlinNoiseSampler oreGapNoise;
|
||||
|
||||
@Shadow @Final
|
||||
private GenerationShapeConfig config;
|
||||
|
||||
@Shadow
|
||||
protected abstract double method_38409(double d, double e, double f);
|
||||
|
||||
@Shadow
|
||||
protected abstract double sampleCaveLayerNoise(int x, int y, int z);
|
||||
|
||||
@Shadow
|
||||
protected abstract double samplePillarNoise(int x, int y, int z);
|
||||
|
||||
@Shadow
|
||||
protected abstract double sampleSpaghetti2dNoise(int x, int y, int z);
|
||||
|
||||
@Shadow
|
||||
protected abstract double sampleSpaghetti3dNoise(int x, int y, int z);
|
||||
|
||||
@Shadow
|
||||
protected abstract double sampleSpaghettiRoughnessNoise(int x, int y, int z);
|
||||
|
||||
@Shadow
|
||||
protected abstract double sampleCaveEntranceNoise(int x, int y, int z);
|
||||
|
||||
@Shadow
|
||||
public abstract double sampleShiftNoise(int x, int y, int z);
|
||||
|
||||
@Shadow
|
||||
private static double sample(DoublePerlinNoiseSampler sampler, double x, double y, double z, double invertedScale) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public abstract MultiNoiseUtil.NoiseValuePoint sample(int i, int j, int k);
|
||||
|
||||
@Shadow
|
||||
public abstract TerrainNoisePoint createTerrainNoisePoint(int x, int z, float continentalness, float weirdness, float erosion, Blender blender);
|
||||
|
||||
@Override
|
||||
public double getNoiseSample(String name, int x, int y, int z) {
|
||||
MultiNoiseUtil.NoiseValuePoint noiseValuePoint = this.sample(x, y, z);
|
||||
TerrainNoisePoint terrainNoisePoint = createTerrainNoisePoint(
|
||||
x,
|
||||
z,
|
||||
MultiNoiseUtil.method_38666(noiseValuePoint.continentalnessNoise()),
|
||||
MultiNoiseUtil.method_38666(noiseValuePoint.weirdnessNoise()),
|
||||
MultiNoiseUtil.method_38666(noiseValuePoint.erosionNoise()),
|
||||
Blender.getNoBlending()
|
||||
);
|
||||
switch (name) {
|
||||
case "temperature" -> {
|
||||
return MultiNoiseUtil.method_38666(noiseValuePoint.temperatureNoise());
|
||||
}
|
||||
case "humidity" -> {
|
||||
return MultiNoiseUtil.method_38666(noiseValuePoint.humidityNoise());
|
||||
}
|
||||
case "continentalness" -> {
|
||||
return MultiNoiseUtil.method_38666(noiseValuePoint.continentalnessNoise());
|
||||
}
|
||||
case "erosion" -> {
|
||||
return MultiNoiseUtil.method_38666(noiseValuePoint.erosionNoise());
|
||||
}
|
||||
case "weirdness" -> {
|
||||
return MultiNoiseUtil.method_38666(noiseValuePoint.weirdnessNoise());
|
||||
}
|
||||
case "depth" -> {
|
||||
return MultiNoiseUtil.method_38666(noiseValuePoint.depth());
|
||||
}
|
||||
case "shiftX" -> {
|
||||
return x + this.sampleShiftNoise(x, 0, z);
|
||||
}
|
||||
case "shiftY" -> {
|
||||
return y + this.sampleShiftNoise(y, z, x);
|
||||
}
|
||||
case "shiftZ" -> {
|
||||
return z + this.sampleShiftNoise(z, x, 0);
|
||||
}
|
||||
case "terrain" -> {
|
||||
return this.terrainNoise.calculateNoise(x, y, z);
|
||||
}
|
||||
case "island" -> {
|
||||
return this.islandNoise == null ? 0 : this.islandNoise.sample(x, y, z);
|
||||
}
|
||||
case "jagged" -> {
|
||||
return this.method_38409(terrainNoisePoint.peaks(), x, z);
|
||||
}
|
||||
case "aquiferBarrier" -> {
|
||||
return this.aquiferBarrierNoise.sample(x, y, z);
|
||||
}
|
||||
case "aquiferFluidLevelFloodedness" -> {
|
||||
return this.aquiferFluidLevelFloodednessNoise.sample(x, y, z);
|
||||
}
|
||||
case "aquiferFluidLevelSpread" -> {
|
||||
return this.aquiferFluidLevelSpreadNoise.sample(x, y, z);
|
||||
}
|
||||
case "aquiferLava" -> {
|
||||
return this.aquiferLavaNoise.sample(x, y, z);
|
||||
}
|
||||
case "pillar" -> {
|
||||
return this.samplePillarNoise(x, y, z);
|
||||
}
|
||||
case "pillarRareness" -> {
|
||||
return NoiseHelper.lerpFromProgress(this.pillarRarenessNoise, x, y, z, 0.0D, 2.0D);
|
||||
}
|
||||
case "pillarThickness" -> {
|
||||
return NoiseHelper.lerpFromProgress(this.pillarThicknessNoise, x, y, z, 0.0D, 1.1D);
|
||||
}
|
||||
case "spaghetti2d" -> {
|
||||
return this.sampleSpaghetti2dNoise(x, y, z);
|
||||
}
|
||||
case "spaghetti2dElevation" -> {
|
||||
return NoiseHelper.lerpFromProgress(this.spaghetti2dElevationNoise, x, 0.0, z, this.config.minimumBlockY(), 8.0);
|
||||
}
|
||||
case "spaghetti2dModulator" -> {
|
||||
return this.spaghetti2dModulatorNoise.sample(x * 2, y, z * 2);
|
||||
}
|
||||
case "spaghetti2dThickness" -> {
|
||||
return NoiseHelper.lerpFromProgress(this.spaghetti2dThicknessNoise, x * 2, y, z * 2, 0.6, 1.3);
|
||||
}
|
||||
case "spaghetti3d" -> {
|
||||
return this.sampleSpaghetti3dNoise(x, y, z);
|
||||
}
|
||||
case "spaghetti3dFirst" -> {
|
||||
double d = this.spaghetti3dRarityNoise.sample(x * 2, y, z * 2);
|
||||
double e = CaveScalerMixin_scarpetMixin.invokeScaleTunnels(d);
|
||||
return sample(this.spaghetti3dFirstNoise, x, y, z, e);
|
||||
}
|
||||
case "spaghetti3dSecond" -> {
|
||||
double d = this.spaghetti3dRarityNoise.sample(x * 2, y, z * 2);
|
||||
double e = CaveScalerMixin_scarpetMixin.invokeScaleTunnels(d);
|
||||
return sample(this.spaghetti3dSecondNoise, x, y, z, e);
|
||||
}
|
||||
case "spaghetti3dRarity" -> {
|
||||
return this.spaghetti3dRarityNoise.sample(x * 2, y, z * 2);
|
||||
}
|
||||
case "spaghetti3dThickness" -> {
|
||||
return NoiseHelper.lerpFromProgress(this.spaghetti3dThicknessNoise, x, y, z, 0.065, 0.088);
|
||||
}
|
||||
case "spaghettiRoughness" -> {
|
||||
return this.sampleSpaghettiRoughnessNoise(x, y, z);
|
||||
}
|
||||
case "spaghettiRoughnessModulator" -> {
|
||||
return NoiseHelper.lerpFromProgress(this.spaghettiRoughnessModulatorNoise, x, y, z, 0.0, 0.1);
|
||||
}
|
||||
case "caveEntrance" -> {
|
||||
return this.sampleCaveEntranceNoise(x, y, z);
|
||||
}
|
||||
case "caveLayer" -> {
|
||||
// scaling them by 4 because it's sampled in normal coords
|
||||
return this.sampleCaveLayerNoise(
|
||||
BiomeCoords.toBlock(x),
|
||||
BiomeCoords.toBlock(y),
|
||||
BiomeCoords.toBlock(z)
|
||||
);
|
||||
}
|
||||
case "caveCheese" -> {
|
||||
// same reason as above
|
||||
return this.caveCheeseNoise.sample(
|
||||
BiomeCoords.toBlock(x),
|
||||
BiomeCoords.toBlock(y) / 1.5,
|
||||
BiomeCoords.toBlock(z)
|
||||
);
|
||||
}
|
||||
case "terrainPeaks" -> {
|
||||
return terrainNoisePoint.peaks();
|
||||
}
|
||||
case "terrainOffset" -> {
|
||||
return terrainNoisePoint.offset();
|
||||
}
|
||||
case "terrainFactor" -> {
|
||||
return terrainNoisePoint.factor();
|
||||
}
|
||||
case "oreGap" -> {
|
||||
return this.oreGapNoise.sample(x, y, z);
|
||||
}
|
||||
}
|
||||
throw new InternalExpressionException("Unknown noise type: " + name);
|
||||
}
|
||||
}
|
||||
@@ -189,11 +189,11 @@ public final class AnnotationParser
|
||||
if (maxParams == -2)
|
||||
throw new IllegalArgumentException("No maximum number of params specified for " + name + ", use -1 for unlimited. "
|
||||
+ "Provided in " + instance.getClass());
|
||||
if (maxParams == -1)
|
||||
maxParams = Integer.MAX_VALUE;
|
||||
if (maxParams < this.minParams)
|
||||
throw new IllegalArgumentException("Provided maximum number of params for " + name + " is smaller than method's param count."
|
||||
+ "Provided in " + instance.getClass());
|
||||
if (maxParams == -1)
|
||||
maxParams = Integer.MAX_VALUE;
|
||||
}
|
||||
this.maxParams = maxParams;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import carpet.CarpetSettings;
|
||||
import carpet.fakes.BiomeArrayInterface;
|
||||
import carpet.fakes.ChunkGeneratorInterface;
|
||||
import carpet.fakes.ChunkTicketManagerInterface;
|
||||
import carpet.fakes.NoiseColumnSamplerInterface;
|
||||
import carpet.fakes.ServerChunkManagerInterface;
|
||||
import carpet.fakes.ServerWorldInterface;
|
||||
import carpet.fakes.SpawnHelperInnerInterface;
|
||||
@@ -16,6 +17,8 @@ import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.Fluff;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.annotation.Locator;
|
||||
import carpet.script.annotation.ScarpetFunction;
|
||||
import carpet.script.argument.BlockArgument;
|
||||
import carpet.script.argument.Vector3Argument;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
@@ -93,6 +96,9 @@ import net.minecraft.world.SpawnHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.source.BiomeCoords;
|
||||
import net.minecraft.world.biome.source.BiomeSource;
|
||||
import net.minecraft.world.biome.source.MultiNoiseBiomeSource;
|
||||
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
@@ -221,6 +227,20 @@ public class WorldAccess {
|
||||
return bs;
|
||||
}
|
||||
|
||||
private static void nullCheck(Value v, String name) {
|
||||
if (v.isNull()) {
|
||||
throw new IllegalArgumentException(name + " cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
private static float numberGetOrThrow(Value v) {
|
||||
double num = v.readDoubleNumber();
|
||||
if (Double.isNaN(num)) {
|
||||
throw new IllegalArgumentException(v.getString() + " needs to be a numeric value");
|
||||
}
|
||||
return (float) num;
|
||||
}
|
||||
|
||||
private static void BooYah(ChunkGenerator generator)
|
||||
{
|
||||
synchronized (generator)
|
||||
@@ -343,7 +363,7 @@ public class WorldAccess {
|
||||
}
|
||||
String poiTypeString = poi.getString().toLowerCase(Locale.ROOT);
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.getOrEmpty(InputValidator.identifierOf(poiTypeString))
|
||||
.orElseThrow(() -> new ThrowStatement(poiTypeString, Throwables.UNKNOWN_POI));
|
||||
.orElseThrow(() -> new ThrowStatement(poiTypeString, Throwables.UNKNOWN_POI));
|
||||
int occupancy = 0;
|
||||
if (locator.offset + 1 < lv.size())
|
||||
{
|
||||
@@ -1129,19 +1149,55 @@ public class WorldAccess {
|
||||
return BooleanValue.of(blockLocator.block.getBlockState().isIn(blockTag));
|
||||
});
|
||||
|
||||
|
||||
expression.addContextFunction("biome", -1, (c, t, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
ServerWorld world = cc.s.getWorld();
|
||||
if (lv.size() == 0)
|
||||
return ListValue.wrap(world.getRegistryManager().get(Registry.BIOME_KEY).getIds().stream().map(ValueConversions::of));
|
||||
BlockArgument locator = BlockArgument.findIn(cc, lv, 0, false, false, true);
|
||||
|
||||
Biome biome;
|
||||
BiomeSource biomeSource = world.getChunkManager().getChunkGenerator().getBiomeSource();
|
||||
if ( lv.size() == 1
|
||||
&& lv.get(0) instanceof MapValue map
|
||||
&& biomeSource instanceof MultiNoiseBiomeSource mnbs
|
||||
) {
|
||||
Value temperature = map.get(new StringValue("temperature"));
|
||||
nullCheck(temperature, "temperature");
|
||||
|
||||
Value humidity = map.get(new StringValue("humidity"));
|
||||
nullCheck(humidity, "humidity");
|
||||
|
||||
Value continentalness = map.get(new StringValue("continentalness"));
|
||||
nullCheck(continentalness, "continentalness");
|
||||
|
||||
Value erosion = map.get(new StringValue("erosion"));
|
||||
nullCheck(erosion, "erosion");
|
||||
|
||||
Value depth = map.get(new StringValue("depth"));
|
||||
nullCheck(depth, "depth");
|
||||
|
||||
Value weirdness = map.get(new StringValue("weirdness"));
|
||||
nullCheck(weirdness, "weirdness");
|
||||
|
||||
MultiNoiseUtil.NoiseValuePoint point = new MultiNoiseUtil.NoiseValuePoint(
|
||||
MultiNoiseUtil.method_38665(numberGetOrThrow(temperature)),
|
||||
MultiNoiseUtil.method_38665(numberGetOrThrow(humidity)),
|
||||
MultiNoiseUtil.method_38665(numberGetOrThrow(continentalness)),
|
||||
MultiNoiseUtil.method_38665(numberGetOrThrow(erosion)),
|
||||
MultiNoiseUtil.method_38665(numberGetOrThrow(depth)),
|
||||
MultiNoiseUtil.method_38665(numberGetOrThrow(weirdness))
|
||||
);
|
||||
biome = mnbs.getBiomeAtPoint(point);
|
||||
Identifier biomeId = cc.s.getServer().getRegistryManager().get(Registry.BIOME_KEY).getId(biome);
|
||||
return new StringValue(NBTSerializableValue.nameFromRegistryId(biomeId));
|
||||
}
|
||||
|
||||
BlockArgument locator = BlockArgument.findIn(cc, lv, 0, false, false, true);
|
||||
|
||||
if (locator.replacement != null)
|
||||
{
|
||||
biome = world.getRegistryManager().get(Registry.BIOME_KEY).get(InputValidator.identifierOf(locator.replacement));
|
||||
if (biome == null) throw new ThrowStatement(locator.replacement, Throwables.UNKNOWN_BIOME) ;
|
||||
if (biome == null) throw new ThrowStatement(locator.replacement, Throwables.UNKNOWN_BIOME);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1156,7 +1212,8 @@ public class WorldAccess {
|
||||
}
|
||||
String biomeFeature = lv.get(locator.offset).getString();
|
||||
BiFunction<ServerWorld, Biome, Value> featureProvider = BiomeInfo.biomeFeatures.get(biomeFeature);
|
||||
if (featureProvider == null) throw new InternalExpressionException("Unknown biome feature: "+biomeFeature);
|
||||
if (featureProvider == null)
|
||||
throw new InternalExpressionException("Unknown biome feature: " + biomeFeature);
|
||||
return featureProvider.apply(world, biome);
|
||||
});
|
||||
|
||||
@@ -1512,4 +1569,23 @@ public class WorldAccess {
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ScarpetFunction(maxParams = -1)
|
||||
public Value query_noise(Context c, @Locator.Block BlockPos pos, String... noiseQueries) {
|
||||
int mappedX = BiomeCoords.fromBlock(pos.getX());
|
||||
int mappedY = BiomeCoords.fromBlock(pos.getY());
|
||||
int mappedZ = BiomeCoords.fromBlock(pos.getZ());
|
||||
MultiNoiseUtil.MultiNoiseSampler mns = ((CarpetContext) c).s.getWorld().getChunkManager().getChunkGenerator().getMultiNoiseSampler();
|
||||
Map<Value, Value> ret = new HashMap<>();
|
||||
|
||||
if (noiseQueries.length == 0) {
|
||||
noiseQueries = new String[]{"continentalness", "erosion", "weirdness", "temperature", "humidity", "depth"};
|
||||
}
|
||||
|
||||
for (String noise : noiseQueries) {
|
||||
double noiseValue = ((NoiseColumnSamplerInterface) mns).getNoiseSample(noise, mappedX, mappedY, mappedZ);
|
||||
ret.put(new StringValue(noise), new NumericValue(noiseValue));
|
||||
}
|
||||
return MapValue.wrap(ret);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user