Octree.java

net.minecraft.client.renderer.Octree

信息

  • 全限定名:net.minecraft.client.renderer.Octree
  • 类型:public class
  • 包:net.minecraft.client.renderer
  • 源码路径:src/main/java/net/minecraft/client/renderer/Octree.java
  • 起始行号:L16
  • 职责:

    TODO

字段/常量

  • root

    • 类型: Octree.Branch
    • 修饰符: private final
    • 源码定位: L17
    • 说明:

      TODO

  • cameraSectionCenter

    • 类型: BlockPos
    • 修饰符: private final
    • 源码定位: L18
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.renderer.Octree.AxisSorting

    • 类型: enum
    • 修饰符: private static
    • 源码定位: L56
    • 说明:

      TODO

  • net.minecraft.client.renderer.Octree.Branch

    • 类型: class
    • 修饰符: private
    • 源码定位: L86
    • 说明:

      TODO

  • net.minecraft.client.renderer.Octree.Leaf

    • 类型: class
    • 修饰符: private final
    • 源码定位: L242
    • 说明:

      TODO

  • net.minecraft.client.renderer.Octree.Node

    • 类型: interface
    • 修饰符: public
    • 源码定位: L275
    • 说明:

      TODO

  • net.minecraft.client.renderer.Octree.OctreeVisitor

    • 类型: interface
    • 修饰符: public
    • 源码定位: L285
    • 说明:

      TODO

构造器

public Octree(SectionPos cameraSection, int renderDistance, int sectionsPerChunk, int minBlockY) @ L20

  • 构造器名:Octree
  • 源码定位:L20
  • 修饰符:public

参数:

  • cameraSection: SectionPos
  • renderDistance: int
  • sectionsPerChunk: int
  • minBlockY: int

说明:

TODO

方法

下面的方法块按源码顺序生成。

public boolean add(SectionRenderDispatcher.RenderSection section) @ L35

  • 方法名:add
  • 源码定位:L35
  • 返回类型:boolean
  • 修饰符:public

参数:

  • section: SectionRenderDispatcher.RenderSection

说明:

TODO

public void visitNodes(Octree.OctreeVisitor visitor, Frustum frustum, int closeDistance) @ L39

  • 方法名:visitNodes
  • 源码定位:L39
  • 返回类型:void
  • 修饰符:public

参数:

  • visitor: Octree.OctreeVisitor
  • frustum: Frustum
  • closeDistance: int

说明:

TODO

private boolean isClose(double minX, double minY, double minZ, double maxX, double maxY, double maxZ, int closeDistance) @ L43

  • 方法名:isClose
  • 源码定位:L43
  • 返回类型:boolean
  • 修饰符:private

参数:

  • minX: double
  • minY: double
  • minZ: double
  • maxX: double
  • maxY: double
  • maxZ: double
  • closeDistance: int

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class Octree {
    private final Octree.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 Octree.Branch(new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ));
    }
 
    public boolean add(SectionRenderDispatcher.RenderSection section) {
        return this.root.add(section);
    }
 
    public void visitNodes(Octree.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 cameraX > minX - closeDistance
            && cameraX < maxX + closeDistance
            && cameraY > minY - closeDistance
            && cameraY < maxY + closeDistance
            && cameraZ > minZ - closeDistance
            && cameraZ < maxZ + closeDistance;
    }
 
    @OnlyIn(Dist.CLIENT)
    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 Octree.AxisSorting getAxisSorting(int absXDiff, int absYDiff, int absZDiff) {
            if (absXDiff > absYDiff && absXDiff > absZDiff) {
                return absYDiff > absZDiff ? XYZ : XZY;
            } else if (absYDiff > absXDiff && absYDiff > absZDiff) {
                return absXDiff > absZDiff ? YXZ : YZX;
            } else {
                return absXDiff > absYDiff ? ZXY : ZYX;
            }
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private class Branch implements Octree.Node {
        private final Octree.@Nullable Node[] nodes;
        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(BoundingBox boundingBox) {
            Objects.requireNonNull(Octree.this);
            super();
            this.nodes = new Octree.Node[8];
            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 = Octree.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 = getNodeIndex(this.sorting, xDiffsOppositeSides, yDiffsOppositeSides, zDiffsOppositeSides);
            if (this.areChildrenLeaves()) {
                boolean alreadyExisted = this.nodes[nodeIndex] != null;
                this.nodes[nodeIndex] = Octree.this.new Leaf(section);
                return !alreadyExisted;
            } else if (this.nodes[nodeIndex] != null) {
                Octree.Branch branch = (Octree.Branch)this.nodes[nodeIndex];
                return branch.add(section);
            } else {
                BoundingBox childBoundingBox = this.createChildBoundingBox(sectionXDiffNegative, sectionYDiffNegative, sectionZDiffNegative);
                Octree.Branch branch = Octree.this.new Branch(childBoundingBox);
                this.nodes[nodeIndex] = branch;
                return branch.add(section);
            }
        }
 
        private static int getNodeIndex(Octree.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 minX;
            int maxX;
            if (sectionXDiffNegative) {
                minX = this.boundingBox.minX();
                maxX = this.bbCenterX - 1;
            } else {
                minX = this.bbCenterX;
                maxX = this.boundingBox.maxX();
            }
 
            int minY;
            int maxY;
            if (sectionYDiffNegative) {
                minY = this.boundingBox.minY();
                maxY = this.bbCenterY - 1;
            } else {
                minY = this.bbCenterY;
                maxY = this.boundingBox.maxY();
            }
 
            int minZ;
            int maxZ;
            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(Octree.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;
                isVisible = checkResult == -2 || checkResult == -1;
            }
 
            if (isVisible) {
                isClose = isClose
                    && Octree.this.isClose(
                        (double)this.boundingBox.minX(),
                        (double)this.boundingBox.minY(),
                        (double)this.boundingBox.minZ(),
                        (double)this.boundingBox.maxX(),
                        (double)this.boundingBox.maxY(),
                        (double)this.boundingBox.maxZ(),
                        closeDistance
                    );
                visitor.visit(this, skipFrustumCheck, depth, isClose);
 
                for (Octree.Node node : this.nodes) {
                    if (node != null) {
                        node.visitNodes(visitor, skipFrustumCheck, frustum, depth + 1, closeDistance, isClose);
                    }
                }
            }
        }
 
        @Override
        public SectionRenderDispatcher.@Nullable 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
            );
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private final class Leaf implements Octree.Node {
        private final SectionRenderDispatcher.RenderSection section;
 
        private Leaf(SectionRenderDispatcher.RenderSection section) {
            Objects.requireNonNull(Octree.this);
            super();
            this.section = section;
        }
 
        @Override
        public void visitNodes(Octree.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();
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    public interface Node {
        void visitNodes(Octree.OctreeVisitor visitor, boolean skipFrustumCheck, Frustum frustum, int depth, final int closeDistance, boolean isClose);
 
        SectionRenderDispatcher.@Nullable RenderSection getSection();
 
        AABB getAABB();
    }
 
    @FunctionalInterface
    @OnlyIn(Dist.CLIENT)
    public interface OctreeVisitor {
        void visit(final Octree.Node node, final boolean fullyVisible, int depth, boolean isClose);
    }
}

引用的其他类

  • SectionRenderDispatcher

    • 引用位置: 参数
  • Frustum

    • 引用位置: 参数
  • BlockPos

    • 引用位置: 字段
  • SectionPos

    • 引用位置: 参数/方法调用
    • 关联成员: SectionPos.sectionToBlockCoord(), SectionPos.x(), SectionPos.y(), SectionPos.z()
  • Mth

    • 引用位置: 方法调用
    • 关联成员: Mth.smallestEncompassingPowerOfTwo()
  • BoundingBox

    • 引用位置: 构造调用
    • 关联成员: BoundingBox()
  • AABB

    • 引用位置: 构造调用
    • 关联成员: AABB()