minecraft-src/net/minecraft/world/level/levelgen/Beardifier.java
2025-07-04 01:41:11 +03:00

164 lines
6.1 KiB
Java

package net.minecraft.world.level.levelgen;
import com.google.common.annotations.VisibleForTesting;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import net.minecraft.Util;
import net.minecraft.util.Mth;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.world.level.levelgen.structure.TerrainAdjustment;
import net.minecraft.world.level.levelgen.structure.pools.JigsawJunction;
import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool;
/**
* Modifies terrain noise to be flatter near structures such as villages.
*/
public class Beardifier implements DensityFunctions.BeardifierOrMarker {
public static final int BEARD_KERNEL_RADIUS = 12;
private static final int BEARD_KERNEL_SIZE = 24;
private static final float[] BEARD_KERNEL = Util.make(new float[13824], fs -> {
for (int i = 0; i < 24; i++) {
for (int j = 0; j < 24; j++) {
for (int k = 0; k < 24; k++) {
fs[i * 24 * 24 + j * 24 + k] = (float)computeBeardContribution(j - 12, k - 12, i - 12);
}
}
}
});
private final ObjectListIterator<Beardifier.Rigid> pieceIterator;
private final ObjectListIterator<JigsawJunction> junctionIterator;
public static Beardifier forStructuresInChunk(StructureManager structureManager, ChunkPos chunkPos) {
int i = chunkPos.getMinBlockX();
int j = chunkPos.getMinBlockZ();
ObjectList<Beardifier.Rigid> objectList = new ObjectArrayList<>(10);
ObjectList<JigsawJunction> objectList2 = new ObjectArrayList<>(32);
structureManager.startsForStructure(chunkPos, structure -> structure.terrainAdaptation() != TerrainAdjustment.NONE).forEach(structureStart -> {
TerrainAdjustment terrainAdjustment = structureStart.getStructure().terrainAdaptation();
for (StructurePiece structurePiece : structureStart.getPieces()) {
if (structurePiece.isCloseToChunk(chunkPos, 12)) {
if (structurePiece instanceof PoolElementStructurePiece poolElementStructurePiece) {
StructureTemplatePool.Projection projection = poolElementStructurePiece.getElement().getProjection();
if (projection == StructureTemplatePool.Projection.RIGID) {
objectList.add(new Beardifier.Rigid(poolElementStructurePiece.getBoundingBox(), terrainAdjustment, poolElementStructurePiece.getGroundLevelDelta()));
}
for (JigsawJunction jigsawJunction : poolElementStructurePiece.getJunctions()) {
int k = jigsawJunction.getSourceX();
int l = jigsawJunction.getSourceZ();
if (k > i - 12 && l > j - 12 && k < i + 15 + 12 && l < j + 15 + 12) {
objectList2.add(jigsawJunction);
}
}
} else {
objectList.add(new Beardifier.Rigid(structurePiece.getBoundingBox(), terrainAdjustment, 0));
}
}
}
});
return new Beardifier(objectList.iterator(), objectList2.iterator());
}
@VisibleForTesting
public Beardifier(ObjectListIterator<Beardifier.Rigid> pieceIterator, ObjectListIterator<JigsawJunction> junctionIterator) {
this.pieceIterator = pieceIterator;
this.junctionIterator = junctionIterator;
}
@Override
public double compute(DensityFunction.FunctionContext context) {
int i = context.blockX();
int j = context.blockY();
int k = context.blockZ();
double d = 0.0;
while (this.pieceIterator.hasNext()) {
Beardifier.Rigid rigid = (Beardifier.Rigid)this.pieceIterator.next();
BoundingBox boundingBox = rigid.box();
int l = rigid.groundLevelDelta();
int m = Math.max(0, Math.max(boundingBox.minX() - i, i - boundingBox.maxX()));
int n = Math.max(0, Math.max(boundingBox.minZ() - k, k - boundingBox.maxZ()));
int o = boundingBox.minY() + l;
int p = j - o;
int q = switch (rigid.terrainAdjustment()) {
case NONE -> 0;
case BURY, BEARD_THIN -> p;
case BEARD_BOX -> Math.max(0, Math.max(o - j, j - boundingBox.maxY()));
case ENCAPSULATE -> Math.max(0, Math.max(boundingBox.minY() - j, j - boundingBox.maxY()));
};
d += switch (rigid.terrainAdjustment()) {
case NONE -> 0.0;
case BURY -> getBuryContribution(m, q / 2.0, n);
case BEARD_THIN, BEARD_BOX -> getBeardContribution(m, q, n, p) * 0.8;
case ENCAPSULATE -> getBuryContribution(m / 2.0, q / 2.0, n / 2.0) * 0.8;
};
}
this.pieceIterator.back(Integer.MAX_VALUE);
while (this.junctionIterator.hasNext()) {
JigsawJunction jigsawJunction = (JigsawJunction)this.junctionIterator.next();
int r = i - jigsawJunction.getSourceX();
int l = j - jigsawJunction.getSourceGroundY();
int m = k - jigsawJunction.getSourceZ();
d += getBeardContribution(r, l, m, l) * 0.4;
}
this.junctionIterator.back(Integer.MAX_VALUE);
return d;
}
@Override
public double minValue() {
return Double.NEGATIVE_INFINITY;
}
@Override
public double maxValue() {
return Double.POSITIVE_INFINITY;
}
private static double getBuryContribution(double x, double y, double z) {
double d = Mth.length(x, y, z);
return Mth.clampedMap(d, 0.0, 6.0, 1.0, 0.0);
}
private static double getBeardContribution(int x, int y, int z, int height) {
int i = x + 12;
int j = y + 12;
int k = z + 12;
if (isInKernelRange(i) && isInKernelRange(j) && isInKernelRange(k)) {
double d = height + 0.5;
double e = Mth.lengthSquared((double)x, d, (double)z);
double f = -d * Mth.fastInvSqrt(e / 2.0) / 2.0;
return f * BEARD_KERNEL[k * 24 * 24 + i * 24 + j];
} else {
return 0.0;
}
}
private static boolean isInKernelRange(int value) {
return value >= 0 && value < 24;
}
private static double computeBeardContribution(int x, int y, int z) {
return computeBeardContribution(x, y + 0.5, z);
}
private static double computeBeardContribution(int x, double y, int z) {
double d = Mth.lengthSquared((double)x, y, (double)z);
return Math.pow(Math.E, -d / 16.0);
}
@VisibleForTesting
public record Rigid(BoundingBox box, TerrainAdjustment terrainAdjustment, int groundLevelDelta) {
}
}