276 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
	
		
			8.3 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.client.renderer;
 | |
| 
 | |
| import net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
 | |
| import net.minecraft.client.renderer.culling.Frustum;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.SectionPos;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.world.level.levelgen.structure.BoundingBox;
 | |
| import net.minecraft.world.phys.AABB;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| public class Octree {
 | |
| 	private final Octree.Branch root;
 | |
| 	final BlockPos cameraSectionCenter;
 | |
| 
 | |
| 	public Octree(SectionPos cameraSectionPos, int viewDistance, int sectionGridSizeY, int minY) {
 | |
| 		int i = viewDistance * 2 + 1;
 | |
| 		int j = Mth.smallestEncompassingPowerOfTwo(i);
 | |
| 		int k = viewDistance * 16;
 | |
| 		BlockPos blockPos = cameraSectionPos.origin();
 | |
| 		this.cameraSectionCenter = cameraSectionPos.center();
 | |
| 		int l = blockPos.getX() - k;
 | |
| 		int m = l + j * 16 - 1;
 | |
| 		int n = j >= sectionGridSizeY ? minY : blockPos.getY() - k;
 | |
| 		int o = n + j * 16 - 1;
 | |
| 		int p = blockPos.getZ() - k;
 | |
| 		int q = p + j * 16 - 1;
 | |
| 		this.root = new Octree.Branch(new BoundingBox(l, n, p, m, o, q));
 | |
| 	}
 | |
| 
 | |
| 	public boolean add(SectionRenderDispatcher.RenderSection section) {
 | |
| 		return this.root.add(section);
 | |
| 	}
 | |
| 
 | |
| 	public void visitNodes(Octree.OctreeVisitor visitor, Frustum frustum, int nearbyRadius) {
 | |
| 		this.root.visitNodes(visitor, false, frustum, 0, nearbyRadius, true);
 | |
| 	}
 | |
| 
 | |
| 	boolean isClose(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, int radius) {
 | |
| 		int i = this.cameraSectionCenter.getX();
 | |
| 		int j = this.cameraSectionCenter.getY();
 | |
| 		int k = this.cameraSectionCenter.getZ();
 | |
| 		return i > minX - radius && i < maxX + radius && j > minY - radius && j < maxY + radius && k > minZ - radius && k < maxZ + radius;
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	static enum AxisSorting {
 | |
| 		XYZ(4, 2, 1),
 | |
| 		XZY(4, 1, 2),
 | |
| 		YXZ(2, 4, 1),
 | |
| 		YZX(1, 4, 2),
 | |
| 		ZXY(2, 1, 4),
 | |
| 		ZYX(1, 2, 4);
 | |
| 
 | |
| 		final int xShift;
 | |
| 		final int yShift;
 | |
| 		final int zShift;
 | |
| 
 | |
| 		private AxisSorting(final int xShift, final int yShift, final int zShift) {
 | |
| 			this.xShift = xShift;
 | |
| 			this.yShift = yShift;
 | |
| 			this.zShift = zShift;
 | |
| 		}
 | |
| 
 | |
| 		public static Octree.AxisSorting getAxisSorting(int xDiff, int yDiff, int zDiff) {
 | |
| 			if (xDiff > yDiff && xDiff > zDiff) {
 | |
| 				return yDiff > zDiff ? XYZ : XZY;
 | |
| 			} else if (yDiff > xDiff && yDiff > zDiff) {
 | |
| 				return xDiff > zDiff ? YXZ : YZX;
 | |
| 			} else {
 | |
| 				return xDiff > yDiff ? ZXY : ZYX;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	class Branch implements Octree.Node {
 | |
| 		private final Octree.Node[] nodes = new Octree.Node[8];
 | |
| 		private final BoundingBox boundingBox;
 | |
| 		private final int bbCenterX;
 | |
| 		private final int bbCenterY;
 | |
| 		private final int bbCenterZ;
 | |
| 		private final Octree.AxisSorting sorting;
 | |
| 		private final boolean cameraXDiffNegative;
 | |
| 		private final boolean cameraYDiffNegative;
 | |
| 		private final boolean cameraZDiffNegative;
 | |
| 
 | |
| 		public Branch(final BoundingBox boundingBox) {
 | |
| 			this.boundingBox = boundingBox;
 | |
| 			this.bbCenterX = this.boundingBox.minX() + this.boundingBox.getXSpan() / 2;
 | |
| 			this.bbCenterY = this.boundingBox.minY() + this.boundingBox.getYSpan() / 2;
 | |
| 			this.bbCenterZ = this.boundingBox.minZ() + this.boundingBox.getZSpan() / 2;
 | |
| 			int i = Octree.this.cameraSectionCenter.getX() - this.bbCenterX;
 | |
| 			int j = Octree.this.cameraSectionCenter.getY() - this.bbCenterY;
 | |
| 			int k = Octree.this.cameraSectionCenter.getZ() - this.bbCenterZ;
 | |
| 			this.sorting = Octree.AxisSorting.getAxisSorting(Math.abs(i), Math.abs(j), Math.abs(k));
 | |
| 			this.cameraXDiffNegative = i < 0;
 | |
| 			this.cameraYDiffNegative = j < 0;
 | |
| 			this.cameraZDiffNegative = k < 0;
 | |
| 		}
 | |
| 
 | |
| 		public boolean add(SectionRenderDispatcher.RenderSection section) {
 | |
| 			long l = section.getSectionNode();
 | |
| 			boolean bl = SectionPos.sectionToBlockCoord(SectionPos.x(l)) - this.bbCenterX < 0;
 | |
| 			boolean bl2 = SectionPos.sectionToBlockCoord(SectionPos.y(l)) - this.bbCenterY < 0;
 | |
| 			boolean bl3 = SectionPos.sectionToBlockCoord(SectionPos.z(l)) - this.bbCenterZ < 0;
 | |
| 			boolean bl4 = bl != this.cameraXDiffNegative;
 | |
| 			boolean bl5 = bl2 != this.cameraYDiffNegative;
 | |
| 			boolean bl6 = bl3 != this.cameraZDiffNegative;
 | |
| 			int i = getNodeIndex(this.sorting, bl4, bl5, bl6);
 | |
| 			if (this.areChildrenLeaves()) {
 | |
| 				boolean bl7 = this.nodes[i] != null;
 | |
| 				this.nodes[i] = Octree.this.new Leaf(section);
 | |
| 				return !bl7;
 | |
| 			} else if (this.nodes[i] != null) {
 | |
| 				Octree.Branch branch = (Octree.Branch)this.nodes[i];
 | |
| 				return branch.add(section);
 | |
| 			} else {
 | |
| 				BoundingBox boundingBox = this.createChildBoundingBox(bl, bl2, bl3);
 | |
| 				Octree.Branch branch2 = Octree.this.new Branch(boundingBox);
 | |
| 				this.nodes[i] = branch2;
 | |
| 				return branch2.add(section);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		private static int getNodeIndex(Octree.AxisSorting sorting, boolean xDiffNegative, boolean yDiffNegative, boolean zDiffNegative) {
 | |
| 			int i = 0;
 | |
| 			if (xDiffNegative) {
 | |
| 				i += sorting.xShift;
 | |
| 			}
 | |
| 
 | |
| 			if (yDiffNegative) {
 | |
| 				i += sorting.yShift;
 | |
| 			}
 | |
| 
 | |
| 			if (zDiffNegative) {
 | |
| 				i += sorting.zShift;
 | |
| 			}
 | |
| 
 | |
| 			return i;
 | |
| 		}
 | |
| 
 | |
| 		private boolean areChildrenLeaves() {
 | |
| 			return this.boundingBox.getXSpan() == 32;
 | |
| 		}
 | |
| 
 | |
| 		private BoundingBox createChildBoundingBox(boolean xDiffNegative, boolean yDiffNegative, boolean zDiffNegative) {
 | |
| 			int i;
 | |
| 			int j;
 | |
| 			if (xDiffNegative) {
 | |
| 				i = this.boundingBox.minX();
 | |
| 				j = this.bbCenterX - 1;
 | |
| 			} else {
 | |
| 				i = this.bbCenterX;
 | |
| 				j = this.boundingBox.maxX();
 | |
| 			}
 | |
| 
 | |
| 			int k;
 | |
| 			int l;
 | |
| 			if (yDiffNegative) {
 | |
| 				k = this.boundingBox.minY();
 | |
| 				l = this.bbCenterY - 1;
 | |
| 			} else {
 | |
| 				k = this.bbCenterY;
 | |
| 				l = this.boundingBox.maxY();
 | |
| 			}
 | |
| 
 | |
| 			int m;
 | |
| 			int n;
 | |
| 			if (zDiffNegative) {
 | |
| 				m = this.boundingBox.minZ();
 | |
| 				n = this.bbCenterZ - 1;
 | |
| 			} else {
 | |
| 				m = this.bbCenterZ;
 | |
| 				n = this.boundingBox.maxZ();
 | |
| 			}
 | |
| 
 | |
| 			return new BoundingBox(i, k, m, j, l, n);
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public void visitNodes(Octree.OctreeVisitor visitor, boolean isLeafNode, Frustum frustum, int recursionDepth, int nearbyRadius, boolean isNearby) {
 | |
| 			boolean bl = isLeafNode;
 | |
| 			if (!isLeafNode) {
 | |
| 				int i = frustum.cubeInFrustum(this.boundingBox);
 | |
| 				isLeafNode = i == -2;
 | |
| 				bl = i == -2 || i == -1;
 | |
| 			}
 | |
| 
 | |
| 			if (bl) {
 | |
| 				isNearby = isNearby
 | |
| 					&& Octree.this.isClose(
 | |
| 						this.boundingBox.minX(),
 | |
| 						this.boundingBox.minY(),
 | |
| 						this.boundingBox.minZ(),
 | |
| 						this.boundingBox.maxX(),
 | |
| 						this.boundingBox.maxY(),
 | |
| 						this.boundingBox.maxZ(),
 | |
| 						nearbyRadius
 | |
| 					);
 | |
| 				visitor.visit(this, isLeafNode, recursionDepth, isNearby);
 | |
| 
 | |
| 				for (Octree.Node node : this.nodes) {
 | |
| 					if (node != null) {
 | |
| 						node.visitNodes(visitor, isLeafNode, frustum, recursionDepth + 1, nearbyRadius, isNearby);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		@Nullable
 | |
| 		@Override
 | |
| 		public SectionRenderDispatcher.RenderSection getSection() {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public AABB getAABB() {
 | |
| 			return new AABB(
 | |
| 				this.boundingBox.minX(),
 | |
| 				this.boundingBox.minY(),
 | |
| 				this.boundingBox.minZ(),
 | |
| 				this.boundingBox.maxX() + 1,
 | |
| 				this.boundingBox.maxY() + 1,
 | |
| 				this.boundingBox.maxZ() + 1
 | |
| 			);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	final class Leaf implements Octree.Node {
 | |
| 		private final SectionRenderDispatcher.RenderSection section;
 | |
| 
 | |
| 		Leaf(final SectionRenderDispatcher.RenderSection section) {
 | |
| 			this.section = section;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public void visitNodes(Octree.OctreeVisitor visitor, boolean isLeafNode, Frustum frustum, int recursionDepth, int nearbyRadius, boolean isNearby) {
 | |
| 			AABB aABB = this.section.getBoundingBox();
 | |
| 			if (isLeafNode || frustum.isVisible(this.getSection().getBoundingBox())) {
 | |
| 				isNearby = isNearby && Octree.this.isClose(aABB.minX, aABB.minY, aABB.minZ, aABB.maxX, aABB.maxY, aABB.maxZ, nearbyRadius);
 | |
| 				visitor.visit(this, isLeafNode, recursionDepth, isNearby);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public SectionRenderDispatcher.RenderSection getSection() {
 | |
| 			return this.section;
 | |
| 		}
 | |
| 
 | |
| 		@Override
 | |
| 		public AABB getAABB() {
 | |
| 			return this.section.getBoundingBox();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	public interface Node {
 | |
| 		void visitNodes(Octree.OctreeVisitor visitor, boolean isLeafNode, Frustum frustum, int recursionDepth, int nearbyRadius, boolean isNearby);
 | |
| 
 | |
| 		@Nullable
 | |
| 		SectionRenderDispatcher.RenderSection getSection();
 | |
| 
 | |
| 		AABB getAABB();
 | |
| 	}
 | |
| 
 | |
| 	@FunctionalInterface
 | |
| 	@Environment(EnvType.CLIENT)
 | |
| 	public interface OctreeVisitor {
 | |
| 		void visit(Octree.Node node, boolean bl, int i, boolean bl2);
 | |
| 	}
 | |
| }
 |