mirror of
https://github.com/FabricMC/fabric.git
synced 2025-03-23 21:40:02 -04:00
Add WorldRenderEvents (#1182)
* Add WorldRenderEvents * Fix typos * Incorporate PR feedback * Simplify context and block outline events * Update implementation * Ensure the BLOCK_OUTLINE mixin does nothing if BEFORE_BLOCK_OUTLINE mixin is disabled * Document event order in class header * Update fabric-rendering-v1/src/main/java/net/fabricmc/fabric/api/client/rendering/v1/WorldRenderEvents.java Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> * Add environment tag to nested type * More envionment tags Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>
This commit is contained in:
parent
6bec8f284f
commit
c26373137e
5 changed files with 820 additions and 1 deletions
fabric-rendering-v1
build.gradle
src/main/java/net/fabricmc/fabric
api/client/rendering/v1
impl/client/rendering
mixin/client/rendering
|
@ -1,5 +1,5 @@
|
|||
archivesBaseName = "fabric-rendering-v1"
|
||||
version = getSubprojectVersion(project, "1.4.0")
|
||||
version = getSubprojectVersion(project, "1.5.0")
|
||||
|
||||
moduleDependencies(project, [
|
||||
'fabric-api-base'
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.rendering.v1;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.Frustum;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
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.Matrix4f;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
/**
|
||||
* Except as noted below, the properties exposed here match the parameters passed to
|
||||
* {@link WorldRenderer#render(MatrixStack, float, long, boolean, Camera, GameRenderer, LightmapTextureManager, Matrix4f)}.
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public interface WorldRenderContext {
|
||||
/**
|
||||
* The world renderer instance doing the rendering and invoking the event.
|
||||
*
|
||||
* @return WorldRenderer instance invoking the event
|
||||
*/
|
||||
WorldRenderer worldRenderer();
|
||||
|
||||
MatrixStack matrixStack();
|
||||
|
||||
float tickDelta();
|
||||
|
||||
long limitTime();
|
||||
|
||||
boolean blockOutlines();
|
||||
|
||||
Camera camera();
|
||||
|
||||
GameRenderer gameRenderer();
|
||||
|
||||
LightmapTextureManager lightmapTextureManager();
|
||||
|
||||
Matrix4f projectionMatrix();
|
||||
|
||||
/**
|
||||
* Convenient access to {WorldRenderer.world}.
|
||||
*
|
||||
* @return world renderer's client world instance
|
||||
*/
|
||||
ClientWorld world();
|
||||
|
||||
/**
|
||||
* Convenient access to game performance profiler.
|
||||
*
|
||||
* @return the active profiler
|
||||
*/
|
||||
Profiler profiler();
|
||||
|
||||
/**
|
||||
* Test to know if "fabulous" graphics mode is enabled.
|
||||
*
|
||||
* <p>Use this for renders that need to render on top of all translucency to activate or deactivate different
|
||||
* event handlers to get optimal depth testing results. When fabulous is off, it may be better to render
|
||||
* during {@code WorldRenderLastCallback} after clouds and weather are drawn. Conversely, when fabulous mode is on,
|
||||
* it may be better to draw during {@code WorldRenderPostTranslucentCallback}, before the fabulous mode composite
|
||||
* shader runs, depending on which translucent buffer is being targeted.
|
||||
*
|
||||
* @return {@code true} when "fabulous" graphics mode is enabled.
|
||||
*/
|
||||
boolean advancedTranslucency();
|
||||
|
||||
/**
|
||||
* The {@code VertexConsumerProvider} instance being used by the world renderer for most non-terrain renders.
|
||||
* Generally this will be better for most use cases because quads for the same layer can be buffered
|
||||
* incrementally and then drawn all at once by the world renderer.
|
||||
*
|
||||
* <p>IMPORTANT - all vertex coordinates sent to consumers should be relative to the camera to
|
||||
* be consistent with other quads emitted by the world renderer and other mods. If this isn't
|
||||
* 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
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* View frustum, after it is initialized. Will be {@code null} during
|
||||
* {@link WorldRenderEvents#START}.
|
||||
*/
|
||||
@Nullable Frustum frustum();
|
||||
|
||||
/**
|
||||
* Used in {@code BLOCK_OUTLINE} to convey the parameters normally sent to
|
||||
* {@code WorldRenderer.drawBlockOutline}.
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public interface BlockOutlineContext {
|
||||
VertexConsumer vertexConsumer();
|
||||
|
||||
Entity entity();
|
||||
|
||||
double cameraX();
|
||||
|
||||
double cameraY();
|
||||
|
||||
double cameraZ();
|
||||
|
||||
BlockPos blockPos();
|
||||
|
||||
BlockState blockState();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.rendering.v1;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext.BlockOutlineContext;
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Mods should use these events to introduce custom rendering during {@link WorldRenderer#render(net.minecraft.client.util.math.MatrixStack, float, long, boolean, net.minecraft.client.render.Camera, net.minecraft.client.render.GameRenderer, net.minecraft.client.render.LightmapTextureManager, net.minecraft.util.math.Matrix4f)}
|
||||
* without adding complicated and conflict-prone injections there. Using these events also enables 3rd-party renderers
|
||||
* that make large-scale changes to rendering maintain compatibility by calling any broken event invokers directly.
|
||||
*
|
||||
* <p>The order of events each frame is as follows:
|
||||
* <ul><li>START
|
||||
* <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_DEBUG_RENDER
|
||||
* <li>AFTER_TRANSLUCENT
|
||||
* <li>LAST
|
||||
* <li>END</ul>
|
||||
*
|
||||
* <p>These events are not dependent on the Fabric rendering API or Indigo but work when those are present.
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class WorldRenderEvents {
|
||||
private WorldRenderEvents() { }
|
||||
|
||||
/**
|
||||
* Called before world rendering executes. Input parameters are available but frustum is not.
|
||||
* Use this event instead of injecting to the HEAD of {@link WorldRenderer#render} to avoid
|
||||
* compatibility problems with 3rd-party renderer implementations.
|
||||
*
|
||||
* <p>Use for setup of state that is needed during the world render call that
|
||||
* does not depend on the view frustum.
|
||||
*/
|
||||
public static final Event<Start> START = EventFactory.createArrayBacked(Start.class, context -> { }, callbacks -> context -> {
|
||||
for (final Start callback : callbacks) {
|
||||
callback.onStart(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called after view Frustum is computed and all render chunks to be rendered are
|
||||
* identified and rebuilt but before chunks are uploaded to GPU.
|
||||
*
|
||||
* <p>Use for setup of state that depends on view frustum.
|
||||
*/
|
||||
public static final Event<AfterSetup> AFTER_SETUP = EventFactory.createArrayBacked(AfterSetup.class, context -> { }, callbacks -> context -> {
|
||||
for (final AfterSetup callback : callbacks) {
|
||||
callback.afterSetup(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called after the Solid, Cutout and Cutout Mipped terrain layers have been output to the framebuffer.
|
||||
*
|
||||
* <p>Use to render non-translucent terrain to the framebuffer.
|
||||
*
|
||||
* <p>Note that 3rd-party renderers may combine these passes or otherwise alter the
|
||||
* rendering pipeline for sake of performance or features. This can break direct writes to the
|
||||
* framebuffer. Use this event for cases that cannot be satisfied by FabricBakedModel,
|
||||
* BlockEntityRenderer or other existing abstraction. If at all possible, use an existing terrain
|
||||
* RenderLayer instead of outputting to the framebuffer directly with GL calls.
|
||||
*
|
||||
* <p>The consumer is responsible for setup and tear down of GL state appropriate for the intended output.
|
||||
*
|
||||
* <p>Because solid and cutout quads are depth-tested, order of output does not matter except to improve
|
||||
* culling performance, which should not be significant after primary terrain rendering. This means
|
||||
* mods that currently hook calls to individual render layers can simply execute them all at once when
|
||||
* the event is called.
|
||||
*
|
||||
* <p>This event fires before entities and block entities are rendered and may be useful to prepare them.
|
||||
*/
|
||||
public static final Event<BeforeEntities> BEFORE_ENTITIES = EventFactory.createArrayBacked(BeforeEntities.class, context -> { }, callbacks -> context -> {
|
||||
for (final BeforeEntities callback : callbacks) {
|
||||
callback.beforeEntities(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called after entities are rendered and solid entity layers
|
||||
* have been drawn to the main frame buffer target, before
|
||||
* block entity rendering begins.
|
||||
*
|
||||
* <p>Use for global block entity render setup, or
|
||||
* to append block-related quads to the entity consumers using the
|
||||
* {@VertexConsumerProvider} from the provided context. This
|
||||
* will generally give better (if not perfect) results
|
||||
* for non-terrain translucency vs. drawing directly later on.
|
||||
*/
|
||||
public static final Event<AfterEntities> AFTER_ENTITIES = EventFactory.createArrayBacked(AfterEntities.class, context -> { }, callbacks -> context -> {
|
||||
for (final AfterEntities callback : callbacks) {
|
||||
callback.afterEntities(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called before default block outline rendering and before checks are
|
||||
* done to determine if it should happen. Can optionally cancel the default
|
||||
* rendering but all event handlers will always be called.
|
||||
*
|
||||
* <p>Use this to decorate or replace the default block outline rendering
|
||||
* for specific modded blocks or when the need for a block outline render
|
||||
* would not be detected. Normally, outline rendering will not happen for
|
||||
* entities, fluids, or other game objects that do not register a block-type hit.
|
||||
*
|
||||
* <p>Returning false from any event subscriber will cancel the default block
|
||||
* outline render and suppress the {@code BLOCK_RENDER} event. This has no
|
||||
* effect on other subscribers to this event - all subscribers will always be called.
|
||||
* Canceling here is appropriate when there is still a valid block hit (with a fluid,
|
||||
* for example) and you don't want the block outline render to appear.
|
||||
*
|
||||
* <p>This event should NOT be used for general-purpose replacement of
|
||||
* the default block outline rendering because it will interfere with mod-specific
|
||||
* renders. Mods that replace the default block outline for specific blocks
|
||||
* should instead subscribe to {@link #BLOCK_OUTLINE}.
|
||||
*/
|
||||
public static final Event<BeforeBlockOutline> BEFORE_BLOCK_OUTLINE = EventFactory.createArrayBacked(BeforeBlockOutline.class, (context, hit) -> true, callbacks -> (context, hit) -> {
|
||||
boolean shouldRender = true;
|
||||
|
||||
for (final BeforeBlockOutline callback : callbacks) {
|
||||
if (!callback.beforeBlockOutline(context, hit)) {
|
||||
shouldRender = false;
|
||||
}
|
||||
}
|
||||
|
||||
return shouldRender;
|
||||
});
|
||||
|
||||
/**
|
||||
* Called after block outline render checks are made and before the
|
||||
* default block outline render runs. Will NOT be called if the default outline
|
||||
* render was cancelled in {@link #BEFORE_BLOCK_OUTLINE}.
|
||||
*
|
||||
* <p>Use this to replace the default block outline rendering for specific blocks that
|
||||
* need special outline rendering or to add information that doesn't replace the block outline.
|
||||
* Subscribers cannot affect each other or detect if another subscriber is also
|
||||
* handling a specific block. If two subscribers render for the same block, both
|
||||
* renders will appear.
|
||||
*
|
||||
* <p>Returning false from any event subscriber will cancel the default block
|
||||
* outline render. This has no effect on other subscribers to this event -
|
||||
* all subscribers will always be called. Canceling is appropriate when the
|
||||
* subscriber replacing the default block outline render for a specific block.
|
||||
*
|
||||
* <p>This event is not appropriate for mods that replace the default block
|
||||
* outline render for <em>all</em> blocks because all event subscribers will
|
||||
* always render - only the default outline render can be cancelled. That should
|
||||
* be accomplished by mixin to the block outline render routine itself, typically
|
||||
* by targeting {@link WorldRenderer#drawShapeOutline}.
|
||||
*/
|
||||
public static final Event<BlockOutline> BLOCK_OUTLINE = EventFactory.createArrayBacked(BlockOutline.class, (worldRenderContext, blockOutlieContext) -> true, callbacks -> (worldRenderContext, blockOutlieContext) -> {
|
||||
boolean shouldRender = true;
|
||||
|
||||
for (final BlockOutline callback : callbacks) {
|
||||
if (!callback.onBlockOutline(worldRenderContext, blockOutlieContext)) {
|
||||
shouldRender = false;
|
||||
}
|
||||
}
|
||||
|
||||
return shouldRender;
|
||||
});
|
||||
|
||||
/**
|
||||
* Called before vanilla debug renderers are output to the framebuffer.
|
||||
* This happens very soon after entities, block breaking and most other
|
||||
* non-translucent renders but before translucency is drawn.
|
||||
*
|
||||
* <p>Unlike most other events, renders in this event are expected to be drawn
|
||||
* directly and immediately to the framebuffer. The OpenGL render state view
|
||||
* matrix will be transformed to match the camera view before the event is called.
|
||||
*
|
||||
* <p>Use to drawn lines, overlays and other content similar to vanilla
|
||||
* debug renders.
|
||||
*/
|
||||
public static final Event<DebugRender> BEFORE_DEBUG_RENDER = EventFactory.createArrayBacked(DebugRender.class, context -> { }, callbacks -> context -> {
|
||||
for (final DebugRender callback : callbacks) {
|
||||
callback.beforeDebugRender(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called after entity, terrain, and particle translucent layers have been
|
||||
* drawn to the framebuffer but before translucency combine has happened
|
||||
* in fabulous mode.
|
||||
*
|
||||
* <p>Use for drawing overlays or other effects on top of those targets
|
||||
* (or the main target when fabulous isn't active) before clouds and weather
|
||||
* are drawn. However, note that {@code WorldRenderPostEntityCallback} will
|
||||
* offer better results in most use cases.
|
||||
*
|
||||
* <p>Vertex consumers are not available in this event because all buffered quads
|
||||
* are drawn before this event is called. Any rendering here must be drawn
|
||||
* directly to the frame buffer. The render state matrix will not include
|
||||
* camera transformation, so {@link #LAST} may be preferable if that is wanted.
|
||||
*/
|
||||
public static final Event<AfterTranslucent> AFTER_TRANSLUCENT = EventFactory.createArrayBacked(AfterTranslucent.class, context -> { }, callbacks -> context -> {
|
||||
for (final AfterTranslucent callback : callbacks) {
|
||||
callback.afterTranslucent(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called after all framebuffer writes are complete but before all world
|
||||
* rendering is torn down.
|
||||
*
|
||||
* <p>Unlike most other events, renders in this event are expected to be drawn
|
||||
* directly and immediately to the framebuffer. The OpenGL render state view
|
||||
* matrix will be transformed to match the camera view before the event is called.
|
||||
*
|
||||
* <p>Use to draw content that should appear on top of the world before hand and GUI rendering occur.
|
||||
*/
|
||||
public static final Event<Last> LAST = EventFactory.createArrayBacked(Last.class, context -> { }, callbacks -> context -> {
|
||||
for (final Last callback : callbacks) {
|
||||
callback.onLast(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Called after all world rendering is complete and changes to GL state are unwound.
|
||||
*
|
||||
* <p>Use to draw overlays that handle GL state management independently or to tear
|
||||
* down transient state in event handlers or as a hook that precedes hand/held item
|
||||
* and GUI rendering.
|
||||
*/
|
||||
public static final Event<End> END = EventFactory.createArrayBacked(End.class, context -> { }, callbacks -> context -> {
|
||||
for (final End callback : callbacks) {
|
||||
callback.onEnd(context);
|
||||
}
|
||||
});
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface Start {
|
||||
void onStart(WorldRenderContext context);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface AfterSetup {
|
||||
void afterSetup(WorldRenderContext context);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface BeforeEntities {
|
||||
void beforeEntities(WorldRenderContext context);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface AfterEntities {
|
||||
void afterEntities(WorldRenderContext context);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface BeforeBlockOutline {
|
||||
/**
|
||||
* Event signature for {@link WorldRenderEvents#BEFORE_BLOCK_OUTLINE}.
|
||||
*
|
||||
* @param context Access to state and parameters available during world rendering.
|
||||
* @param hitResult The game object currently under the crosshair target.
|
||||
* Normally equivalent to {@link MinecraftClient#crosshairTarget}. Provided for convenience.
|
||||
* @return true if vanilla block outline rendering should happen.
|
||||
* Returning false prevents {@link WorldRenderEvents#BLOCK_OUTLINE} from invoking
|
||||
* and also skips the vanilla block outline render, but has no effect on other subscribers to this event.
|
||||
*/
|
||||
boolean beforeBlockOutline(WorldRenderContext context, @Nullable HitResult hitResult);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface BlockOutline {
|
||||
boolean onBlockOutline(WorldRenderContext worldRenderContext, BlockOutlineContext blockOutlieContext);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface DebugRender {
|
||||
void beforeDebugRender(WorldRenderContext context);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface AfterTranslucent {
|
||||
void afterTranslucent(WorldRenderContext context);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface Last {
|
||||
void onLast(WorldRenderContext context);
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@FunctionalInterface
|
||||
public interface End {
|
||||
void onEnd(WorldRenderContext context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.rendering;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.Frustum;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
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.Matrix4f;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public final class WorldRenderContextImpl implements WorldRenderContext.BlockOutlineContext, WorldRenderContext {
|
||||
private WorldRenderer worldRenderer;
|
||||
private MatrixStack matrixStack;
|
||||
private float tickDelta;
|
||||
private long limitTime;
|
||||
private boolean blockOutlines;
|
||||
private Camera camera;
|
||||
private Frustum frustum;
|
||||
private GameRenderer gameRenderer;
|
||||
private LightmapTextureManager lightmapTextureManager;
|
||||
private Matrix4f projectionMatrix;
|
||||
private VertexConsumerProvider consumers;
|
||||
private Profiler profiler;
|
||||
private boolean advancedTranslucency;
|
||||
private ClientWorld world;
|
||||
|
||||
private VertexConsumer vertexConsumer;
|
||||
private Entity entity;
|
||||
private double cameraX;
|
||||
private double cameraY;
|
||||
private double cameraZ;
|
||||
private BlockPos blockPos;
|
||||
private BlockState blockState;
|
||||
|
||||
public boolean renderBlockOutline = true;
|
||||
|
||||
public void prepare(
|
||||
WorldRenderer worldRenderer,
|
||||
MatrixStack matrixStack,
|
||||
float tickDelta,
|
||||
long limitTime,
|
||||
boolean blockOutlines,
|
||||
Camera camera,
|
||||
GameRenderer gameRenderer,
|
||||
LightmapTextureManager lightmapTextureManager,
|
||||
Matrix4f projectionMatrix,
|
||||
VertexConsumerProvider consumers,
|
||||
Profiler profiler,
|
||||
boolean advancedTranslucency,
|
||||
ClientWorld world
|
||||
) {
|
||||
this.worldRenderer = worldRenderer;
|
||||
this.matrixStack = matrixStack;
|
||||
this.tickDelta = tickDelta;
|
||||
this.limitTime = limitTime;
|
||||
this.blockOutlines = blockOutlines;
|
||||
this.camera = camera;
|
||||
this.gameRenderer = gameRenderer;
|
||||
this.lightmapTextureManager = lightmapTextureManager;
|
||||
this.projectionMatrix = projectionMatrix;
|
||||
this.consumers = consumers;
|
||||
this.profiler = profiler;
|
||||
this.advancedTranslucency = advancedTranslucency;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public void setFrustum(Frustum frustum) {
|
||||
this.frustum = frustum;
|
||||
}
|
||||
|
||||
public void prepareBlockOutline(
|
||||
VertexConsumer vertexConsumer,
|
||||
Entity entity,
|
||||
double cameraX,
|
||||
double cameraY,
|
||||
double cameraZ,
|
||||
BlockPos blockPos,
|
||||
BlockState blockState
|
||||
) {
|
||||
this.vertexConsumer = vertexConsumer;
|
||||
this.entity = entity;
|
||||
this.cameraX = cameraX;
|
||||
this.cameraY = cameraY;
|
||||
this.cameraZ = cameraZ;
|
||||
this.blockPos = blockPos;
|
||||
this.blockState = blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldRenderer worldRenderer() {
|
||||
return worldRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatrixStack matrixStack() {
|
||||
return matrixStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float tickDelta() {
|
||||
return tickDelta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long limitTime() {
|
||||
return limitTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean blockOutlines() {
|
||||
return blockOutlines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Camera camera() {
|
||||
return camera;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matrix4f projectionMatrix() {
|
||||
return projectionMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientWorld world() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frustum frustum() {
|
||||
return frustum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumerProvider consumers() {
|
||||
return consumers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameRenderer gameRenderer() {
|
||||
return gameRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LightmapTextureManager lightmapTextureManager() {
|
||||
return lightmapTextureManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Profiler profiler() {
|
||||
return profiler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advancedTranslucency() {
|
||||
return advancedTranslucency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer vertexConsumer() {
|
||||
return vertexConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity entity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double cameraX() {
|
||||
return cameraX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double cameraY() {
|
||||
return cameraY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double cameraZ() {
|
||||
return cameraZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos blockPos() {
|
||||
return blockPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState blockState() {
|
||||
return blockState;
|
||||
}
|
||||
}
|
|
@ -17,16 +17,149 @@
|
|||
package net.fabricmc.fabric.mixin.client.rendering;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
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.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gl.ShaderEffect;
|
||||
import net.minecraft.client.render.BufferBuilderStorage;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.Frustum;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
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.Matrix4f;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.InvalidateRenderStateCallback;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
|
||||
import net.fabricmc.fabric.impl.client.rendering.WorldRenderContextImpl;
|
||||
|
||||
@Mixin(WorldRenderer.class)
|
||||
public abstract class MixinWorldRenderer {
|
||||
@Shadow private BufferBuilderStorage bufferBuilders;
|
||||
@Shadow private ClientWorld world;
|
||||
@Shadow private ShaderEffect transparencyShader;
|
||||
@Shadow private MinecraftClient client;
|
||||
@Unique private final WorldRenderContextImpl context = new WorldRenderContextImpl();
|
||||
@Unique private boolean didRenderParticles;
|
||||
|
||||
@Inject(method = "render", at = @At("HEAD"))
|
||||
private void beforeRender(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) {
|
||||
context.prepare((WorldRenderer) (Object) this, matrices, tickDelta, limitTime, renderBlockOutline, camera, gameRenderer, lightmapTextureManager, matrix4f, bufferBuilders.getEntityVertexConsumers(), world.getProfiler(), transparencyShader != null, world);
|
||||
WorldRenderEvents.START.invoker().onStart(context);
|
||||
didRenderParticles = false;
|
||||
}
|
||||
|
||||
@Inject(method = "setupTerrain", at = @At("RETURN"))
|
||||
private void afterTerrainSetup(Camera camera, Frustum frustum, boolean hasForcedFrustum, int frame, boolean spectator, CallbackInfo ci) {
|
||||
context.setFrustum(frustum);
|
||||
WorldRenderEvents.AFTER_SETUP.invoker().afterSetup(context);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/render/WorldRenderer;renderLayer(Lnet/minecraft/client/render/RenderLayer;Lnet/minecraft/client/util/math/MatrixStack;DDD)V",
|
||||
ordinal = 2,
|
||||
shift = Shift.AFTER
|
||||
)
|
||||
)
|
||||
private void afterTerrainSolid(CallbackInfo ci) {
|
||||
WorldRenderEvents.BEFORE_ENTITIES.invoker().beforeEntities(context);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "CONSTANT", args = "stringValue=blockentities", ordinal = 0))
|
||||
private void afterEntities(CallbackInfo ci) {
|
||||
WorldRenderEvents.AFTER_ENTITIES.invoker().afterEntities(context);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(
|
||||
value = "FIELD",
|
||||
target = "Lnet/minecraft/client/MinecraftClient;crosshairTarget:Lnet/minecraft/util/hit/HitResult;",
|
||||
shift = At.Shift.AFTER,
|
||||
ordinal = 1
|
||||
)
|
||||
)
|
||||
private void beforeRenderOutline(CallbackInfo ci) {
|
||||
context.renderBlockOutline = WorldRenderEvents.BEFORE_BLOCK_OUTLINE.invoker().beforeBlockOutline(context, client.crosshairTarget);
|
||||
}
|
||||
|
||||
@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, CallbackInfo ci) {
|
||||
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(vertexConsumer, entity, cameraX, cameraY, cameraZ, blockPos, blockState);
|
||||
|
||||
if (!WorldRenderEvents.BLOCK_OUTLINE.invoker().onBlockOutline(context, context)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/render/debug/DebugRenderer;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;DDD)V",
|
||||
ordinal = 0
|
||||
)
|
||||
)
|
||||
private void beforeDebugRender(CallbackInfo ci) {
|
||||
WorldRenderEvents.BEFORE_DEBUG_RENDER.invoker().beforeDebugRender(context);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/particle/ParticleManager;renderParticles(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/render/LightmapTextureManager;Lnet/minecraft/client/render/Camera;F)V"
|
||||
)
|
||||
)
|
||||
private void onRenderParticles(CallbackInfo ci) {
|
||||
// set a flag so we know the next pushMatrix call is after particles
|
||||
didRenderParticles = true;
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;pushMatrix()V"))
|
||||
private void beforeClouds(CallbackInfo ci) {
|
||||
if (didRenderParticles) {
|
||||
didRenderParticles = false;
|
||||
WorldRenderEvents.AFTER_TRANSLUCENT.invoker().afterTranslucent(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "render",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/render/WorldRenderer;renderChunkDebugInfo(Lnet/minecraft/client/render/Camera;)V"
|
||||
)
|
||||
)
|
||||
private void onChunkDebugRender(CallbackInfo ci) {
|
||||
WorldRenderEvents.LAST.invoker().onLast(context);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At("RETURN"))
|
||||
private void afterRender(CallbackInfo ci) {
|
||||
WorldRenderEvents.END.invoker().onEnd(context);
|
||||
}
|
||||
|
||||
@Inject(method = "reload", at = @At("HEAD"))
|
||||
private void onReload(CallbackInfo ci) {
|
||||
InvalidateRenderStateCallback.EVENT.invoker().onInvalidate();
|
||||
|
|
Loading…
Add table
Reference in a new issue