260 lines
11 KiB
Java
260 lines
11 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
package net.minecraft.client.renderer;
|
|
|
|
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.jspecify.annotations.Nullable;
|
|
|
|
public class Octree {
|
|
private final Branch root;
|
|
private final BlockPos cameraSectionCenter;
|
|
|
|
public Octree(SectionPos cameraSection, int renderDistance, int sectionsPerChunk, int minBlockY) {
|
|
int visibleAreaDiameterInSections = renderDistance * 2 + 1;
|
|
int boundingBoxSizeInSections = Mth.smallestEncompassingPowerOfTwo(visibleAreaDiameterInSections);
|
|
int distanceToBBEdgeInBlocks = renderDistance * 16;
|
|
BlockPos cameraSectionOrigin = cameraSection.origin();
|
|
this.cameraSectionCenter = cameraSection.center();
|
|
int minX = cameraSectionOrigin.getX() - distanceToBBEdgeInBlocks;
|
|
int maxX = minX + boundingBoxSizeInSections * 16 - 1;
|
|
int minY = boundingBoxSizeInSections >= sectionsPerChunk ? minBlockY : cameraSectionOrigin.getY() - distanceToBBEdgeInBlocks;
|
|
int maxY = minY + boundingBoxSizeInSections * 16 - 1;
|
|
int minZ = cameraSectionOrigin.getZ() - distanceToBBEdgeInBlocks;
|
|
int maxZ = minZ + boundingBoxSizeInSections * 16 - 1;
|
|
this.root = new Branch(new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ));
|
|
}
|
|
|
|
public boolean add(SectionRenderDispatcher.RenderSection section) {
|
|
return this.root.add(section);
|
|
}
|
|
|
|
public void visitNodes(OctreeVisitor visitor, Frustum frustum, int closeDistance) {
|
|
this.root.visitNodes(visitor, false, frustum, 0, closeDistance, true);
|
|
}
|
|
|
|
private boolean isClose(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, int closeDistance) {
|
|
int cameraX = this.cameraSectionCenter.getX();
|
|
int cameraY = this.cameraSectionCenter.getY();
|
|
int cameraZ = this.cameraSectionCenter.getZ();
|
|
return (double)cameraX > minX - (double)closeDistance && (double)cameraX < maxX + (double)closeDistance && (double)cameraY > minY - (double)closeDistance && (double)cameraY < maxY + (double)closeDistance && (double)cameraZ > minZ - (double)closeDistance && (double)cameraZ < maxZ + (double)closeDistance;
|
|
}
|
|
|
|
private class Branch
|
|
implements Node {
|
|
private final @Nullable Node[] nodes = new Node[8];
|
|
private final BoundingBox boundingBox;
|
|
private final int bbCenterX;
|
|
private final int bbCenterY;
|
|
private final int bbCenterZ;
|
|
private final AxisSorting sorting;
|
|
private final boolean cameraXDiffNegative;
|
|
private final boolean cameraYDiffNegative;
|
|
private final boolean cameraZDiffNegative;
|
|
|
|
public Branch(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 cameraXDiff = Octree.this.cameraSectionCenter.getX() - this.bbCenterX;
|
|
int cameraYDiff = Octree.this.cameraSectionCenter.getY() - this.bbCenterY;
|
|
int cameraZDiff = Octree.this.cameraSectionCenter.getZ() - this.bbCenterZ;
|
|
this.sorting = AxisSorting.getAxisSorting(Math.abs(cameraXDiff), Math.abs(cameraYDiff), Math.abs(cameraZDiff));
|
|
this.cameraXDiffNegative = cameraXDiff < 0;
|
|
this.cameraYDiffNegative = cameraYDiff < 0;
|
|
this.cameraZDiffNegative = cameraZDiff < 0;
|
|
}
|
|
|
|
public boolean add(SectionRenderDispatcher.RenderSection section) {
|
|
long sectionNode = section.getSectionNode();
|
|
boolean sectionXDiffNegative = SectionPos.sectionToBlockCoord(SectionPos.x(sectionNode)) - this.bbCenterX < 0;
|
|
boolean sectionYDiffNegative = SectionPos.sectionToBlockCoord(SectionPos.y(sectionNode)) - this.bbCenterY < 0;
|
|
boolean sectionZDiffNegative = SectionPos.sectionToBlockCoord(SectionPos.z(sectionNode)) - this.bbCenterZ < 0;
|
|
boolean xDiffsOppositeSides = sectionXDiffNegative != this.cameraXDiffNegative;
|
|
boolean yDiffsOppositeSides = sectionYDiffNegative != this.cameraYDiffNegative;
|
|
boolean zDiffsOppositeSides = sectionZDiffNegative != this.cameraZDiffNegative;
|
|
int nodeIndex = Branch.getNodeIndex(this.sorting, xDiffsOppositeSides, yDiffsOppositeSides, zDiffsOppositeSides);
|
|
if (this.areChildrenLeaves()) {
|
|
boolean alreadyExisted = this.nodes[nodeIndex] != null;
|
|
this.nodes[nodeIndex] = new Leaf(section);
|
|
return !alreadyExisted;
|
|
}
|
|
if (this.nodes[nodeIndex] != null) {
|
|
Branch branch = (Branch)this.nodes[nodeIndex];
|
|
return branch.add(section);
|
|
}
|
|
BoundingBox childBoundingBox = this.createChildBoundingBox(sectionXDiffNegative, sectionYDiffNegative, sectionZDiffNegative);
|
|
Branch branch = new Branch(childBoundingBox);
|
|
this.nodes[nodeIndex] = branch;
|
|
return branch.add(section);
|
|
}
|
|
|
|
private static int getNodeIndex(AxisSorting sorting, boolean xDiffsOppositeSides, boolean yDiffsOppositeSides, boolean zDiffsOppositeSides) {
|
|
int index = 0;
|
|
if (xDiffsOppositeSides) {
|
|
index += sorting.xShift;
|
|
}
|
|
if (yDiffsOppositeSides) {
|
|
index += sorting.yShift;
|
|
}
|
|
if (zDiffsOppositeSides) {
|
|
index += sorting.zShift;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
private boolean areChildrenLeaves() {
|
|
return this.boundingBox.getXSpan() == 32;
|
|
}
|
|
|
|
private BoundingBox createChildBoundingBox(boolean sectionXDiffNegative, boolean sectionYDiffNegative, boolean sectionZDiffNegative) {
|
|
int maxZ;
|
|
int minZ;
|
|
int maxY;
|
|
int minY;
|
|
int maxX;
|
|
int minX;
|
|
if (sectionXDiffNegative) {
|
|
minX = this.boundingBox.minX();
|
|
maxX = this.bbCenterX - 1;
|
|
} else {
|
|
minX = this.bbCenterX;
|
|
maxX = this.boundingBox.maxX();
|
|
}
|
|
if (sectionYDiffNegative) {
|
|
minY = this.boundingBox.minY();
|
|
maxY = this.bbCenterY - 1;
|
|
} else {
|
|
minY = this.bbCenterY;
|
|
maxY = this.boundingBox.maxY();
|
|
}
|
|
if (sectionZDiffNegative) {
|
|
minZ = this.boundingBox.minZ();
|
|
maxZ = this.bbCenterZ - 1;
|
|
} else {
|
|
minZ = this.bbCenterZ;
|
|
maxZ = this.boundingBox.maxZ();
|
|
}
|
|
return new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
|
|
}
|
|
|
|
@Override
|
|
public void visitNodes(OctreeVisitor visitor, boolean skipFrustumCheck, Frustum frustum, int depth, int closeDistance, boolean isClose) {
|
|
boolean isVisible = skipFrustumCheck;
|
|
if (!skipFrustumCheck) {
|
|
int checkResult = frustum.cubeInFrustum(this.boundingBox);
|
|
skipFrustumCheck = checkResult == -2;
|
|
boolean bl = isVisible = checkResult == -2 || checkResult == -1;
|
|
}
|
|
if (isVisible) {
|
|
isClose = isClose && Octree.this.isClose(this.boundingBox.minX(), this.boundingBox.minY(), this.boundingBox.minZ(), this.boundingBox.maxX(), this.boundingBox.maxY(), this.boundingBox.maxZ(), closeDistance);
|
|
visitor.visit(this, skipFrustumCheck, depth, isClose);
|
|
for (Node node : this.nodes) {
|
|
if (node == null) continue;
|
|
node.visitNodes(visitor, skipFrustumCheck, frustum, depth + 1, closeDistance, isClose);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public @Nullable 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);
|
|
}
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public static interface OctreeVisitor {
|
|
public void visit(Node var1, boolean var2, int var3, boolean var4);
|
|
}
|
|
|
|
private 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);
|
|
|
|
private final int xShift;
|
|
private final int yShift;
|
|
private final int zShift;
|
|
|
|
private AxisSorting(int xShift, int yShift, int zShift) {
|
|
this.xShift = xShift;
|
|
this.yShift = yShift;
|
|
this.zShift = zShift;
|
|
}
|
|
|
|
public static AxisSorting getAxisSorting(int absXDiff, int absYDiff, int absZDiff) {
|
|
if (absXDiff > absYDiff && absXDiff > absZDiff) {
|
|
if (absYDiff > absZDiff) {
|
|
return XYZ;
|
|
}
|
|
return XZY;
|
|
}
|
|
if (absYDiff > absXDiff && absYDiff > absZDiff) {
|
|
if (absXDiff > absZDiff) {
|
|
return YXZ;
|
|
}
|
|
return YZX;
|
|
}
|
|
if (absXDiff > absYDiff) {
|
|
return ZXY;
|
|
}
|
|
return ZYX;
|
|
}
|
|
}
|
|
|
|
public static interface Node {
|
|
public void visitNodes(OctreeVisitor var1, boolean var2, Frustum var3, int var4, int var5, boolean var6);
|
|
|
|
public @Nullable SectionRenderDispatcher.RenderSection getSection();
|
|
|
|
public AABB getAABB();
|
|
}
|
|
|
|
private final class Leaf
|
|
implements Node {
|
|
private final SectionRenderDispatcher.RenderSection section;
|
|
|
|
private Leaf(SectionRenderDispatcher.RenderSection section) {
|
|
this.section = section;
|
|
}
|
|
|
|
@Override
|
|
public void visitNodes(OctreeVisitor visitor, boolean skipFrustumCheck, Frustum frustum, int depth, int closeDistance, boolean isClose) {
|
|
AABB boundingBox = this.section.getBoundingBox();
|
|
if (skipFrustumCheck || frustum.isVisible(this.getSection().getBoundingBox())) {
|
|
isClose = isClose && Octree.this.isClose(boundingBox.minX, boundingBox.minY, boundingBox.minZ, boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ, closeDistance);
|
|
visitor.visit(this, skipFrustumCheck, depth, isClose);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public SectionRenderDispatcher.RenderSection getSection() {
|
|
return this.section;
|
|
}
|
|
|
|
@Override
|
|
public AABB getAABB() {
|
|
return this.section.getBoundingBox();
|
|
}
|
|
}
|
|
}
|
|
|