SectionOcclusionGraph.java

net.minecraft.client.renderer.SectionOcclusionGraph

信息

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

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L45
    • 说明:

      TODO

  • DIRECTIONS

    • 类型: Direction[]
    • 修饰符: private static final
    • 源码定位: L46
    • 说明:

      TODO

  • MINIMUM_ADVANCED_CULLING_DISTANCE

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L47
    • 说明:

      TODO

  • MINIMUM_ADVANCED_CULLING_SECTION_DISTANCE

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L48
    • 说明:

      TODO

  • CEILED_SECTION_DIAGONAL

    • 类型: double
    • 修饰符: private static final
    • 源码定位: L49
    • 说明:

      TODO

  • needsFullUpdate

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L50
    • 说明:

      TODO

  • fullUpdateTask

    • 类型: Future<?>
    • 修饰符: private
    • 源码定位: L51
    • 说明:

      TODO

  • viewArea

    • 类型: ViewArea
    • 修饰符: private
    • 源码定位: L52
    • 说明:

      TODO

  • currentGraph

    • 类型: AtomicReference<SectionOcclusionGraph.GraphState>
    • 修饰符: private final
    • 源码定位: L53
    • 说明:

      TODO

  • nextGraphEvents

    • 类型: AtomicReference<SectionOcclusionGraph.GraphEvents>
    • 修饰符: private final
    • 源码定位: L54
    • 说明:

      TODO

  • needsFrustumUpdate

    • 类型: AtomicBoolean
    • 修饰符: private final
    • 源码定位: L55
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.renderer.SectionOcclusionGraph.GraphEvents

    • 类型: record
    • 修饰符: private
    • 源码定位: L386
    • 说明:

      TODO

  • net.minecraft.client.renderer.SectionOcclusionGraph.GraphState

    • 类型: record
    • 修饰符: private
    • 源码定位: L393
    • 说明:

      TODO

  • net.minecraft.client.renderer.SectionOcclusionGraph.GraphStorage

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

      TODO

  • net.minecraft.client.renderer.SectionOcclusionGraph.Node

    • 类型: class
    • 修饰符: public static
    • 源码定位: L414
    • 说明:

      TODO

  • net.minecraft.client.renderer.SectionOcclusionGraph.SectionToNodeMap

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

      TODO

构造器

方法

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

public void waitAndReset(ViewArea viewArea) @ L57

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

参数:

  • viewArea: ViewArea

说明:

TODO

public void invalidate() @ L76

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

参数:

说明:

TODO

public void addSectionsInFrustum(Frustum frustum, List<SectionRenderDispatcher.RenderSection> visibleSections, List<SectionRenderDispatcher.RenderSection> nearbyVisibleSection) @ L80

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

参数:

  • frustum: Frustum
  • visibleSections: List<SectionRenderDispatcher.RenderSection>
  • nearbyVisibleSection: List<SectionRenderDispatcher.RenderSection>

说明:

TODO

public boolean consumeFrustumUpdate() @ L94

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

参数:

说明:

TODO

public void onChunkReadyToRender(ChunkPos pos) @ L98

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

参数:

  • pos: ChunkPos

说明:

TODO

public void schedulePropagationFrom(SectionRenderDispatcher.RenderSection section) @ L110

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

参数:

  • section: SectionRenderDispatcher.RenderSection

说明:

TODO

public void update(boolean smartCull, Camera camera, Frustum frustum, List<SectionRenderDispatcher.RenderSection> visibleSections, LongOpenHashSet loadedEmptySections) @ L122

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

参数:

  • smartCull: boolean
  • camera: Camera
  • frustum: Frustum
  • visibleSections: List<SectionRenderDispatcher.RenderSection>
  • loadedEmptySections: LongOpenHashSet

说明:

TODO

private void scheduleFullUpdate(boolean smartCull, Camera camera, Vec3 cameraPos, LongOpenHashSet loadedEmptySections) @ L133

  • 方法名:scheduleFullUpdate
  • 源码定位:L133
  • 返回类型:void
  • 修饰符:private

参数:

  • smartCull: boolean
  • camera: Camera
  • cameraPos: Vec3
  • loadedEmptySections: LongOpenHashSet

说明:

TODO

private void runPartialUpdate(boolean smartCull, Frustum frustum, List<SectionRenderDispatcher.RenderSection> visibleSections, Vec3 cameraPos, LongOpenHashSet loadedEmptySections) @ L149

  • 方法名:runPartialUpdate
  • 源码定位:L149
  • 返回类型:void
  • 修饰符:private

参数:

  • smartCull: boolean
  • frustum: Frustum
  • visibleSections: List<SectionRenderDispatcher.RenderSection>
  • cameraPos: Vec3
  • loadedEmptySections: LongOpenHashSet

说明:

TODO

private void queueSectionsWithNewNeighbors(SectionOcclusionGraph.GraphState state) @ L175

  • 方法名:queueSectionsWithNewNeighbors
  • 源码定位:L175
  • 返回类型:void
  • 修饰符:private

参数:

  • state: SectionOcclusionGraph.GraphState

说明:

TODO

private void addNeighbors(SectionOcclusionGraph.GraphEvents events, ChunkPos pos) @ L190

  • 方法名:addNeighbors
  • 源码定位:L190
  • 返回类型:void
  • 修饰符:private

参数:

  • events: SectionOcclusionGraph.GraphEvents
  • pos: ChunkPos

说明:

TODO

private void initializeQueueForFullUpdate(Camera camera, Queue<SectionOcclusionGraph.Node> queue) @ L201

  • 方法名:initializeQueueForFullUpdate
  • 源码定位:L201
  • 返回类型:void
  • 修饰符:private

参数:

  • camera: Camera
  • queue: Queue<SectionOcclusionGraph.Node>

说明:

TODO

private void runUpdates(SectionOcclusionGraph.GraphStorage storage, Vec3 cameraPos, Queue<SectionOcclusionGraph.Node> queue, boolean smartCull, Consumer<SectionRenderDispatcher.RenderSection> onSectionAdded, LongOpenHashSet emptySections) @ L247

  • 方法名:runUpdates
  • 源码定位:L247
  • 返回类型:void
  • 修饰符:private

参数:

  • storage: SectionOcclusionGraph.GraphStorage
  • cameraPos: Vec3
  • queue: Queue<SectionOcclusionGraph.Node>
  • smartCull: boolean
  • onSectionAdded: Consumer<SectionRenderDispatcher.RenderSection>
  • emptySections: LongOpenHashSet

说明:

TODO

private boolean isInViewDistance(long cameraSectionNode, long sectionNode) @ L353

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

参数:

  • cameraSectionNode: long
  • sectionNode: long

说明:

TODO

private SectionRenderDispatcher.RenderSection getRelativeFrom(long cameraSectionNode, SectionRenderDispatcher.RenderSection renderSection, Direction direction) @ L363

  • 方法名:getRelativeFrom
  • 源码定位:L363
  • 返回类型:SectionRenderDispatcher.RenderSection
  • 修饰符:private

参数:

  • cameraSectionNode: long
  • renderSection: SectionRenderDispatcher.RenderSection
  • direction: Direction

说明:

TODO

public SectionOcclusionGraph.Node getNode(SectionRenderDispatcher.RenderSection section) @ L376

  • 方法名:getNode
  • 源码定位:L376
  • 返回类型:SectionOcclusionGraph.Node
  • 修饰符:public

参数:

  • section: SectionRenderDispatcher.RenderSection

说明:

TODO

public Octree getOctree() @ L381

  • 方法名:getOctree
  • 源码定位:L381
  • 返回类型:Octree
  • 修饰符:public

参数:

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class SectionOcclusionGraph {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final Direction[] DIRECTIONS = Direction.values();
    private static final int MINIMUM_ADVANCED_CULLING_DISTANCE = 60;
    private static final int MINIMUM_ADVANCED_CULLING_SECTION_DISTANCE = SectionPos.blockToSectionCoord(60);
    private static final double CEILED_SECTION_DIAGONAL = Math.ceil(Math.sqrt(3.0) * 16.0);
    private boolean needsFullUpdate = true;
    private @Nullable Future<?> fullUpdateTask;
    private @Nullable ViewArea viewArea;
    private final AtomicReference<SectionOcclusionGraph.@Nullable GraphState> currentGraph = new AtomicReference<>();
    private final AtomicReference<SectionOcclusionGraph.@Nullable GraphEvents> nextGraphEvents = new AtomicReference<>();
    private final AtomicBoolean needsFrustumUpdate = new AtomicBoolean(false);
 
    public void waitAndReset(@Nullable ViewArea viewArea) {
        if (this.fullUpdateTask != null) {
            try {
                this.fullUpdateTask.get();
                this.fullUpdateTask = null;
            } catch (Exception var3) {
                LOGGER.warn("Full update failed", (Throwable)var3);
            }
        }
 
        this.viewArea = viewArea;
        if (viewArea != null) {
            this.currentGraph.set(new SectionOcclusionGraph.GraphState(viewArea));
            this.invalidate();
        } else {
            this.currentGraph.set(null);
        }
    }
 
    public void invalidate() {
        this.needsFullUpdate = true;
    }
 
    public void addSectionsInFrustum(
        Frustum frustum, List<SectionRenderDispatcher.RenderSection> visibleSections, List<SectionRenderDispatcher.RenderSection> nearbyVisibleSection
    ) {
        this.currentGraph.get().storage().sectionTree.visitNodes((node, fullyVisible, depth, isClose) -> {
            SectionRenderDispatcher.RenderSection renderSection = node.getSection();
            if (renderSection != null) {
                visibleSections.add(renderSection);
                if (isClose) {
                    nearbyVisibleSection.add(renderSection);
                }
            }
        }, frustum, 32);
    }
 
    public boolean consumeFrustumUpdate() {
        return this.needsFrustumUpdate.compareAndSet(true, false);
    }
 
    public void onChunkReadyToRender(ChunkPos pos) {
        SectionOcclusionGraph.GraphEvents nextEvents = this.nextGraphEvents.get();
        if (nextEvents != null) {
            this.addNeighbors(nextEvents, pos);
        }
 
        SectionOcclusionGraph.GraphEvents events = this.currentGraph.get().events;
        if (events != nextEvents) {
            this.addNeighbors(events, pos);
        }
    }
 
    public void schedulePropagationFrom(SectionRenderDispatcher.RenderSection section) {
        SectionOcclusionGraph.GraphEvents nextEvents = this.nextGraphEvents.get();
        if (nextEvents != null) {
            nextEvents.sectionsToPropagateFrom.add(section);
        }
 
        SectionOcclusionGraph.GraphEvents events = this.currentGraph.get().events;
        if (events != nextEvents) {
            events.sectionsToPropagateFrom.add(section);
        }
    }
 
    public void update(
        boolean smartCull, Camera camera, Frustum frustum, List<SectionRenderDispatcher.RenderSection> visibleSections, LongOpenHashSet loadedEmptySections
    ) {
        Vec3 cameraPos = camera.position();
        if (this.needsFullUpdate && (this.fullUpdateTask == null || this.fullUpdateTask.isDone())) {
            this.scheduleFullUpdate(smartCull, camera, cameraPos, loadedEmptySections);
        }
 
        this.runPartialUpdate(smartCull, frustum, visibleSections, cameraPos, loadedEmptySections);
    }
 
    private void scheduleFullUpdate(boolean smartCull, Camera camera, Vec3 cameraPos, LongOpenHashSet loadedEmptySections) {
        this.needsFullUpdate = false;
        LongOpenHashSet emptySections = loadedEmptySections.clone();
        this.fullUpdateTask = CompletableFuture.runAsync(() -> {
            SectionOcclusionGraph.GraphState newState = new SectionOcclusionGraph.GraphState(this.viewArea);
            this.nextGraphEvents.set(newState.events);
            Queue<SectionOcclusionGraph.Node> queue = Queues.newArrayDeque();
            this.initializeQueueForFullUpdate(camera, queue);
            queue.forEach(node -> newState.storage.sectionToNodeMap.put(node.section, node));
            this.runUpdates(newState.storage, cameraPos, queue, smartCull, node -> {}, emptySections);
            this.currentGraph.set(newState);
            this.nextGraphEvents.set(null);
            this.needsFrustumUpdate.set(true);
        }, Util.backgroundExecutor());
    }
 
    private void runPartialUpdate(
        boolean smartCull, Frustum frustum, List<SectionRenderDispatcher.RenderSection> visibleSections, Vec3 cameraPos, LongOpenHashSet loadedEmptySections
    ) {
        SectionOcclusionGraph.GraphState state = this.currentGraph.get();
        this.queueSectionsWithNewNeighbors(state);
        if (!state.events.sectionsToPropagateFrom.isEmpty()) {
            Queue<SectionOcclusionGraph.Node> queue = Queues.newArrayDeque();
 
            while (!state.events.sectionsToPropagateFrom.isEmpty()) {
                SectionRenderDispatcher.RenderSection renderSection = state.events.sectionsToPropagateFrom.poll();
                SectionOcclusionGraph.Node node = state.storage.sectionToNodeMap.get(renderSection);
                if (node != null && node.section == renderSection) {
                    queue.add(node);
                }
            }
 
            Frustum offsetFrustum = LevelRenderer.offsetFrustum(frustum);
            Consumer<SectionRenderDispatcher.RenderSection> onSectionAdded = section -> {
                if (offsetFrustum.isVisible(section.getBoundingBox())) {
                    this.needsFrustumUpdate.set(true);
                }
            };
            this.runUpdates(state.storage, cameraPos, queue, smartCull, onSectionAdded, loadedEmptySections);
        }
    }
 
    private void queueSectionsWithNewNeighbors(SectionOcclusionGraph.GraphState state) {
        LongIterator iterator = state.events.chunksWhichReceivedNeighbors.iterator();
 
        while (iterator.hasNext()) {
            long chunkWithNewNeighbor = iterator.nextLong();
            List<SectionRenderDispatcher.RenderSection> renderSections = state.storage.chunksWaitingForNeighbors.get(chunkWithNewNeighbor);
            if (renderSections != null && renderSections.get(0).hasAllNeighbors()) {
                state.events.sectionsToPropagateFrom.addAll(renderSections);
                state.storage.chunksWaitingForNeighbors.remove(chunkWithNewNeighbor);
            }
        }
 
        state.events.chunksWhichReceivedNeighbors.clear();
    }
 
    private void addNeighbors(SectionOcclusionGraph.GraphEvents events, ChunkPos pos) {
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x() - 1, pos.z()));
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x(), pos.z() - 1));
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x() + 1, pos.z()));
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x(), pos.z() + 1));
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x() - 1, pos.z() - 1));
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x() - 1, pos.z() + 1));
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x() + 1, pos.z() - 1));
        events.chunksWhichReceivedNeighbors.add(ChunkPos.pack(pos.x() + 1, pos.z() + 1));
    }
 
    private void initializeQueueForFullUpdate(Camera camera, Queue<SectionOcclusionGraph.Node> queue) {
        BlockPos cameraPosition = camera.blockPosition();
        long cameraSectionNode = SectionPos.asLong(cameraPosition);
        int cameraSectionY = SectionPos.y(cameraSectionNode);
        SectionRenderDispatcher.RenderSection cameraSection = this.viewArea.getRenderSection(cameraSectionNode);
        if (cameraSection == null) {
            LevelHeightAccessor heightAccessor = this.viewArea.getLevelHeightAccessor();
            boolean isBelowTheWorld = cameraSectionY < heightAccessor.getMinSectionY();
            int sectionY = isBelowTheWorld ? heightAccessor.getMinSectionY() : heightAccessor.getMaxSectionY();
            int viewDistance = this.viewArea.getViewDistance();
            List<SectionOcclusionGraph.Node> toAdd = Lists.newArrayList();
            int cameraSectionX = SectionPos.x(cameraSectionNode);
            int cameraSectionZ = SectionPos.z(cameraSectionNode);
 
            for (int sectionX = -viewDistance; sectionX <= viewDistance; sectionX++) {
                for (int sectionZ = -viewDistance; sectionZ <= viewDistance; sectionZ++) {
                    SectionRenderDispatcher.RenderSection renderSectionAt = this.viewArea
                        .getRenderSection(SectionPos.asLong(sectionX + cameraSectionX, sectionY, sectionZ + cameraSectionZ));
                    if (renderSectionAt != null && this.isInViewDistance(cameraSectionNode, renderSectionAt.getSectionNode())) {
                        Direction sourceDirection = isBelowTheWorld ? Direction.UP : Direction.DOWN;
                        SectionOcclusionGraph.Node node = new SectionOcclusionGraph.Node(renderSectionAt, sourceDirection, 0);
                        node.setDirections(node.directions, sourceDirection);
                        if (sectionX > 0) {
                            node.setDirections(node.directions, Direction.EAST);
                        } else if (sectionX < 0) {
                            node.setDirections(node.directions, Direction.WEST);
                        }
 
                        if (sectionZ > 0) {
                            node.setDirections(node.directions, Direction.SOUTH);
                        } else if (sectionZ < 0) {
                            node.setDirections(node.directions, Direction.NORTH);
                        }
 
                        toAdd.add(node);
                    }
                }
            }
 
            toAdd.sort(Comparator.comparingDouble(c -> cameraPosition.distSqr(SectionPos.of(c.section.getSectionNode()).center())));
            queue.addAll(toAdd);
        } else {
            queue.add(new SectionOcclusionGraph.Node(cameraSection, null, 0));
        }
    }
 
    private void runUpdates(
        SectionOcclusionGraph.GraphStorage storage,
        Vec3 cameraPos,
        Queue<SectionOcclusionGraph.Node> queue,
        boolean smartCull,
        Consumer<SectionRenderDispatcher.RenderSection> onSectionAdded,
        LongOpenHashSet emptySections
    ) {
        SectionPos cameraSectionPos = SectionPos.of(cameraPos);
        long cameraSectionNode = cameraSectionPos.asLong();
        BlockPos cameraSectionCenter = cameraSectionPos.center();
 
        while (!queue.isEmpty()) {
            SectionOcclusionGraph.Node node = queue.poll();
            SectionRenderDispatcher.RenderSection currentSection = node.section;
            if (!emptySections.contains(node.section.getSectionNode())) {
                if (storage.sectionTree.add(node.section)) {
                    onSectionAdded.accept(node.section);
                }
            } else {
                node.section.sectionMesh.compareAndSet(CompiledSectionMesh.UNCOMPILED, CompiledSectionMesh.EMPTY);
            }
 
            long sectionNode = currentSection.getSectionNode();
            boolean distantFromCamera = Math.abs(SectionPos.x(sectionNode) - cameraSectionPos.x()) > MINIMUM_ADVANCED_CULLING_SECTION_DISTANCE
                || Math.abs(SectionPos.y(sectionNode) - cameraSectionPos.y()) > MINIMUM_ADVANCED_CULLING_SECTION_DISTANCE
                || Math.abs(SectionPos.z(sectionNode) - cameraSectionPos.z()) > MINIMUM_ADVANCED_CULLING_SECTION_DISTANCE;
 
            for (Direction direction : DIRECTIONS) {
                SectionRenderDispatcher.RenderSection renderSectionAt = this.getRelativeFrom(cameraSectionNode, currentSection, direction);
                if (renderSectionAt != null && (!smartCull || !node.hasDirection(direction.getOpposite()))) {
                    if (smartCull && node.hasSourceDirections()) {
                        SectionMesh sectionMesh = currentSection.getSectionMesh();
                        boolean visible = false;
 
                        for (int i = 0; i < DIRECTIONS.length; i++) {
                            if (node.hasSourceDirection(i) && sectionMesh.facesCanSeeEachother(DIRECTIONS[i].getOpposite(), direction)) {
                                visible = true;
                                break;
                            }
                        }
 
                        if (!visible) {
                            continue;
                        }
                    }
 
                    if (smartCull && distantFromCamera) {
                        int renderSectionOriginX = SectionPos.sectionToBlockCoord(SectionPos.x(sectionNode));
                        int renderSectionOriginY = SectionPos.sectionToBlockCoord(SectionPos.y(sectionNode));
                        int renderSectionOriginZ = SectionPos.sectionToBlockCoord(SectionPos.z(sectionNode));
                        boolean maxX = direction.getAxis() == Direction.Axis.X
                            ? cameraSectionCenter.getX() > renderSectionOriginX
                            : cameraSectionCenter.getX() < renderSectionOriginX;
                        boolean maxY = direction.getAxis() == Direction.Axis.Y
                            ? cameraSectionCenter.getY() > renderSectionOriginY
                            : cameraSectionCenter.getY() < renderSectionOriginY;
                        boolean maxZ = direction.getAxis() == Direction.Axis.Z
                            ? cameraSectionCenter.getZ() > renderSectionOriginZ
                            : cameraSectionCenter.getZ() < renderSectionOriginZ;
                        Vector3d checkPos = new Vector3d(
                            renderSectionOriginX + (maxX ? 16 : 0), renderSectionOriginY + (maxY ? 16 : 0), renderSectionOriginZ + (maxZ ? 16 : 0)
                        );
                        Vector3d step = new Vector3d(cameraPos.x, cameraPos.y, cameraPos.z).sub(checkPos).normalize().mul(CEILED_SECTION_DIAGONAL);
                        boolean visible = true;
 
                        while (checkPos.distanceSquared(cameraPos.x, cameraPos.y, cameraPos.z) > 3600.0) {
                            checkPos.add(step);
                            LevelHeightAccessor heightAccessor = this.viewArea.getLevelHeightAccessor();
                            if (checkPos.y > heightAccessor.getMaxY() || checkPos.y < heightAccessor.getMinY()) {
                                break;
                            }
 
                            SectionRenderDispatcher.RenderSection checkSection = this.viewArea
                                .getRenderSectionAt(BlockPos.containing(checkPos.x, checkPos.y, checkPos.z));
                            if (checkSection == null || storage.sectionToNodeMap.get(checkSection) == null) {
                                visible = false;
                                break;
                            }
                        }
 
                        if (!visible) {
                            continue;
                        }
                    }
 
                    SectionOcclusionGraph.Node existingNode = storage.sectionToNodeMap.get(renderSectionAt);
                    if (existingNode != null) {
                        existingNode.addSourceDirection(direction);
                    } else {
                        SectionOcclusionGraph.Node newNode = new SectionOcclusionGraph.Node(renderSectionAt, direction, node.step + 1);
                        newNode.setDirections(node.directions, direction);
                        if (renderSectionAt.hasAllNeighbors()) {
                            queue.add(newNode);
                            storage.sectionToNodeMap.put(renderSectionAt, newNode);
                        } else if (this.isInViewDistance(cameraSectionNode, renderSectionAt.getSectionNode())) {
                            storage.sectionToNodeMap.put(renderSectionAt, newNode);
                            long chunkNode = SectionPos.sectionToChunk(renderSectionAt.getSectionNode());
                            storage.chunksWaitingForNeighbors.computeIfAbsent(chunkNode, l -> new ArrayList<>()).add(renderSectionAt);
                        }
                    }
                }
            }
        }
    }
 
    private boolean isInViewDistance(long cameraSectionNode, long sectionNode) {
        return ChunkTrackingView.isInViewDistance(
            SectionPos.x(cameraSectionNode),
            SectionPos.z(cameraSectionNode),
            this.viewArea.getViewDistance(),
            SectionPos.x(sectionNode),
            SectionPos.z(sectionNode)
        );
    }
 
    private SectionRenderDispatcher.@Nullable RenderSection getRelativeFrom(
        long cameraSectionNode, SectionRenderDispatcher.RenderSection renderSection, Direction direction
    ) {
        long relative = renderSection.getNeighborSectionNode(direction);
        if (!this.isInViewDistance(cameraSectionNode, relative)) {
            return null;
        } else {
            return Mth.abs(SectionPos.y(cameraSectionNode) - SectionPos.y(relative)) > this.viewArea.getViewDistance()
                ? null
                : this.viewArea.getRenderSection(relative);
        }
    }
 
    @VisibleForDebug
    public SectionOcclusionGraph.@Nullable Node getNode(SectionRenderDispatcher.RenderSection section) {
        return this.currentGraph.get().storage.sectionToNodeMap.get(section);
    }
 
    public Octree getOctree() {
        return this.currentGraph.get().storage.sectionTree;
    }
 
    @OnlyIn(Dist.CLIENT)
    private record GraphEvents(LongSet chunksWhichReceivedNeighbors, BlockingQueue<SectionRenderDispatcher.RenderSection> sectionsToPropagateFrom) {
        private GraphEvents() {
            this(new LongOpenHashSet(), new LinkedBlockingQueue<>());
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private record GraphState(SectionOcclusionGraph.GraphStorage storage, SectionOcclusionGraph.GraphEvents events) {
        private GraphState(ViewArea viewArea) {
            this(new SectionOcclusionGraph.GraphStorage(viewArea), new SectionOcclusionGraph.GraphEvents());
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class GraphStorage {
        public final SectionOcclusionGraph.SectionToNodeMap sectionToNodeMap;
        public final Octree sectionTree;
        public final Long2ObjectMap<List<SectionRenderDispatcher.RenderSection>> chunksWaitingForNeighbors;
 
        public GraphStorage(ViewArea viewArea) {
            this.sectionToNodeMap = new SectionOcclusionGraph.SectionToNodeMap(viewArea.sections.length);
            this.sectionTree = new Octree(viewArea.getCameraSectionPos(), viewArea.getViewDistance(), viewArea.sectionGridSizeY, viewArea.level.getMinY());
            this.chunksWaitingForNeighbors = new Long2ObjectOpenHashMap<>();
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    @VisibleForDebug
    public static class Node {
        @VisibleForDebug
        protected final SectionRenderDispatcher.RenderSection section;
        private byte sourceDirections;
        private byte directions;
        @VisibleForDebug
        public final int step;
 
        private Node(SectionRenderDispatcher.RenderSection section, @Nullable Direction sourceDirection, int step) {
            this.section = section;
            if (sourceDirection != null) {
                this.addSourceDirection(sourceDirection);
            }
 
            this.step = step;
        }
 
        private void setDirections(byte oldDirections, Direction direction) {
            this.directions = (byte)(this.directions | oldDirections | 1 << direction.ordinal());
        }
 
        private boolean hasDirection(Direction direction) {
            return (this.directions & 1 << direction.ordinal()) > 0;
        }
 
        private void addSourceDirection(Direction direction) {
            this.sourceDirections = (byte)(this.sourceDirections | this.sourceDirections | 1 << direction.ordinal());
        }
 
        @VisibleForDebug
        public boolean hasSourceDirection(int directionOrdinal) {
            return (this.sourceDirections & 1 << directionOrdinal) > 0;
        }
 
        private boolean hasSourceDirections() {
            return this.sourceDirections != 0;
        }
 
        @Override
        public int hashCode() {
            return Long.hashCode(this.section.getSectionNode());
        }
 
        @Override
        public boolean equals(Object obj) {
            return !(obj instanceof SectionOcclusionGraph.Node other) ? false : this.section.getSectionNode() == other.section.getSectionNode();
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class SectionToNodeMap {
        private final SectionOcclusionGraph.Node[] nodes;
 
        private SectionToNodeMap(int sectionCount) {
            this.nodes = new SectionOcclusionGraph.Node[sectionCount];
        }
 
        public void put(SectionRenderDispatcher.RenderSection renderSection, SectionOcclusionGraph.Node node) {
            this.nodes[renderSection.index] = node;
        }
 
        public SectionOcclusionGraph.@Nullable Node get(SectionRenderDispatcher.RenderSection renderSection) {
            int index = renderSection.index;
            return index >= 0 && index < this.nodes.length ? this.nodes[index] : null;
        }
    }
}

引用的其他类

  • Camera

    • 引用位置: 参数
  • LevelRenderer

    • 引用位置: 方法调用
    • 关联成员: LevelRenderer.offsetFrustum()
  • Octree

    • 引用位置: 构造调用/返回值
    • 关联成员: Octree()
  • ViewArea

    • 引用位置: 参数/字段
  • SectionRenderDispatcher

    • 引用位置: 参数/返回值
  • Frustum

    • 引用位置: 参数
  • BlockPos

    • 引用位置: 方法调用
    • 关联成员: BlockPos.containing()
  • Direction

    • 引用位置: 参数/字段/方法调用
    • 关联成员: Direction.values()
  • SectionPos

    • 引用位置: 方法调用
    • 关联成员: SectionPos.asLong(), SectionPos.blockToSectionCoord(), SectionPos.of(), SectionPos.sectionToBlockCoord(), SectionPos.sectionToChunk(), SectionPos.x(), SectionPos.y(), SectionPos.z()
  • ChunkTrackingView

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

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

    • 引用位置: 方法调用
    • 关联成员: Util.backgroundExecutor()
  • ChunkPos

    • 引用位置: 参数/方法调用
    • 关联成员: ChunkPos.pack()
  • Vec3

    • 引用位置: 参数