164 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			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) {
 | |
| 	}
 | |
| }
 |