794 lines
25 KiB
Java
794 lines
25 KiB
Java
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.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.ParameterPoint;
|
|
import net.minecraft.world.level.biome.Climate.Sampler;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.levelgen.Aquifer.FluidPicker;
|
|
import net.minecraft.world.level.levelgen.DensityFunctions.Marker.Type;
|
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
|
import net.minecraft.world.level.levelgen.blending.Blender.BlendingOutput;
|
|
import net.minecraft.world.level.levelgen.material.MaterialRuleList;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class NoiseChunk implements DensityFunction.ContextProvider, DensityFunction.FunctionContext {
|
|
private final NoiseSettings noiseSettings;
|
|
final int cellCountXZ;
|
|
final int cellCountY;
|
|
final int cellNoiseMinY;
|
|
private final int firstCellX;
|
|
private final int firstCellZ;
|
|
final int firstNoiseX;
|
|
final int firstNoiseZ;
|
|
final List<NoiseChunk.NoiseInterpolator> interpolators;
|
|
final List<NoiseChunk.CacheAllInCell> cellCaches;
|
|
private final Map<DensityFunction, DensityFunction> wrapped = new HashMap();
|
|
private final Long2IntMap preliminarySurfaceLevel = new Long2IntOpenHashMap();
|
|
private final Aquifer aquifer;
|
|
private final DensityFunction initialDensityNoJaggedness;
|
|
private final NoiseChunk.BlockStateFiller blockStateRule;
|
|
private final Blender blender;
|
|
private final NoiseChunk.FlatCache blendAlpha;
|
|
private final NoiseChunk.FlatCache blendOffset;
|
|
private final DensityFunctions.BeardifierOrMarker beardifier;
|
|
private long lastBlendingDataPos = ChunkPos.INVALID_CHUNK_POS;
|
|
private BlendingOutput lastBlendingOutput = new BlendingOutput(1.0, 0.0);
|
|
final int noiseSizeXZ;
|
|
final int cellWidth;
|
|
final int cellHeight;
|
|
boolean interpolating;
|
|
boolean fillingCell;
|
|
private int cellStartBlockX;
|
|
int cellStartBlockY;
|
|
private int cellStartBlockZ;
|
|
int inCellX;
|
|
int inCellY;
|
|
int inCellZ;
|
|
long interpolationCounter;
|
|
long arrayInterpolationCounter;
|
|
int arrayIndex;
|
|
private final DensityFunction.ContextProvider sliceFillingContextProvider = new DensityFunction.ContextProvider() {
|
|
@Override
|
|
public DensityFunction.FunctionContext forIndex(int arrayIndex) {
|
|
NoiseChunk.this.cellStartBlockY = (arrayIndex + NoiseChunk.this.cellNoiseMinY) * NoiseChunk.this.cellHeight;
|
|
NoiseChunk.this.interpolationCounter++;
|
|
NoiseChunk.this.inCellY = 0;
|
|
NoiseChunk.this.arrayIndex = arrayIndex;
|
|
return NoiseChunk.this;
|
|
}
|
|
|
|
@Override
|
|
public void fillAllDirectly(double[] values, DensityFunction function) {
|
|
for (int i = 0; i < NoiseChunk.this.cellCountY + 1; i++) {
|
|
NoiseChunk.this.cellStartBlockY = (i + NoiseChunk.this.cellNoiseMinY) * NoiseChunk.this.cellHeight;
|
|
NoiseChunk.this.interpolationCounter++;
|
|
NoiseChunk.this.inCellY = 0;
|
|
NoiseChunk.this.arrayIndex = i;
|
|
values[i] = function.compute(NoiseChunk.this);
|
|
}
|
|
}
|
|
};
|
|
|
|
public static NoiseChunk forChunk(
|
|
ChunkAccess chunk,
|
|
RandomState state,
|
|
DensityFunctions.BeardifierOrMarker beardifierOrMarker,
|
|
NoiseGeneratorSettings noiseGeneratorSettings,
|
|
FluidPicker fluidPicke,
|
|
Blender blender
|
|
) {
|
|
NoiseSettings noiseSettings = noiseGeneratorSettings.noiseSettings().clampToHeightAccessor(chunk);
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
int i = 16 / noiseSettings.getCellWidth();
|
|
return new NoiseChunk(
|
|
i, state, chunkPos.getMinBlockX(), chunkPos.getMinBlockZ(), noiseSettings, beardifierOrMarker, noiseGeneratorSettings, fluidPicke, blender
|
|
);
|
|
}
|
|
|
|
public NoiseChunk(
|
|
int cellCountXZ,
|
|
RandomState random,
|
|
int firstNoiseX,
|
|
int firstNoiseZ,
|
|
NoiseSettings noiseSettings,
|
|
DensityFunctions.BeardifierOrMarker beardifier,
|
|
NoiseGeneratorSettings noiseGeneratorSettings,
|
|
FluidPicker fluidPicker,
|
|
Blender blendifier
|
|
) {
|
|
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(firstNoiseX, this.cellWidth);
|
|
this.firstCellZ = Math.floorDiv(firstNoiseZ, this.cellWidth);
|
|
this.interpolators = Lists.<NoiseChunk.NoiseInterpolator>newArrayList();
|
|
this.cellCaches = Lists.<NoiseChunk.CacheAllInCell>newArrayList();
|
|
this.firstNoiseX = QuartPos.fromBlock(firstNoiseX);
|
|
this.firstNoiseZ = QuartPos.fromBlock(firstNoiseZ);
|
|
this.noiseSizeXZ = QuartPos.fromBlock(cellCountXZ * this.cellWidth);
|
|
this.blender = blendifier;
|
|
this.beardifier = beardifier;
|
|
this.blendAlpha = new NoiseChunk.FlatCache(new NoiseChunk.BlendAlpha(), false);
|
|
this.blendOffset = new NoiseChunk.FlatCache(new NoiseChunk.BlendOffset(), false);
|
|
|
|
for (int i = 0; i <= this.noiseSizeXZ; i++) {
|
|
int j = this.firstNoiseX + i;
|
|
int k = QuartPos.toBlock(j);
|
|
|
|
for (int l = 0; l <= this.noiseSizeXZ; l++) {
|
|
int m = this.firstNoiseZ + l;
|
|
int n = QuartPos.toBlock(m);
|
|
BlendingOutput blendingOutput = blendifier.blendOffsetAndFactor(k, n);
|
|
this.blendAlpha.values[i][l] = blendingOutput.alpha();
|
|
this.blendOffset.values[i][l] = blendingOutput.blendingOffset();
|
|
}
|
|
}
|
|
|
|
NoiseRouter noiseRouter = random.router();
|
|
NoiseRouter noiseRouter2 = noiseRouter.mapAll(this::wrap);
|
|
if (!noiseGeneratorSettings.isAquifersEnabled()) {
|
|
this.aquifer = Aquifer.createDisabled(fluidPicker);
|
|
} else {
|
|
int k = SectionPos.blockToSectionCoord(firstNoiseX);
|
|
int l = SectionPos.blockToSectionCoord(firstNoiseZ);
|
|
this.aquifer = Aquifer.create(this, new ChunkPos(k, l), noiseRouter2, random.aquiferRandom(), noiseSettings.minY(), noiseSettings.height(), fluidPicker);
|
|
}
|
|
|
|
List<NoiseChunk.BlockStateFiller> list = new ArrayList();
|
|
DensityFunction densityFunction = DensityFunctions.cacheAllInCell(
|
|
DensityFunctions.add(noiseRouter2.finalDensity(), DensityFunctions.BeardifierMarker.INSTANCE)
|
|
)
|
|
.mapAll(this::wrap);
|
|
list.add((NoiseChunk.BlockStateFiller)functionContext -> this.aquifer.computeSubstance(functionContext, densityFunction.compute(functionContext)));
|
|
if (noiseGeneratorSettings.oreVeinsEnabled()) {
|
|
list.add(OreVeinifier.create(noiseRouter2.veinToggle(), noiseRouter2.veinRidged(), noiseRouter2.veinGap(), random.oreRandom()));
|
|
}
|
|
|
|
this.blockStateRule = new MaterialRuleList((NoiseChunk.BlockStateFiller[])list.toArray(new NoiseChunk.BlockStateFiller[0]));
|
|
this.initialDensityNoJaggedness = noiseRouter2.initialDensityWithoutJaggedness();
|
|
}
|
|
|
|
protected Sampler cachedClimateSampler(NoiseRouter noiseRouter, List<ParameterPoint> points) {
|
|
return new Sampler(
|
|
noiseRouter.temperature().mapAll(this::wrap),
|
|
noiseRouter.vegetation().mapAll(this::wrap),
|
|
noiseRouter.continents().mapAll(this::wrap),
|
|
noiseRouter.erosion().mapAll(this::wrap),
|
|
noiseRouter.depth().mapAll(this::wrap),
|
|
noiseRouter.ridges().mapAll(this::wrap),
|
|
points
|
|
);
|
|
}
|
|
|
|
@Nullable
|
|
protected 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 preliminarySurfaceLevel(int x, int z) {
|
|
int i = QuartPos.toBlock(QuartPos.fromBlock(x));
|
|
int j = QuartPos.toBlock(QuartPos.fromBlock(z));
|
|
return this.preliminarySurfaceLevel.computeIfAbsent(ColumnPos.asLong(i, j), this::computePreliminarySurfaceLevel);
|
|
}
|
|
|
|
private int computePreliminarySurfaceLevel(long packedChunkPos) {
|
|
int i = ColumnPos.getX(packedChunkPos);
|
|
int j = ColumnPos.getZ(packedChunkPos);
|
|
int k = this.noiseSettings.minY();
|
|
|
|
for (int l = k + this.noiseSettings.height(); l >= k; l -= this.cellHeight) {
|
|
if (this.initialDensityNoJaggedness.compute(new DensityFunction.SinglePointContext(i, l, j)) > 0.390625) {
|
|
return l;
|
|
}
|
|
}
|
|
|
|
return Integer.MAX_VALUE;
|
|
}
|
|
|
|
@Override
|
|
public Blender getBlender() {
|
|
return this.blender;
|
|
}
|
|
|
|
private void fillSlice(boolean isSlice0, int start) {
|
|
this.cellStartBlockX = start * this.cellWidth;
|
|
this.inCellX = 0;
|
|
|
|
for (int i = 0; i < this.cellCountXZ + 1; i++) {
|
|
int j = this.firstCellZ + i;
|
|
this.cellStartBlockZ = j * this.cellWidth;
|
|
this.inCellZ = 0;
|
|
this.arrayInterpolationCounter++;
|
|
|
|
for (NoiseChunk.NoiseInterpolator noiseInterpolator : this.interpolators) {
|
|
double[] ds = (isSlice0 ? noiseInterpolator.slice0 : noiseInterpolator.slice1)[i];
|
|
noiseInterpolator.fillArray(ds, this.sliceFillingContextProvider);
|
|
}
|
|
}
|
|
|
|
this.arrayInterpolationCounter++;
|
|
}
|
|
|
|
public void initializeForFirstCellX() {
|
|
if (this.interpolating) {
|
|
throw new IllegalStateException("Staring interpolation twice");
|
|
} else {
|
|
this.interpolating = true;
|
|
this.interpolationCounter = 0L;
|
|
this.fillSlice(true, this.firstCellX);
|
|
}
|
|
}
|
|
|
|
public void advanceCellX(int increment) {
|
|
this.fillSlice(false, this.firstCellX + increment + 1);
|
|
this.cellStartBlockX = (this.firstCellX + increment) * this.cellWidth;
|
|
}
|
|
|
|
public NoiseChunk forIndex(int i) {
|
|
int j = Math.floorMod(i, this.cellWidth);
|
|
int k = Math.floorDiv(i, this.cellWidth);
|
|
int l = Math.floorMod(k, this.cellWidth);
|
|
int m = this.cellHeight - 1 - Math.floorDiv(k, this.cellWidth);
|
|
this.inCellX = l;
|
|
this.inCellY = m;
|
|
this.inCellZ = j;
|
|
this.arrayIndex = i;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public void fillAllDirectly(double[] values, DensityFunction function) {
|
|
this.arrayIndex = 0;
|
|
|
|
for (int i = this.cellHeight - 1; i >= 0; i--) {
|
|
this.inCellY = i;
|
|
|
|
for (int j = 0; j < this.cellWidth; j++) {
|
|
this.inCellX = j;
|
|
|
|
for (int k = 0; k < this.cellWidth; k++) {
|
|
this.inCellZ = k;
|
|
values[this.arrayIndex++] = function.compute(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void selectCellYZ(int y, int z) {
|
|
for (NoiseChunk.NoiseInterpolator noiseInterpolator : this.interpolators) {
|
|
noiseInterpolator.selectCellYZ(y, z);
|
|
}
|
|
|
|
this.fillingCell = true;
|
|
this.cellStartBlockY = (y + this.cellNoiseMinY) * this.cellHeight;
|
|
this.cellStartBlockZ = (this.firstCellZ + z) * this.cellWidth;
|
|
this.arrayInterpolationCounter++;
|
|
|
|
for (NoiseChunk.CacheAllInCell cacheAllInCell : this.cellCaches) {
|
|
cacheAllInCell.noiseFiller.fillArray(cacheAllInCell.values, this);
|
|
}
|
|
|
|
this.arrayInterpolationCounter++;
|
|
this.fillingCell = false;
|
|
}
|
|
|
|
public void updateForY(int cellEndBlockY, double y) {
|
|
this.inCellY = cellEndBlockY - this.cellStartBlockY;
|
|
|
|
for (NoiseChunk.NoiseInterpolator noiseInterpolator : this.interpolators) {
|
|
noiseInterpolator.updateForY(y);
|
|
}
|
|
}
|
|
|
|
public void updateForX(int cellEndBlockX, double x) {
|
|
this.inCellX = cellEndBlockX - this.cellStartBlockX;
|
|
|
|
for (NoiseChunk.NoiseInterpolator noiseInterpolator : this.interpolators) {
|
|
noiseInterpolator.updateForX(x);
|
|
}
|
|
}
|
|
|
|
public void updateForZ(int cellEndBlockZ, double z) {
|
|
this.inCellZ = cellEndBlockZ - this.cellStartBlockZ;
|
|
this.interpolationCounter++;
|
|
|
|
for (NoiseChunk.NoiseInterpolator noiseInterpolator : this.interpolators) {
|
|
noiseInterpolator.updateForZ(z);
|
|
}
|
|
}
|
|
|
|
public void stopInterpolation() {
|
|
if (!this.interpolating) {
|
|
throw new IllegalStateException("Staring interpolation twice");
|
|
} else {
|
|
this.interpolating = false;
|
|
}
|
|
}
|
|
|
|
public void swapSlices() {
|
|
this.interpolators.forEach(NoiseChunk.NoiseInterpolator::swapSlices);
|
|
}
|
|
|
|
public Aquifer aquifer() {
|
|
return this.aquifer;
|
|
}
|
|
|
|
protected int cellWidth() {
|
|
return this.cellWidth;
|
|
}
|
|
|
|
protected int cellHeight() {
|
|
return this.cellHeight;
|
|
}
|
|
|
|
BlendingOutput getOrComputeBlendingOutput(int chunkX, int chunkZ) {
|
|
long l = ChunkPos.asLong(chunkX, chunkZ);
|
|
if (this.lastBlendingDataPos == l) {
|
|
return this.lastBlendingOutput;
|
|
} else {
|
|
this.lastBlendingDataPos = l;
|
|
BlendingOutput blendingOutput = this.blender.blendOffsetAndFactor(chunkX, chunkZ);
|
|
this.lastBlendingOutput = blendingOutput;
|
|
return blendingOutput;
|
|
}
|
|
}
|
|
|
|
protected DensityFunction wrap(DensityFunction densityFunction) {
|
|
return (DensityFunction)this.wrapped.computeIfAbsent(densityFunction, this::wrapNew);
|
|
}
|
|
|
|
private DensityFunction wrapNew(DensityFunction densityFunction) {
|
|
if (densityFunction instanceof DensityFunctions.Marker marker) {
|
|
return (DensityFunction)(switch (marker.type()) {
|
|
case Interpolated -> new NoiseChunk.NoiseInterpolator(marker.wrapped());
|
|
case FlatCache -> new NoiseChunk.FlatCache(marker.wrapped(), true);
|
|
case Cache2D -> new NoiseChunk.Cache2D(marker.wrapped());
|
|
case CacheOnce -> new NoiseChunk.CacheOnce(marker.wrapped());
|
|
case CacheAllInCell -> new NoiseChunk.CacheAllInCell(marker.wrapped());
|
|
default -> throw new MatchException(null, null);
|
|
});
|
|
} else {
|
|
if (this.blender != Blender.empty()) {
|
|
if (densityFunction == DensityFunctions.BlendAlpha.INSTANCE) {
|
|
return this.blendAlpha;
|
|
}
|
|
|
|
if (densityFunction == DensityFunctions.BlendOffset.INSTANCE) {
|
|
return this.blendOffset;
|
|
}
|
|
}
|
|
|
|
if (densityFunction == DensityFunctions.BeardifierMarker.INSTANCE) {
|
|
return this.beardifier;
|
|
} else {
|
|
return densityFunction instanceof DensityFunctions.HolderHolder holderHolder ? holderHolder.function().value() : densityFunction;
|
|
}
|
|
}
|
|
}
|
|
|
|
class BlendAlpha implements NoiseChunk.NoiseChunkDensityFunction {
|
|
@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[] array, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(array, 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;
|
|
}
|
|
}
|
|
|
|
class BlendOffset implements NoiseChunk.NoiseChunkDensityFunction {
|
|
@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[] array, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(array, 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 interface BlockStateFiller {
|
|
@Nullable
|
|
BlockState calculate(DensityFunction.FunctionContext functionContext);
|
|
}
|
|
|
|
static class Cache2D implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
private final DensityFunction function;
|
|
private long lastPos2D = ChunkPos.INVALID_CHUNK_POS;
|
|
private double lastValue;
|
|
|
|
Cache2D(DensityFunction function) {
|
|
this.function = function;
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
int i = context.blockX();
|
|
int j = context.blockZ();
|
|
long l = ChunkPos.asLong(i, j);
|
|
if (this.lastPos2D == l) {
|
|
return this.lastValue;
|
|
} else {
|
|
this.lastPos2D = l;
|
|
double d = this.function.compute(context);
|
|
this.lastValue = d;
|
|
return d;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
|
|
this.function.fillArray(array, contextProvider);
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.function;
|
|
}
|
|
|
|
@Override
|
|
public Type type() {
|
|
return Type.Cache2D;
|
|
}
|
|
}
|
|
|
|
class CacheAllInCell implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
final DensityFunction noiseFiller;
|
|
final double[] values;
|
|
|
|
CacheAllInCell(final DensityFunction noiseFilter) {
|
|
this.noiseFiller = noiseFilter;
|
|
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);
|
|
} else if (!NoiseChunk.this.interpolating) {
|
|
throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
} else {
|
|
int i = NoiseChunk.this.inCellX;
|
|
int j = NoiseChunk.this.inCellY;
|
|
int k = NoiseChunk.this.inCellZ;
|
|
return i >= 0 && j >= 0 && k >= 0 && i < NoiseChunk.this.cellWidth && j < NoiseChunk.this.cellHeight && k < NoiseChunk.this.cellWidth
|
|
? this.values[((NoiseChunk.this.cellHeight - 1 - j) * NoiseChunk.this.cellWidth + i) * NoiseChunk.this.cellWidth + k]
|
|
: this.noiseFiller.compute(context);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(array, this);
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.noiseFiller;
|
|
}
|
|
|
|
@Override
|
|
public Type type() {
|
|
return Type.CacheAllInCell;
|
|
}
|
|
}
|
|
|
|
class CacheOnce implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
private final DensityFunction function;
|
|
private long lastCounter;
|
|
private long lastArrayCounter;
|
|
private double lastValue;
|
|
@Nullable
|
|
private double[] lastArray;
|
|
|
|
CacheOnce(final DensityFunction function) {
|
|
this.function = function;
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
if (context != NoiseChunk.this) {
|
|
return this.function.compute(context);
|
|
} else if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
return this.lastArray[NoiseChunk.this.arrayIndex];
|
|
} else if (this.lastCounter == NoiseChunk.this.interpolationCounter) {
|
|
return this.lastValue;
|
|
} else {
|
|
this.lastCounter = NoiseChunk.this.interpolationCounter;
|
|
double d = this.function.compute(context);
|
|
this.lastValue = d;
|
|
return d;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
|
|
if (this.lastArray != null && this.lastArrayCounter == NoiseChunk.this.arrayInterpolationCounter) {
|
|
System.arraycopy(this.lastArray, 0, array, 0, array.length);
|
|
} else {
|
|
this.wrapped().fillArray(array, contextProvider);
|
|
if (this.lastArray != null && this.lastArray.length == array.length) {
|
|
System.arraycopy(array, 0, this.lastArray, 0, array.length);
|
|
} else {
|
|
this.lastArray = (double[])array.clone();
|
|
}
|
|
|
|
this.lastArrayCounter = NoiseChunk.this.arrayInterpolationCounter;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.function;
|
|
}
|
|
|
|
@Override
|
|
public Type type() {
|
|
return Type.CacheOnce;
|
|
}
|
|
}
|
|
|
|
class FlatCache implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
private final DensityFunction noiseFiller;
|
|
final double[][] values;
|
|
|
|
FlatCache(final DensityFunction noiseFiller, final boolean computeValues) {
|
|
this.noiseFiller = noiseFiller;
|
|
this.values = new double[NoiseChunk.this.noiseSizeXZ + 1][NoiseChunk.this.noiseSizeXZ + 1];
|
|
if (computeValues) {
|
|
for (int i = 0; i <= NoiseChunk.this.noiseSizeXZ; i++) {
|
|
int j = NoiseChunk.this.firstNoiseX + i;
|
|
int k = QuartPos.toBlock(j);
|
|
|
|
for (int l = 0; l <= NoiseChunk.this.noiseSizeXZ; l++) {
|
|
int m = NoiseChunk.this.firstNoiseZ + l;
|
|
int n = QuartPos.toBlock(m);
|
|
this.values[i][l] = noiseFiller.compute(new DensityFunction.SinglePointContext(k, 0, n));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
int i = QuartPos.fromBlock(context.blockX());
|
|
int j = QuartPos.fromBlock(context.blockZ());
|
|
int k = i - NoiseChunk.this.firstNoiseX;
|
|
int l = j - NoiseChunk.this.firstNoiseZ;
|
|
int m = this.values.length;
|
|
return k >= 0 && l >= 0 && k < m && l < m ? this.values[k][l] : this.noiseFiller.compute(context);
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
|
|
contextProvider.fillAllDirectly(array, this);
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.noiseFiller;
|
|
}
|
|
|
|
@Override
|
|
public Type type() {
|
|
return Type.FlatCache;
|
|
}
|
|
}
|
|
|
|
interface NoiseChunkDensityFunction extends DensityFunction {
|
|
DensityFunction wrapped();
|
|
|
|
@Override
|
|
default double minValue() {
|
|
return this.wrapped().minValue();
|
|
}
|
|
|
|
@Override
|
|
default double maxValue() {
|
|
return this.wrapped().maxValue();
|
|
}
|
|
}
|
|
|
|
public class NoiseInterpolator implements DensityFunctions.MarkerOrMarked, NoiseChunk.NoiseChunkDensityFunction {
|
|
double[][] slice0;
|
|
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;
|
|
|
|
NoiseInterpolator(final DensityFunction noiseFilter) {
|
|
this.noiseFiller = noiseFilter;
|
|
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 cellCountXZ) {
|
|
int i = cellCountXZ + 1;
|
|
int j = cellCountY + 1;
|
|
double[][] ds = new double[i][j];
|
|
|
|
for (int k = 0; k < i; k++) {
|
|
ds[k] = new double[j];
|
|
}
|
|
|
|
return ds;
|
|
}
|
|
|
|
void selectCellYZ(int y, int z) {
|
|
this.noise000 = this.slice0[z][y];
|
|
this.noise001 = this.slice0[z + 1][y];
|
|
this.noise100 = this.slice1[z][y];
|
|
this.noise101 = this.slice1[z + 1][y];
|
|
this.noise010 = this.slice0[z][y + 1];
|
|
this.noise011 = this.slice0[z + 1][y + 1];
|
|
this.noise110 = this.slice1[z][y + 1];
|
|
this.noise111 = this.slice1[z + 1][y + 1];
|
|
}
|
|
|
|
void updateForY(double y) {
|
|
this.valueXZ00 = Mth.lerp(y, this.noise000, this.noise010);
|
|
this.valueXZ10 = Mth.lerp(y, this.noise100, this.noise110);
|
|
this.valueXZ01 = Mth.lerp(y, this.noise001, this.noise011);
|
|
this.valueXZ11 = Mth.lerp(y, this.noise101, this.noise111);
|
|
}
|
|
|
|
void updateForX(double x) {
|
|
this.valueZ0 = Mth.lerp(x, this.valueXZ00, this.valueXZ10);
|
|
this.valueZ1 = Mth.lerp(x, this.valueXZ01, this.valueXZ11);
|
|
}
|
|
|
|
void updateForZ(double z) {
|
|
this.value = Mth.lerp(z, this.valueZ0, this.valueZ1);
|
|
}
|
|
|
|
@Override
|
|
public double compute(DensityFunction.FunctionContext context) {
|
|
if (context != NoiseChunk.this) {
|
|
return this.noiseFiller.compute(context);
|
|
} else if (!NoiseChunk.this.interpolating) {
|
|
throw new IllegalStateException("Trying to sample interpolator outside the interpolation loop");
|
|
} else {
|
|
return NoiseChunk.this.fillingCell
|
|
? Mth.lerp3(
|
|
(double)NoiseChunk.this.inCellX / NoiseChunk.this.cellWidth,
|
|
(double)NoiseChunk.this.inCellY / NoiseChunk.this.cellHeight,
|
|
(double)NoiseChunk.this.inCellZ / NoiseChunk.this.cellWidth,
|
|
this.noise000,
|
|
this.noise100,
|
|
this.noise010,
|
|
this.noise110,
|
|
this.noise001,
|
|
this.noise101,
|
|
this.noise011,
|
|
this.noise111
|
|
)
|
|
: this.value;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) {
|
|
if (NoiseChunk.this.fillingCell) {
|
|
contextProvider.fillAllDirectly(array, this);
|
|
} else {
|
|
this.wrapped().fillArray(array, contextProvider);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public DensityFunction wrapped() {
|
|
return this.noiseFiller;
|
|
}
|
|
|
|
private void swapSlices() {
|
|
double[][] ds = this.slice0;
|
|
this.slice0 = this.slice1;
|
|
this.slice1 = ds;
|
|
}
|
|
|
|
@Override
|
|
public Type type() {
|
|
return Type.Interpolated;
|
|
}
|
|
}
|
|
}
|