Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add visible section context to RenderLevelStageEvent #1472

Merged
merged 1 commit into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@
if (j < k) {
j = k;
}
@@ -1449,5 +_,22 @@
@@ -1449,5 +_,27 @@

public CloudRenderer getCloudRenderer() {
return this.cloudRenderer;
Expand All @@ -251,5 +251,10 @@
+ synchronized (this.globalBlockEntities) {
+ this.globalBlockEntities.forEach(blockEntityConsumer);
+ }
+ }
+
+ @org.jetbrains.annotations.ApiStatus.Internal
+ public Iterable<? extends net.neoforged.neoforge.client.IRenderableSection> getRenderableSections() {
+ return this.visibleSections;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
--- a/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java
+++ b/net/minecraft/client/renderer/chunk/SectionRenderDispatcher.java
@@ -254,7 +_,7 @@
}

@OnlyIn(Dist.CLIENT)
- public class RenderSection {
+ public class RenderSection implements net.neoforged.neoforge.client.IRenderableSection {
public static final int SIZE = 16;
public final int index;
public final AtomicReference<SectionRenderDispatcher.CompiledSection> compiled = new AtomicReference<>(
@@ -399,9 +_,10 @@

public SectionRenderDispatcher.RenderSection.CompileTask createCompileTask(RenderRegionCache p_295324_) {
Expand All @@ -13,6 +22,22 @@
return this.lastRebuildTask;
}

@@ -441,6 +_,15 @@
);
}

+ // Neo: start
+
+ @Override
+ public boolean isEmpty() {
+ return !getCompiled().hasRenderableLayers();
+ }
+
+ // Neo: end
+
@OnlyIn(Dist.CLIENT)
public abstract class CompileTask {
protected final AtomicBoolean isCancelled = new AtomicBoolean(false);
@@ -470,10 +_,17 @@
class RebuildTask extends SectionRenderDispatcher.RenderSection.CompileTask {
@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public static void dispatchRenderStage(RenderLevelStageEvent.Stage stage, LevelR
var mc = Minecraft.getInstance();
var profiler = Profiler.get();
profiler.push(stage.toString());
NeoForge.EVENT_BUS.post(new RenderLevelStageEvent(stage, levelRenderer, poseStack, modelViewMatrix, projectionMatrix, renderTick, mc.getDeltaTracker(), camera, frustum));
NeoForge.EVENT_BUS.post(new RenderLevelStageEvent(stage, levelRenderer, poseStack, modelViewMatrix, projectionMatrix, renderTick, mc.getDeltaTracker(), camera, frustum, levelRenderer.getRenderableSections()));
profiler.pop();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.client;

import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.AABB;

/**
* Describes a chunk section that may be rendered on the GPU.
*
* The renderer may choose to reuse a common backing object
* for this interface under the hood (for performance reasons),
* so the {@link IRenderableSection} and any objects its methods
* return are not guaranteed to be immutable or valid after
* exiting the scope in which its provided.
*/
public interface IRenderableSection {
/**
* {@return the block position at the origin of the section}
*/
BlockPos getOrigin();

/**
* {@return the bounding box of the section}
*/
AABB getBoundingBox();

/**
* {@return true if the compiled section contains no chunk render layers}
*/
boolean isEmpty();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.mojang.blaze3d.vertex.PoseStack;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.GameRenderer;
Expand All @@ -20,6 +21,7 @@
import net.neoforged.bus.api.ICancellableEvent;
import net.neoforged.fml.LogicalSide;
import net.neoforged.fml.event.IModBusEvent;
import net.neoforged.neoforge.client.IRenderableSection;
import net.neoforged.neoforge.client.NeoForgeRenderTypes;
import net.neoforged.neoforge.common.NeoForge;
import org.jetbrains.annotations.Nullable;
Expand All @@ -44,8 +46,9 @@ public class RenderLevelStageEvent extends Event {
private final DeltaTracker partialTick;
private final Camera camera;
private final Frustum frustum;
private final Iterable<? extends IRenderableSection> renderableSections;

public RenderLevelStageEvent(Stage stage, LevelRenderer levelRenderer, @Nullable PoseStack poseStack, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, int renderTick, DeltaTracker partialTick, Camera camera, Frustum frustum) {
public RenderLevelStageEvent(Stage stage, LevelRenderer levelRenderer, @Nullable PoseStack poseStack, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, int renderTick, DeltaTracker partialTick, Camera camera, Frustum frustum, Iterable<? extends IRenderableSection> renderableSections) {
this.stage = stage;
this.levelRenderer = levelRenderer;
this.poseStack = poseStack != null ? poseStack : new PoseStack();
Expand All @@ -55,6 +58,7 @@ public RenderLevelStageEvent(Stage stage, LevelRenderer levelRenderer, @Nullable
this.partialTick = partialTick;
this.camera = camera;
this.frustum = frustum;
this.renderableSections = renderableSections;
}

/**
Expand Down Expand Up @@ -121,6 +125,16 @@ public Frustum getFrustum() {
return frustum;
}

/**
* Returns an iterable of all visible sections.
*
* Calling {@link Iterable#forEach(Consumer)} on the returned iterable allows the underlying renderer
* to optimize how it fetches the visible sections, and is recommended.
*/
public Iterable<? extends IRenderableSection> getRenderableSections() {
return renderableSections;
}

/**
* Use to create a custom {@linkplain RenderLevelStageEvent.Stage stages}.
* Fired after the LevelRenderer has been created.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.Map;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.entity.AbstractHoglinRenderer;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.MobRenderer;
Expand Down Expand Up @@ -185,4 +186,48 @@ class TestBrokenHoglinRendererTypeToken<T extends Mob & HoglinBase> extends Type
});
});
}

@TestHolder(description = { "Tests if rendering custom geometry on visible chunks works", "When the message \"gold block\" is sent in chat, gold blocks should render at the origin of every visible section with blocks" })
static void renderLevelStageWithSectionData(final DynamicTest test) {
test.whenEnabled(listeners -> {
listeners.forge().addListener((final ClientChatEvent chatEvent) -> {
if (chatEvent.getMessage().equalsIgnoreCase("gold block")) {
var player = Minecraft.getInstance().player;
NeoForge.EVENT_BUS.addListener((final RenderLevelStageEvent event) -> {
if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_SOLID_BLOCKS) {
var buffer = Minecraft.getInstance().renderBuffers().bufferSource().getBuffer(Sheets.solidBlockSheet());
var randomSource = new SingleThreadedRandomSource(0);
var state = Blocks.GOLD_BLOCK.defaultBlockState();
var stack = event.getPoseStack();
var camera = event.getCamera().getPosition();
event.getRenderableSections().forEach(section -> {
if (section.isEmpty()) {
return;
}

stack.pushPose();
stack.translate(
section.getOrigin().getX() - camera.x,
section.getOrigin().getY() - camera.y,
section.getOrigin().getZ() - camera.z);
Minecraft.getInstance().getBlockRenderer().renderBatched(
state,
section.getOrigin(),
Minecraft.getInstance().level,
stack,
buffer,
false,
randomSource,
ModelData.EMPTY,
RenderType.solid());
stack.popPose();

test.pass();
});
}
});
}
});
});
}
}
Loading