781 lines
31 KiB
Java
781 lines
31 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Lists
|
|
* it.unimi.dsi.fastutil.longs.Long2IntMap
|
|
* it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.world.level.levelgen;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import net.minecraft.core.QuartPos;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.server.level.ColumnPos;
|
|
import net.minecraft.util.KeyDispatchDataCodec;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.biome.Climate;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.levelgen.Aquifer;
|
|
import net.minecraft.world.level.levelgen.DensityFunction;
|
|
import net.minecraft.world.level.levelgen.DensityFunctions;
|
|
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
|
import net.minecraft.world.level.levelgen.NoiseRouter;
|
|
import net.minecraft.world.level.levelgen.NoiseSettings;
|
|
import net.minecraft.world.level.levelgen.OreVeinifier;
|
|
import net.minecraft.world.level.levelgen.RandomState;
|
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
|
import net.minecraft.world.level.levelgen.material.MaterialRuleList;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public class NoiseChunk
|
|
implements DensityFunction.FunctionContext,
|
|
DensityFunction.ContextProvider {
|
|
private final NoiseSettings noiseSettings;
|
|
private final int cellCountXZ;
|
|
private final int cellCountY;
|
|
private final int cellNoiseMinY;
|
|
private final int firstCellX;
|
|
private final int firstCellZ;
|
|
private final int firstNoiseX;
|
|
private final int firstNoiseZ;
|
|
private final List<NoiseInterpolator> interpolators;
|
|
private final List<CacheAllInCell> cellCaches;
|
|
private final Map<DensityFunction, DensityFunction> wrapped = new HashMap<DensityFunction, DensityFunction>();
|
|
private final Long2IntMap preliminarySurfaceLevelCache = new Long2IntOpenHashMap();
|
|
private final Aquifer aquifer;
|
|
private final DensityFunction preliminarySurfaceLevel;
|
|
private final BlockStateFiller blockStateRule;
|
|
private final Blender blender;
|
|
private final FlatCache blendAlpha;
|
|
private final FlatCache blendOffset;
|
|
private final DensityFunctions.BeardifierOrMarker beardifier;
|
|
private long lastBlendingDataPos = ChunkPos.INVALID_CHUNK_POS;
|
|
private Blender.BlendingOutput lastBlendingOutput = new Blender.BlendingOutput(1.0, 0.0);
|
|
private final int noiseSizeXZ;
|
|
private final int cellWidth;
|
|
private final int cellHeight;
|
|
private boolean interpolating;
|
|
private boolean fillingCell;
|
|
private int cellStartBlockX;
|
|
private int cellStartBlockY;
|
|
private int cellStartBlockZ;
|
|
private int inCellX;
|
|
private int inCellY;
|
|
private int inCellZ;
|
|
private long interpolationCounter;
|
|
private long arrayInterpolationCounter;
|
|
private int arrayIndex;
|
|
private final DensityFunction.ContextProvider sliceFillingContextProvider = new DensityFunction.ContextProvider(){
|
|
|
|
@Override
|
|
public DensityFunction.FunctionContext forIndex(int cellYIndex) {
|
|
NoiseChunk.this.cellStartBlockY = (cellYIndex + NoiseChunk.this.cellNoiseMinY) * NoiseChunk.this.cellHeight;
|
|
++NoiseChunk.this.interpolationCounter;
|
|
NoiseChunk.this.inCellY = 0;
|
|
NoiseChunk.this.arrayIndex = cellYIndex;
|
|
return NoiseChunk.this;
|
|
}
|
|
|
|
@Override
|
|
public void fillAllDirectly(double[] output, DensityFunction function) {
|
|
for (int cellYIndex = 0; cellYIndex < NoiseChunk.this.cellCountY + 1; ++cellYIndex) {
|
|
NoiseChunk.this.cellStartBlockY = (cellYIndex + NoiseChunk.this.cellNoiseMinY) * NoiseChunk.this.cellHeight;
|
|
++NoiseChunk.this.interpolationCounter;
|
|
NoiseChunk.this.inCellY = 0;
|
|
NoiseChunk.this.arrayIndex = cellYIndex;
|
|
output[cellYIndex] = function.compute(NoiseChunk.this);
|
|
}
|
|
}
|
|
};
|
|
|
|
public static NoiseChunk forChunk(ChunkAccess chunk, RandomState randomState, DensityFunctions.BeardifierOrMarker beardifier, NoiseGeneratorSettings settings, Aquifer.FluidPicker globalFluidPicker, Blender blender) {
|
|
NoiseSettings noiseSettings = settings.noiseSettings().clampToHeightAccessor(chunk);
|
|
ChunkPos pos = chunk.getPos();
|
|
int cellCountXZ = 16 / noiseSettings.getCellWidth();
|
|
return new NoiseChunk(cellCountXZ, randomState, pos.getMinBlockX(), pos.getMinBlockZ(), noiseSettings, beardifier, settings, globalFluidPicker, blender);
|
|
}
|
|
|
|
public NoiseChunk(int cellCountXZ, RandomState randomState, int chunkMinBlockX, int chunkMinBlockZ, NoiseSettings noiseSettings, DensityFunctions.BeardifierOrMarker beardifier, NoiseGeneratorSettings settings, Aquifer.FluidPicker globalFluidPicker, Blender blender) {
|
|
this.noiseSettings = noiseSettings;
|
|
this.cellWidth = noiseSettings.getCellWidth();
|
|
this.cellHeight = noiseSettings.getCellHeight();
|
|
this.cellCountXZ = cellCountXZ;
|
|
this.cellCountY = Mth.floorDiv(noiseSettings.height(), this.cellHeight);
|
|
this.cellNoiseMinY = Mth.floorDiv(noiseSettings.minY(), this.cellHeight);
|
|
this.firstCellX = Math.floorDiv(chunkMinBlockX, this.cellWidth);
|
|
this.firstCellZ = Math.floorDiv(chunkMinBlockZ, this.cellWidth);
|
|
this.interpolators = Lists.newArrayList();
|
|
this.cellCaches = Lists.newArrayList();
|
|
this.firstNoiseX = QuartPos.fromBlock(chunkMinBlockX);
|
|
this.firstNoiseZ = QuartPos.fromBlock(chunkMinBlockZ);
|
|
this.noiseSizeXZ = QuartPos.fromBlock(cellCountXZ * this.cellWidth);
|
|
this.blender = blender;
|
|
this.beardifier = beardifier;
|
|
this.blendAlpha = new FlatCache(new BlendAlpha(), false);
|
|
this.blendOffset = new FlatCache(new BlendOffset(), false);
|
|
if (!blender.isEmpty()) {
|
|
for (int x = 0; x <= this.noiseSizeXZ; ++x) {
|
|
int quartX = this.firstNoiseX + x;
|
|
int blockX = QuartPos.toBlock(quartX);
|
|
for (int z = 0; z <= this.noiseSizeXZ; ++z) {
|
|
int quartZ = this.firstNoiseZ + z;
|
|
int blockZ = QuartPos.toBlock(quartZ);
|
|
Blender.BlendingOutput blendingOutput = blender.blendOffsetAndFactor(blockX, blockZ);
|
|
this.blendAlpha.values[x + z * this.blendAlpha.sizeXZ] = blendingOutput.alpha();
|
|
this.blendOffset.values[x + z * this.blendOffset.sizeXZ] = blendingOutput.blendingOffset();
|
|
}
|
|
}
|
|
} else {
|
|
Arrays.fill(this.blendAlpha.values, 1.0);
|
|
Arrays.fill(this.blendOffset.values, 0.0);
|
|
}
|
|
NoiseRouter router = randomState.router();
|
|
NoiseRouter wrappedRouter = router.mapAll(this::wrap);
|
|
this.preliminarySurfaceLevel = wrappedRouter.preliminarySurfaceLevel();
|
|
if (!settings.isAquifersEnabled()) {
|
|
this.aquifer = Aquifer.createDisabled(globalFluidPicker);
|
|
} else {
|
|
int chunkX = SectionPos.blockToSectionCoord(chunkMinBlockX);
|
|
int chunkZ = SectionPos.blockToSectionCoord(chunkMinBlockZ);
|
|
this.aquifer = Aquifer.create(this, new ChunkPos(chunkX, chunkZ), wrappedRouter, randomState.aquiferRandom(), noiseSettings.minY(), noiseSettings.height(), globalFluidPicker);
|
|
}
|
|
ArrayList<BlockStateFiller> builder = new ArrayList<BlockStateFiller>();
|
|
DensityFunction fullNoiseValue = DensityFunctions.cacheAllInCell(DensityFunctions.add(wrappedRouter.finalDensity(), DensityFunctions.BeardifierMarker.INSTANCE)).mapAll(this::wrap);
|
|
builder.add(context -> this.aquifer.computeSubstance(context, fullNoiseValue.compute(context)));
|
|
if (settings.oreVeinsEnabled()) {
|
|
builder.add(OreVeinifier.create(wrappedRouter.veinToggle(), wrappedRouter.veinRidged(), wrappedRouter.veinGap(), randomState.oreRandom()));
|
|
}
|
|
this.blockStateRule = new MaterialRuleList(builder.toArray(new BlockStateFiller[0]));
|
|
}
|
|
|
|
protected Climate.Sampler cachedClimateSampler(NoiseRouter noises, List<Climate.ParameterPoint> spawnTarget) {
|
|
return new Climate.Sampler(noises.temperature().mapAll(this::wrap), noises.vegetation().mapAll(this::wrap), noises.continents().mapAll(this::wrap), noises.erosion().mapAll(this::wrap), noises.depth().mapAll(this::wrap), noises.ridges().mapAll(this::wrap), spawnTarget);
|
|
}
|
|
|
|
protected @Nullable BlockState getInterpolatedState() {
|
|
return this.blockStateRule.calculate(this);
|
|
}
|
|
|
|
@Override
|
|
public int blockX() {
|
|
return this.cellStartBlockX + this.inCellX;
|
|
}
|
|
|
|
@Override
|
|
public int blockY() {
|
|
return this.cellStartBlockY + this.inCellY;
|
|
}
|
|
|
|
@Override
|
|
public int blockZ() {
|
|
return this.cellStartBlockZ + this.inCellZ;
|
|
}
|
|
|
|
public int maxPreliminarySurfaceLevel(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) {
|
|
int maxY = Integer.MIN_VALUE;
|
|
for (int blockZ = minBlockZ; blockZ <= maxBlockZ; blockZ += 4) {
|
|
for (int blockX = minBlockX; blockX <= maxBlockX; blockX += 4) {
|
|
int surfaceLevel = this.preliminarySurfaceLevel(blockX, blockZ);
|
|
if (surfaceLevel <= maxY) continue;
|
|
maxY = surfaceLevel;
|
|
}
|
|
}
|
|
return maxY;
|
|
}
|
|
|
|
public int preliminarySurfaceLevel(int sampleX, int sampleZ) {
|
|
int quantizedX = QuartPos.toBlock(QuartPos.fromBlock(sampleX));
|
|
int quantizedZ = QuartPos.toBlock(QuartPos.fromBlock(sampleZ));
|
|
return this.preliminarySurfaceLevelCache.computeIfAbsent(ColumnPos.asLong(quantizedX, quantizedZ), this::computePreliminarySurfaceLevel);
|
|
}
|
|
|
|
private int computePreliminarySurfaceLevel(long key) {
|
|
int blockX = ColumnPos.getX(key);
|
|
int blockZ = ColumnPos.getZ(key);
|
|
return Mth.floor(this.preliminarySurfaceLevel.compute(new DensityFunction.SinglePointContext(blockX, 0, blockZ)));
|
|
}
|
|
|
|
@Override
|
|
public Blender getBlender() {
|
|
return this.blender;
|
|
}
|
|
|
|
private void fillSlice(boolean slice0, int cellX) {
|
|
this.cellStartBlockX = cellX * this.cellWidth;
|
|
this.inCellX = 0;
|
|
for (int cellZIndex = 0; cellZIndex < this.cellCountXZ + 1; ++cellZIndex) {
|
|
int cellZ = this.firstCellZ + cellZIndex;
|
|
this.cellStartBlockZ = cellZ * this.cellWidth;
|
|
this.inCellZ = 0;
|
|
++this.arrayInterpolationCounter;
|
|
for (NoiseInterpolator noiseInterpolator : this.interpolators) {
|
|
double[] slice = (slice0 ? noiseInterpolator.slice0 : noiseInterpolator.slice1)[cellZIndex];
|
|
noiseInterpolator.fillArray(slice, this.sliceFillingContextProvider);
|
|
}
|
|
}
|
|
++this.arrayInterpolationCounter;
|
|
}
|
|
|
|
public void initializeForFirstCellX() {
|
|
if (this.interpolating) {
|
|
throw new IllegalStateException("Staring interpolation twice");
|
|
}
|
|
this.interpolating = true;
|
|
this.interpolationCounter = 0L;
|
|
this.fillSlice(true, this.firstCellX);
|
|
}
|
|
|
|
public void advanceCellX(int cellXIndex) {
|
|
this.fillSlice(false, this.firstCellX + cellXIndex + 1);
|
|
this.cellStartBlockX = (this.firstCellX + cellXIndex) * this.cellWidth;
|
|
}
|
|
|
|
@Override
|
|
public NoiseChunk forIndex(int cellIndex) {
|
|
int zInCell = Math.floorMod(cellIndex, this.cellWidth);
|
|
int xyIndex = Math.floorDiv(cellIndex, this.cellWidth);
|
|
int xInCell = Math.floorMod(xyIndex, this.cellWidth);
|
|
int yInCell = this.cellHeight - 1 - Math.floorDiv(xyIndex, this.cellWidth);
|
|
this.inCellX = xInCell;
|
|
this.inCellY = yInCell;
|
|
this.inCellZ = zInCell;
|
|
this.arrayIndex = cellIndex;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public void fillAllDirectly(double[] output, DensityFunction function) {
|
|
this.arrayIndex = 0;
|
|
for (int yInCell = this.cellHeight - 1; yInCell >= 0; --yInCell) {
|
|
this.inCellY = yInCell;
|
|
for (int xInCell = 0; xInCell < this.cellWidth; ++xInCell) {
|
|
this.inCellX = xInCell;
|
|
int zInCell = 0;
|
|
while (zInCell < this.cellWidth) {
|
|
this.inCellZ = zInCell++;
|
|
output[this.arrayIndex++] = function.compute(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void selectCellYZ(int cellYIndex, int cellZIndex) {
|
|
for (NoiseInterpolator i : this.interpolators) {
|
|
i.selectCellYZ(cellYIndex, cellZIndex);
|
|
}
|
|
this.fillingCell = true;
|
|
this.cellStartBlockY = (cellYIndex + this.cellNoiseMinY) * this.cellHeight;
|
|
this.cellStartBlockZ = (this.firstCellZ + cellZIndex) * this.cellWidth;
|
|
++this.arrayInterpolationCounter;
|
|
for (CacheAllInCell cellCache : this.cellCaches) {
|
|
cellCache.noiseFiller.fillArray(cellCache.values, this);
|
|
}
|
|
++this.arrayInterpolationCounter;
|
|
this.fillingCell = false;
|
|
}
|
|
|
|
public void updateForY(int posY, double factorY) {
|
|
this.inCellY = posY - this.cellStartBlockY;
|
|
for (NoiseInterpolator i : this.interpolators) {
|
|
i.updateForY(factorY);
|
|
}
|
|
}
|
|
|
|
public void updateForX(int posX, double factorX) {
|
|
this.inCellX = posX - this.cellStartBlockX;
|
|
for (NoiseInterpolator i : this.interpolators) {
|
|
i.updateForX(factorX);
|
|
}
|
|
}
|
|
|
|
public void updateForZ(int posZ, double factorZ) {
|
|
this.inCellZ = posZ - this.cellStartBlockZ;
|
|
++this.interpolationCounter;
|
|
for (NoiseInterpolator i : this.interpolators) {
|
|
i.updateForZ(factorZ);
|
|
}
|
|
}
|
|
|
|
public void stopInterpolation() {
|
|
if (!this.interpolating) {
|
|
throw new IllegalStateException("Staring interpolation twice");
|
|
}
|
|
this.interpolating = false;
|
|
}
|
|
|
|
public void swapSlices() {
|
|
this.interpolators.forEach(NoiseInterpolator::swapSlices);
|
|
}
|
|
|
|
public Aquifer aquifer() {
|
|
return this.aquifer;
|
|
}
|
|
|
|
protected int cellWidth() {
|
|
return this.cellWidth;
|
|
}
|
|
|
|
protected int cellHeight() {
|
|
return this.cellHeight;
|
|
}
|
|
|
|
private Blender.BlendingOutput getOrComputeBlendingOutput(int blockX, int blockZ) {
|
|
Blender.BlendingOutput output;
|
|
long pos2D = ChunkPos.asLong(blockX, blockZ);
|
|
if (this.lastBlendingDataPos == pos2D) {
|
|
return this.lastBlendingOutput;
|
|
}
|
|
this.lastBlendingDataPos = pos2D;
|
|
this.lastBlendingOutput = output = this.blender.blendOffsetAndFactor(blockX, blockZ);
|
|
return output;
|
|
}
|
|
|
|
protected DensityFunction wrap(DensityFunction function) {
|
|
return this.wrapped.computeIfAbsent(function, this::wrapNew);
|
|
}
|
|
|
|
private DensityFunction wrapNew(DensityFunction function) {
|
|
if (function instanceof DensityFunctions.Marker) {
|
|
DensityFunctions.Marker marker = (DensityFunctions.Marker)function;
|
|
return switch (marker.type()) {
|
|
default -> throw new MatchException(null, null);
|
|
case DensityFunctions.Marker.Type.Interpolated -> new NoiseInterpolator(marker.wrapped());
|
|
case DensityFunctions.Marker.Type.FlatCache -> new FlatCache(marker.wrapped(), true);
|
|
case DensityFunctions.Marker.Type.Cache2D -> new Cache2D(marker.wrapped());
|
|
case DensityFunctions.Marker.Type.CacheOnce -> new CacheOnce(marker.wrapped());
|
|
case DensityFunctions.Marker.Type.CacheAllInCell -> new CacheAllInCell(marker.wrapped());
|
|
};
|
|
}
|
|
if (this.blender != Blender.empty()) {
|
|
if (function == DensityFunctions.BlendAlpha.INSTANCE) {
|
|
return this.blendAlpha;
|
|
}
|
|
if (function == DensityFunctions.BlendOffset.INSTANCE) {
|
|
return this.blendOffset;
|
|
}
|
|
}
|
|
if (function == DensityFunctions.BeardifierMarker.INSTANCE) {
|
|
return this.beardifier;
|
|
}
|
|
if (function instanceof DensityFunctions.HolderHolder) {
|
|
DensityFunctions.HolderHolder holder = (DensityFunctions.HolderHolder)function;
|
|
return holder.function().value();
|
|
}
|
|
return function;
|
|
}
|
|
|
|
private class FlatCache
|
|
implements NoiseChunkDensityFunction,
|
|
DensityFunctions.MarkerOrMarked {
|
|
private final DensityFunction noiseFiller;
|
|
private final double[] values;
|
|
private final int sizeXZ;
|
|
|
|
private FlatCache(DensityFunction noiseFiller, boolean fill) {
|
|
this.noiseFiller = noiseFiller;
|
|
this.sizeXZ = NoiseChunk.this.noiseSizeXZ + 1;
|
|
this.values = new double[this.sizeXZ * this.sizeXZ];
|
|
if (fill) {
|
|
for (int x = 0; x <= NoiseChunk.this.noiseSizeXZ; ++x) {
|
|
int quartX = NoiseChunk.this.firstNoiseX + x;
|
|
int blockX = QuartPos.toBlock(quartX);
|
|
for (int z = 0; z <= NoiseChunk.this.noiseSizeXZ; ++z) {
|
|
int quartZ = NoiseChunk.this.firstNoiseZ + z;
|
|
int blockZ = QuartPos.toBlock(quartZ);
|
|
this.values[x + z * this.sizeXZ] = noiseFiller.compute(new DensityFunction.SinglePointContext(blockX, 0, blockZ));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
int quartX = QuartPos.fromBlock(context.blockX());
|
|
int quartZ = QuartPos.fromBlock(context.blockZ());
|
|
int x = quartX - NoiseChunk.this.firstNoiseX;
|
|
int z = quartZ - NoiseChunk.this.firstNoiseZ;
|
|
if (x >= 0 && z >= 0 && x < this.sizeXZ && z < this.sizeXZ) {
|
|
return this.values[x + z * this.sizeXZ];
|
|
}
|
|
return this.noiseFiller.compute(context);
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(output, this);
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.noiseFiller;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunctions.Marker.Type type() {
|
|
return DensityFunctions.Marker.Type.FlatCache;
|
|
}
|
|
}
|
|
|
|
private class BlendAlpha
|
|
implements NoiseChunkDensityFunction {
|
|
private BlendAlpha() {
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return DensityFunctions.BlendAlpha.INSTANCE;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
|
|
return this.wrapped().mapAll(visitor);
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
return NoiseChunk.this.getOrComputeBlendingOutput(context.blockX(), context.blockZ()).alpha();
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(output, this);
|
|
}
|
|
|
|
@Override
|
|
public double minValue() {
|
|
return 0.0;
|
|
}
|
|
|
|
@Override
|
|
public double maxValue() {
|
|
return 1.0;
|
|
}
|
|
|
|
@Override
|
|
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
|
|
return DensityFunctions.BlendAlpha.CODEC;
|
|
}
|
|
}
|
|
|
|
private class BlendOffset
|
|
implements NoiseChunkDensityFunction {
|
|
private BlendOffset() {
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return DensityFunctions.BlendOffset.INSTANCE;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction mapAll(DensityFunction.Visitor visitor) {
|
|
return this.wrapped().mapAll(visitor);
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
return NoiseChunk.this.getOrComputeBlendingOutput(context.blockX(), context.blockZ()).blendingOffset();
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(output, this);
|
|
}
|
|
|
|
@Override
|
|
public double minValue() {
|
|
return Double.NEGATIVE_INFINITY;
|
|
}
|
|
|
|
@Override
|
|
public double maxValue() {
|
|
return Double.POSITIVE_INFINITY;
|
|
}
|
|
|
|
@Override
|
|
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
|
|
return DensityFunctions.BlendOffset.CODEC;
|
|
}
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public static interface BlockStateFiller {
|
|
public @Nullable BlockState calculate(DensityFunction.FunctionContext var1);
|
|
}
|
|
|
|
public class NoiseInterpolator
|
|
implements NoiseChunkDensityFunction,
|
|
DensityFunctions.MarkerOrMarked {
|
|
private double[][] slice0;
|
|
private double[][] slice1;
|
|
private final DensityFunction noiseFiller;
|
|
private double noise000;
|
|
private double noise001;
|
|
private double noise100;
|
|
private double noise101;
|
|
private double noise010;
|
|
private double noise011;
|
|
private double noise110;
|
|
private double noise111;
|
|
private double valueXZ00;
|
|
private double valueXZ10;
|
|
private double valueXZ01;
|
|
private double valueXZ11;
|
|
private double valueZ0;
|
|
private double valueZ1;
|
|
private double value;
|
|
|
|
private NoiseInterpolator(DensityFunction noiseFiller) {
|
|
this.noiseFiller = noiseFiller;
|
|
this.slice0 = this.allocateSlice(NoiseChunk.this.cellCountY, NoiseChunk.this.cellCountXZ);
|
|
this.slice1 = this.allocateSlice(NoiseChunk.this.cellCountY, NoiseChunk.this.cellCountXZ);
|
|
NoiseChunk.this.interpolators.add(this);
|
|
}
|
|
|
|
private double[][] allocateSlice(int cellCountY, int cellCountZ) {
|
|
int sizeZ = cellCountZ + 1;
|
|
int sizeY = cellCountY + 1;
|
|
double[][] result = new double[sizeZ][sizeY];
|
|
for (int cellZIndex = 0; cellZIndex < sizeZ; ++cellZIndex) {
|
|
result[cellZIndex] = new double[sizeY];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private void selectCellYZ(int cellYIndex, int cellZIndex) {
|
|
this.noise000 = this.slice0[cellZIndex][cellYIndex];
|
|
this.noise001 = this.slice0[cellZIndex + 1][cellYIndex];
|
|
this.noise100 = this.slice1[cellZIndex][cellYIndex];
|
|
this.noise101 = this.slice1[cellZIndex + 1][cellYIndex];
|
|
this.noise010 = this.slice0[cellZIndex][cellYIndex + 1];
|
|
this.noise011 = this.slice0[cellZIndex + 1][cellYIndex + 1];
|
|
this.noise110 = this.slice1[cellZIndex][cellYIndex + 1];
|
|
this.noise111 = this.slice1[cellZIndex + 1][cellYIndex + 1];
|
|
}
|
|
|
|
private void updateForY(double factorY) {
|
|
this.valueXZ00 = Mth.lerp(factorY, this.noise000, this.noise010);
|
|
this.valueXZ10 = Mth.lerp(factorY, this.noise100, this.noise110);
|
|
this.valueXZ01 = Mth.lerp(factorY, this.noise001, this.noise011);
|
|
this.valueXZ11 = Mth.lerp(factorY, this.noise101, this.noise111);
|
|
}
|
|
|
|
private void updateForX(double factorX) {
|
|
this.valueZ0 = Mth.lerp(factorX, this.valueXZ00, this.valueXZ10);
|
|
this.valueZ1 = Mth.lerp(factorX, this.valueXZ01, this.valueXZ11);
|
|
}
|
|
|
|
private void updateForZ(double factorZ) {
|
|
this.value = Mth.lerp(factorZ, this.valueZ0, this.valueZ1);
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
if (context != NoiseChunk.this) {
|
|
return this.noiseFiller.compute(context);
|
|
}
|
|
if (!NoiseChunk.this.interpolating) {
|
|
throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
}
|
|
if (NoiseChunk.this.fillingCell) {
|
|
return Mth.lerp3((double)NoiseChunk.this.inCellX / (double)NoiseChunk.this.cellWidth, (double)NoiseChunk.this.inCellY / (double)NoiseChunk.this.cellHeight, (double)NoiseChunk.this.inCellZ / (double)NoiseChunk.this.cellWidth, this.noise000, this.noise100, this.noise010, this.noise110, this.noise001, this.noise101, this.noise011, this.noise111);
|
|
}
|
|
return this.value;
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) {
|
|
if (NoiseChunk.this.fillingCell) {
|
|
contextProvider.fillAllDirectly(output, this);
|
|
return;
|
|
}
|
|
this.wrapped().fillArray(output, contextProvider);
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.noiseFiller;
|
|
}
|
|
|
|
private void swapSlices() {
|
|
double[][] tmp = this.slice0;
|
|
this.slice0 = this.slice1;
|
|
this.slice1 = tmp;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunctions.Marker.Type type() {
|
|
return DensityFunctions.Marker.Type.Interpolated;
|
|
}
|
|
}
|
|
|
|
private class CacheAllInCell
|
|
implements NoiseChunkDensityFunction,
|
|
DensityFunctions.MarkerOrMarked {
|
|
private final DensityFunction noiseFiller;
|
|
private final double[] values;
|
|
|
|
private CacheAllInCell(DensityFunction noiseFiller) {
|
|
this.noiseFiller = noiseFiller;
|
|
this.values = new double[NoiseChunk.this.cellWidth * NoiseChunk.this.cellWidth * NoiseChunk.this.cellHeight];
|
|
NoiseChunk.this.cellCaches.add(this);
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
if (context != NoiseChunk.this) {
|
|
return this.noiseFiller.compute(context);
|
|
}
|
|
if (!NoiseChunk.this.interpolating) {
|
|
throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
}
|
|
int x = NoiseChunk.this.inCellX;
|
|
int y = NoiseChunk.this.inCellY;
|
|
int z = NoiseChunk.this.inCellZ;
|
|
if (x >= 0 && y >= 0 && z >= 0 && x < NoiseChunk.this.cellWidth && y < NoiseChunk.this.cellHeight && z < NoiseChunk.this.cellWidth) {
|
|
return this.values[((NoiseChunk.this.cellHeight - 1 - y) * NoiseChunk.this.cellWidth + x) * NoiseChunk.this.cellWidth + z];
|
|
}
|
|
return this.noiseFiller.compute(context);
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(output, this);
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.noiseFiller;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunctions.Marker.Type type() {
|
|
return DensityFunctions.Marker.Type.CacheAllInCell;
|
|
}
|
|
}
|
|
|
|
private static class Cache2D
|
|
implements NoiseChunkDensityFunction,
|
|
DensityFunctions.MarkerOrMarked {
|
|
private final DensityFunction function;
|
|
private long lastPos2D = ChunkPos.INVALID_CHUNK_POS;
|
|
private double lastValue;
|
|
|
|
private Cache2D(DensityFunction function) {
|
|
this.function = function;
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
double value;
|
|
int blockZ;
|
|
int blockX = context.blockX();
|
|
long pos2D = ChunkPos.asLong(blockX, blockZ = context.blockZ());
|
|
if (this.lastPos2D == pos2D) {
|
|
return this.lastValue;
|
|
}
|
|
this.lastPos2D = pos2D;
|
|
this.lastValue = value = this.function.compute(context);
|
|
return value;
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) {
|
|
this.function.fillArray(output, contextProvider);
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.function;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunctions.Marker.Type type() {
|
|
return DensityFunctions.Marker.Type.Cache2D;
|
|
}
|
|
}
|
|
|
|
private class CacheOnce
|
|
implements NoiseChunkDensityFunction,
|
|
DensityFunctions.MarkerOrMarked {
|
|
private final DensityFunction function;
|
|
private long lastCounter;
|
|
private long lastArrayCounter;
|
|
private double lastValue;
|
|
private double @Nullable [] lastArray;
|
|
|
|
private CacheOnce(DensityFunction function) {
|
|
this.function = function;
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
double value;
|
|
if (context != NoiseChunk.this) {
|
|
return this.function.compute(context);
|
|
}
|
|
if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
return this.lastArray[NoiseChunk.this.arrayIndex];
|
|
}
|
|
if (this.lastCounter == NoiseChunk.this.interpolationCounter) {
|
|
return this.lastValue;
|
|
}
|
|
this.lastCounter = NoiseChunk.this.interpolationCounter;
|
|
this.lastValue = value = this.function.compute(context);
|
|
return value;
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] output, DensityFunction.ContextProvider contextProvider) {
|
|
if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
System.arraycopy(this.lastArray, 0, output, 0, output.length);
|
|
return;
|
|
}
|
|
this.wrapped().fillArray(output, contextProvider);
|
|
if (this.lastArray != null && this.lastArray.length == output.length) {
|
|
System.arraycopy(output, 0, this.lastArray, 0, output.length);
|
|
} else {
|
|
this.lastArray = (double[])output.clone();
|
|
}
|
|
this.lastArrayCounter = NoiseChunk.this.arrayInterpolationCounter;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.function;
|
|
}
|
|
|
|
@Override
|
|
public DensityFunctions.Marker.Type type() {
|
|
return DensityFunctions.Marker.Type.CacheOnce;
|
|
}
|
|
}
|
|
|
|
private static interface NoiseChunkDensityFunction
|
|
extends DensityFunction {
|
|
public DensityFunction wrapped();
|
|
|
|
@Override
|
|
default public double minValue() {
|
|
return this.wrapped().minValue();
|
|
}
|
|
|
|
@Override
|
|
default public double maxValue() {
|
|
return this.wrapped().maxValue();
|
|
}
|
|
}
|
|
}
|
|
|