minecraft-src/net/minecraft/client/renderer/Octree.java
2025-07-04 02:00:41 +03:00

269 lines
7.6 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 sectionPos, int i, int j, int k) {
int l = i * 2 + 1;
int m = Mth.smallestEncompassingPowerOfTwo(l);
int n = i * 16;
BlockPos blockPos = sectionPos.origin();
this.cameraSectionCenter = sectionPos.center();
int o = blockPos.getX() - n;
int p = o + m * 16 - 1;
int q = m >= j ? k : blockPos.getY() - n;
int r = q + m * 16 - 1;
int s = blockPos.getZ() - n;
int t = s + m * 16 - 1;
this.root = new Octree.Branch(new BoundingBox(o, q, s, p, r, t));
}
public boolean add(SectionRenderDispatcher.RenderSection renderSection) {
return this.root.add(renderSection);
}
public void visitNodes(Octree.OctreeVisitor octreeVisitor, Frustum frustum, int i) {
this.root.visitNodes(octreeVisitor, false, frustum, 0, i, true);
}
boolean isClose(double d, double e, double f, double g, double h, double i, int j) {
int k = this.cameraSectionCenter.getX();
int l = this.cameraSectionCenter.getY();
int m = this.cameraSectionCenter.getZ();
return k > d - j && k < g + j && l > e - j && l < h + j && m > f - j && m < i + j;
}
@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 j, final int k, final int l) {
this.xShift = j;
this.yShift = k;
this.zShift = l;
}
public static Octree.AxisSorting getAxisSorting(int i, int j, int k) {
if (i > j && i > k) {
return j > k ? XYZ : XZY;
} else if (j > i && j > k) {
return i > k ? YXZ : YZX;
} else {
return i > j ? 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 renderSection) {
boolean bl = renderSection.getOrigin().getX() - this.bbCenterX < 0;
boolean bl2 = renderSection.getOrigin().getY() - this.bbCenterY < 0;
boolean bl3 = renderSection.getOrigin().getZ() - 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(renderSection);
return !bl7;
} else if (this.nodes[i] != null) {
Octree.Branch branch = (Octree.Branch)this.nodes[i];
return branch.add(renderSection);
} else {
BoundingBox boundingBox = this.createChildBoundingBox(bl, bl2, bl3);
Octree.Branch branch2 = Octree.this.new Branch(boundingBox);
this.nodes[i] = branch2;
return branch2.add(renderSection);
}
}
private static int getNodeIndex(Octree.AxisSorting axisSorting, boolean bl, boolean bl2, boolean bl3) {
int i = 0;
if (bl) {
i += axisSorting.xShift;
}
if (bl2) {
i += axisSorting.yShift;
}
if (bl3) {
i += axisSorting.zShift;
}
return i;
}
private boolean areChildrenLeaves() {
return this.boundingBox.getXSpan() == 32;
}
private BoundingBox createChildBoundingBox(boolean bl, boolean bl2, boolean bl3) {
int i;
int j;
if (bl) {
i = this.boundingBox.minX();
j = this.bbCenterX - 1;
} else {
i = this.bbCenterX;
j = this.boundingBox.maxX();
}
int k;
int l;
if (bl2) {
k = this.boundingBox.minY();
l = this.bbCenterY - 1;
} else {
k = this.bbCenterY;
l = this.boundingBox.maxY();
}
int m;
int n;
if (bl3) {
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 octreeVisitor, boolean bl, Frustum frustum, int i, int j, boolean bl2) {
boolean bl3 = bl;
if (!bl) {
int k = frustum.cubeInFrustum(this.boundingBox);
bl = k == -2;
bl3 = k == -2 || k == -1;
}
if (bl3) {
bl2 = bl2
&& Octree.this.isClose(
this.boundingBox.minX(), this.boundingBox.minY(), this.boundingBox.minZ(), this.boundingBox.maxX(), this.boundingBox.maxY(), this.boundingBox.maxZ(), j
);
octreeVisitor.visit(this, bl, i, bl2);
for (Octree.Node node : this.nodes) {
if (node != null) {
node.visitNodes(octreeVisitor, bl, frustum, i + 1, j, bl2);
}
}
}
}
@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 renderSection) {
this.section = renderSection;
}
@Override
public void visitNodes(Octree.OctreeVisitor octreeVisitor, boolean bl, Frustum frustum, int i, int j, boolean bl2) {
AABB aABB = this.section.getBoundingBox();
if (bl || frustum.isVisible(this.getSection().getBoundingBox())) {
bl2 = bl2 && Octree.this.isClose(aABB.minX, aABB.minY, aABB.minZ, aABB.maxX, aABB.maxY, aABB.maxZ, j);
octreeVisitor.visit(this, bl, i, bl2);
}
}
@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 octreeVisitor, boolean bl, Frustum frustum, int i, int j, boolean bl2);
@Nullable
SectionRenderDispatcher.RenderSection getSection();
AABB getAABB();
}
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface OctreeVisitor {
void visit(Octree.Node node, boolean bl, int i, boolean bl2);
}
}