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

Fix block outline rendering #4286

Merged
merged 3 commits into from
Dec 12, 2024
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 @@ -44,12 +44,6 @@ public interface WorldRenderContext {
*/
WorldRenderer worldRenderer();

/**
* The matrix stack is only not null in {@link WorldRenderEvents#AFTER_ENTITIES} or later events.
*/
@Nullable
MatrixStack matrixStack();

RenderTickCounter tickCounter();

boolean blockOutlines();
Expand All @@ -58,10 +52,10 @@ public interface WorldRenderContext {

GameRenderer gameRenderer();

Matrix4f projectionMatrix();

Matrix4f positionMatrix();

Matrix4f projectionMatrix();

/**
* Convenient access to {WorldRenderer.world}.
*
Expand Down Expand Up @@ -92,30 +86,39 @@ public interface WorldRenderContext {
* possible, caller should use a separate "immediate" instance.
*
* <p>This property is {@code null} before {@link WorldRenderEvents#BEFORE_ENTITIES} and after
* {@link WorldRenderEvents#BEFORE_DEBUG_RENDER} because the consumer buffers are not available before or
* {@link WorldRenderEvents#BLOCK_OUTLINE} (translucent) because the consumer buffers are not available before or
* drawn after that in vanilla world rendering. Renders that cannot draw in one of the supported events
* must be drawn directly to the frame buffer, preferably in {@link WorldRenderEvents#LAST} to avoid being
* overdrawn or cleared.
*/
@Nullable VertexConsumerProvider consumers();
@Nullable
VertexConsumerProvider consumers();

/**
* View frustum, after it is initialized. Will be {@code null} during
* {@link WorldRenderEvents#START}.
*/
@Nullable Frustum frustum();
@Nullable
Frustum frustum();

/**
* Used in {@code BLOCK_OUTLINE} to convey the parameters normally sent to
* The matrix stack is only not null in {@link WorldRenderEvents#AFTER_ENTITIES} or later events.
*/
@Nullable
MatrixStack matrixStack();

/**
* Meant to be used in {@link WorldRenderEvents#BEFORE_BLOCK_OUTLINE} and {@link WorldRenderEvents#BLOCK_OUTLINE}.
* @return {@code true} if the current block outline is being rendered after translucent terrain; {@code false} if
* it is being rendered after solid terrain
*/
boolean translucentBlockOutline();

/**
* Used in {@link WorldRenderEvents#BLOCK_OUTLINE} to convey the parameters normally sent to
* {@code WorldRenderer.drawBlockOutline}.
*/
interface BlockOutlineContext {
/**
* @deprecated Use {@link #consumers()} directly.
*/
@Deprecated
VertexConsumer vertexConsumer();
PepperCode1 marked this conversation as resolved.
Show resolved Hide resolved

Entity entity();

double cameraX();
Expand All @@ -127,5 +130,11 @@ interface BlockOutlineContext {
BlockPos blockPos();

BlockState blockState();

/**
* @deprecated Use {@link #consumers()} directly.
*/
@Deprecated
VertexConsumer vertexConsumer();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@
* <li>AFTER_SETUP
* <li>BEFORE_ENTITIES
* <li>AFTER_ENTITIES
* <li>BEFORE_BLOCK_OUTLINE
* <li>BLOCK_OUTLINE (If not cancelled in BEFORE_BLOCK_OUTLINE)
* <li>BEFORE_BLOCK_OUTLINE (non-translucent)
* <li>BLOCK_OUTLINE (if not cancelled in non-translucent BEFORE_BLOCK_OUTLINE and vanilla checks pass)
* <li>BEFORE_DEBUG_RENDER
* <li>BEFORE_BLOCK_OUTLINE (translucent)
* <li>BLOCK_OUTLINE (if not cancelled in translucent BEFORE_BLOCK_OUTLINE and vanilla checks pass)
* <li>AFTER_TRANSLUCENT
* <li>LAST
* <li>END</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package net.fabricmc.fabric.impl.client.rendering;

import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;

import net.minecraft.block.BlockState;
Expand All @@ -37,17 +38,21 @@
public final class WorldRenderContextImpl implements WorldRenderContext.BlockOutlineContext, WorldRenderContext {
private WorldRenderer worldRenderer;
private RenderTickCounter tickCounter;
private MatrixStack matrixStack;
private boolean blockOutlines;
private Camera camera;
private Frustum frustum;
private GameRenderer gameRenderer;
private Matrix4f projectionMatrix;
private Matrix4f positionMatrix;
private Matrix4f projectionMatrix;
private VertexConsumerProvider consumers;
private boolean advancedTranslucency;
private ClientWorld world;

@Nullable
private Frustum frustum;
@Nullable
private MatrixStack matrixStack;
private boolean translucentBlockOutline;

private Entity entity;
private double cameraX;
private double cameraY;
Expand All @@ -59,27 +64,29 @@ public final class WorldRenderContextImpl implements WorldRenderContext.BlockOut

public void prepare(
WorldRenderer worldRenderer,
RenderTickCounter delta,
RenderTickCounter tickCounter,
boolean blockOutlines,
Camera camera,
GameRenderer gameRenderer,
Matrix4f projectionMatrix,
Matrix4f positionMatrix,
Matrix4f projectionMatrix,
VertexConsumerProvider consumers,
boolean advancedTranslucency,
ClientWorld world
) {
this.worldRenderer = worldRenderer;
this.tickCounter = delta;
this.matrixStack = null;
this.tickCounter = tickCounter;
this.blockOutlines = blockOutlines;
this.camera = camera;
this.gameRenderer = gameRenderer;
this.projectionMatrix = projectionMatrix;
this.positionMatrix = positionMatrix;
this.projectionMatrix = projectionMatrix;
this.consumers = consumers;
this.advancedTranslucency = advancedTranslucency;
this.world = world;

frustum = null;
matrixStack = null;
}

public void setFrustum(Frustum frustum) {
Expand All @@ -90,6 +97,10 @@ public void setMatrixStack(MatrixStack matrixStack) {
this.matrixStack = matrixStack;
}

public void setTranslucentBlockOutline(boolean translucentBlockOutline) {
this.translucentBlockOutline = translucentBlockOutline;
}

public void prepareBlockOutline(
Entity entity,
double cameraX,
Expand All @@ -111,11 +122,6 @@ public WorldRenderer worldRenderer() {
return worldRenderer;
}

@Override
public MatrixStack matrixStack() {
return matrixStack;
}

@Override
public RenderTickCounter tickCounter() {
return this.tickCounter;
Expand All @@ -132,23 +138,28 @@ public Camera camera() {
}

@Override
public Matrix4f projectionMatrix() {
return projectionMatrix;
public GameRenderer gameRenderer() {
return gameRenderer;
}

@Override
public Matrix4f positionMatrix() {
return positionMatrix;
}

@Override
public Matrix4f projectionMatrix() {
return projectionMatrix;
}

@Override
public ClientWorld world() {
return world;
}

@Override
public Frustum frustum() {
return frustum;
public boolean advancedTranslucency() {
return advancedTranslucency;
}

@Override
Expand All @@ -157,18 +168,20 @@ public VertexConsumerProvider consumers() {
}

@Override
public GameRenderer gameRenderer() {
return gameRenderer;
@Nullable
public Frustum frustum() {
return frustum;
}

@Override
public boolean advancedTranslucency() {
return advancedTranslucency;
@Nullable
public MatrixStack matrixStack() {
return matrixStack;
}

@Override
public VertexConsumer vertexConsumer() {
return consumers.getBuffer(RenderLayer.getLines());
public boolean translucentBlockOutline() {
return translucentBlockOutline;
}

@Override
Expand Down Expand Up @@ -200,4 +213,10 @@ public BlockPos blockPos() {
public BlockState blockState() {
return blockState;
}

@Deprecated
@Override
public VertexConsumer vertexConsumer() {
return consumers.getBuffer(RenderLayer.getLines());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import net.minecraft.block.BlockState;
Expand All @@ -39,15 +38,13 @@
import net.minecraft.client.render.FrameGraphBuilder;
import net.minecraft.client.render.Frustum;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderPass;
import net.minecraft.client.render.RenderTickCounter;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.ObjectAllocator;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;

Expand All @@ -72,7 +69,7 @@ public abstract class WorldRendererMixin {

@Inject(method = "render", at = @At("HEAD"))
private void beforeRender(ObjectAllocator objectAllocator, RenderTickCounter tickCounter, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, CallbackInfo ci) {
context.prepare((WorldRenderer) (Object) this, tickCounter, renderBlockOutline, camera, gameRenderer, projectionMatrix, positionMatrix, bufferBuilders.getEntityVertexConsumers(), MinecraftClient.isFabulousGraphicsOrBetter(), world);
context.prepare((WorldRenderer) (Object) this, tickCounter, renderBlockOutline, camera, gameRenderer, positionMatrix, projectionMatrix, bufferBuilders.getEntityVertexConsumers(), MinecraftClient.isFabulousGraphicsOrBetter(), world);
WorldRenderEvents.START.invoker().onStart(context);
}

Expand Down Expand Up @@ -118,41 +115,28 @@ private void afterEntities(CallbackInfo ci) {
WorldRenderEvents.AFTER_ENTITIES.invoker().afterEntities(context);
}

@Inject(
method = "renderTargetBlockOutline",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/client/MinecraftClient;crosshairTarget:Lnet/minecraft/util/hit/HitResult;",
shift = At.Shift.AFTER,
ordinal = 0
)
)
private void beforeRenderOutline(CallbackInfo ci) {
@Inject(method = "renderTargetBlockOutline", at = @At("HEAD"))
private void beforeRenderOutline(Camera camera, VertexConsumerProvider.Immediate vertexConsumers, MatrixStack matrices, boolean translucent, CallbackInfo ci) {
context.setTranslucentBlockOutline(translucent);
context.renderBlockOutline = WorldRenderEvents.BEFORE_BLOCK_OUTLINE.invoker().beforeBlockOutline(context, client.crosshairTarget);
}

@SuppressWarnings("ConstantConditions")
@Inject(method = "drawBlockOutline", at = @At("HEAD"), cancellable = true)
private void onDrawBlockOutline(MatrixStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double cameraX, double cameraY, double cameraZ, BlockPos blockPos, BlockState blockState, int color, CallbackInfo ci) {
@Inject(method = "renderTargetBlockOutline", at = @At(value = "INVOKE", target = "net/minecraft/client/option/GameOptions.getHighContrastBlockOutline()Lnet/minecraft/client/option/SimpleOption;"), cancellable = true)
private void onDrawBlockOutline(Camera camera, VertexConsumerProvider.Immediate vertexConsumers, MatrixStack matrices, boolean translucent, CallbackInfo ci, @Local BlockPos blockPos, @Local BlockState blockState, @Local Vec3d cameraPos) {
if (!context.renderBlockOutline) {
// Was cancelled before we got here, so do not
// fire the BLOCK_OUTLINE event per contract of the API.
ci.cancel();
} else {
context.prepareBlockOutline(entity, cameraX, cameraY, cameraZ, blockPos, blockState);

if (!WorldRenderEvents.BLOCK_OUTLINE.invoker().onBlockOutline(context, context)) {
ci.cancel();
}
return;
}
}

@SuppressWarnings("ConstantConditions")
@ModifyVariable(method = "drawBlockOutline", at = @At("HEAD"))
private VertexConsumer resetBlockOutlineBuffer(VertexConsumer vertexConsumer) {
// The original VertexConsumer may have been ended during the block outlines event, so we
// have to re-request it to prevent a crash when the vanilla block overlay is submitted.
return context.consumers().getBuffer(RenderLayer.getLines());
context.prepareBlockOutline(camera.getFocusedEntity(), cameraPos.x, cameraPos.y, cameraPos.z, blockPos, blockState);

if (!WorldRenderEvents.BLOCK_OUTLINE.invoker().onBlockOutline(context, context)) {
vertexConsumers.drawCurrentLayer();
ci.cancel();
}
}

@Inject(
Expand Down