Fluid rendering fixes (#3593)

* Fluid rendering fixes

- Fix default overlay block check using TransparentBlock instead of TranslucentBlock
- Fix setBlockTransparency/isBlockTransparent not being thread-safe
- Reuse the same render handler objects for water and lava

* Fix implementation issues

- Fix custom geometry being buffered twice if FluidRenderHandler#renderFluid is invoked directly
- Fix calling FluidRenderHandler.super.renderFluid not using passed arguments to calculate color
- Fix calling FluidRenderHandler.super.renderFluid more than once producing incorrect geometry
- Fix fluids with no handler never receiving water overlay instead of using default behavior

* Add way to render fluid with non-vanilla default

- Fix testmod
This commit is contained in:
PepperCode1 2024-02-18 05:00:58 -08:00 committed by GitHub
parent 6dfc9a786d
commit e761c6698e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 264 additions and 139 deletions

View file

@ -20,13 +20,14 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.block.FluidRenderer;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.fluid.FluidState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRenderHandlerRegistryImpl;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRenderingImpl;
/**
* Interface for handling the rendering of a FluidState.
@ -67,14 +68,13 @@ public interface FluidRenderHandler {
}
/**
* Tessellate your fluid. This method will be invoked before the default
* fluid renderer. By default, it will call the default fluid renderer. Call
* {@code FluidRenderHandler.super.renderFluid} if you want to render over
* the default fluid renderer.
*
* <p>Note that this method must *only* return {@code true} if at least one
* face is tessellated. If no faces are tessellated this method must return
* {@code false}.
* Tessellate your fluid. By default, this method will call the default
* fluid renderer. Call {@code FluidRenderHandler.super.renderFluid} if
* you want to render over the default fluid renderer. This is the
* intended way to render default geometry; calling
* {@link FluidRenderer#render} is not supported. When rendering default
* geometry, the current handler will be used instead of looking up
* a new one for the passed fluid state.
*
* @param pos The position in the world, of the fluid to render.
* @param world The world the fluid is in
@ -83,7 +83,7 @@ public interface FluidRenderHandler {
* @param fluidState The fluid state being rendered.
*/
default void renderFluid(BlockPos pos, BlockRenderView world, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
((FluidRenderHandlerRegistryImpl) FluidRenderHandlerRegistry.INSTANCE).renderFluid(pos, world, vertexConsumer, blockState, fluidState);
FluidRenderingImpl.renderDefault(this, world, pos, vertexConsumer, blockState, fluidState);
}
/**

View file

@ -0,0 +1,72 @@
/*
* 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.render.fluid.v1;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.block.FluidRenderer;
import net.minecraft.fluid.FluidState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRenderingImpl;
/**
* A class containing some utilities for rendering fluids.
*/
public final class FluidRendering {
private FluidRendering() {
}
/**
* Renders a fluid using the given handler, default renderer, and context. Internally, this just invokes
* {@link FluidRenderHandler#renderFluid}, but the passed default renderer is invoked instead of the vanilla
* renderer whenever the handler requests default geometry to be rendered.
*
* @param handler the render handler to invoke {@link FluidRenderHandler#renderFluid} on
* @param world the world
* @param pos the pos
* @param vertexConsumer the vertex consumer
* @param blockState the block state
* @param fluidState the fluid state
* @param defaultRenderer the renderer to use whenever the handler requests default geometry
*/
public static void render(FluidRenderHandler handler, BlockRenderView world, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState, DefaultRenderer defaultRenderer) {
FluidRenderingImpl.render(handler, world, pos, vertexConsumer, blockState, fluidState, defaultRenderer);
}
public interface DefaultRenderer {
/**
* Render the default geometry when it is requested by {@link FluidRenderHandler#renderFluid}. The default
* implementation invokes the vanilla renderer. Calling {@link FluidRenderer#render} directly is not supported
* but using {@code DefaultRenderer.super.render} is supported. Note that the parameter values passed to this
* call are provided by the render handler, meaning they are not necessarily the same as those provided to the
* initial rendering call. As per the documentation of {@link FluidRenderHandler#renderFluid}, a new handler
* should not be retrieved and only the passed one should be used.
*
* @param handler the handler that {@link FluidRenderHandler#renderFluid} was invoked on
* @param world the world
* @param pos the pos
* @param vertexConsumer the vertex consumer
* @param blockState the block state
* @param fluidState the fluid state
*/
default void render(FluidRenderHandler handler, BlockRenderView world, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
FluidRenderingImpl.renderVanillaDefault(handler, world, pos, vertexConsumer, blockState, fluidState);
}
}
}

View file

@ -85,7 +85,7 @@ public class SimpleFluidRenderHandler implements FluidRenderHandler {
*/
public SimpleFluidRenderHandler(Identifier stillTexture, Identifier flowingTexture, @Nullable Identifier overlayTexture, int tint) {
this.stillTexture = Objects.requireNonNull(stillTexture, "stillTexture");
this.flowingTexture = Objects.requireNonNull(flowingTexture, "flowingTexture");;
this.flowingTexture = Objects.requireNonNull(flowingTexture, "flowingTexture");
this.overlayTexture = overlayTexture;
this.sprites = new Sprite[overlayTexture == null ? 2 : 3];
this.tint = tint;

View file

@ -16,7 +16,8 @@
package net.fabricmc.fabric.impl.client.rendering.fluid;
import net.minecraft.block.BlockState;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.texture.Sprite;
import net.minecraft.fluid.FluidState;
import net.minecraft.util.math.BlockPos;
@ -24,41 +25,32 @@ import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
public class FluidRendererHookContainer {
public BlockRenderView view;
public BlockPos pos;
public BlockState blockState;
public FluidState fluidState;
public FluidRenderHandler handler;
public class FluidRenderHandlerInfo {
public final Sprite[] sprites = new Sprite[2];
public Sprite overlay;
@Nullable
public FluidRenderHandler handler;
public boolean hasOverlay;
public Sprite overlaySprite;
public void getSprites(BlockRenderView world, BlockPos pos, FluidState fluidState) {
if (handler != null) {
Sprite[] sprites = handler.getFluidSprites(world, pos, fluidState);
public void setup(FluidRenderHandler handler, BlockRenderView world, BlockPos pos, FluidState fluidState) {
this.handler = handler;
this.sprites[0] = sprites[0];
this.sprites[1] = sprites[1];
Sprite[] sprites = handler.getFluidSprites(world, pos, fluidState);
if (sprites.length > 2) {
hasOverlay = true;
overlay = sprites[2];
}
} else {
hasOverlay = false;
this.sprites[0] = sprites[0];
this.sprites[1] = sprites[1];
if (sprites.length > 2) {
hasOverlay = true;
overlaySprite = sprites[2];
}
}
public void clear() {
view = null;
pos = null;
blockState = null;
fluidState = null;
handler = null;
sprites[0] = null;
sprites[1] = null;
overlay = null;
handler = null;
hasOverlay = false;
overlaySprite = null;
}
}

View file

@ -18,16 +18,16 @@ package net.fabricmc.fabric.impl.client.rendering.fluid;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.TransparentBlock;
import net.minecraft.block.TranslucentBlock;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.color.world.BiomeColors;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.block.FluidRenderer;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
@ -43,15 +43,16 @@ import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
public class FluidRenderHandlerRegistryImpl implements FluidRenderHandlerRegistry {
/**
* The water color of {@link BiomeKeys#OCEAN}.
*/
private static final int DEFAULT_WATER_COLOR = 0x3f76e4;
private final Map<Fluid, FluidRenderHandler> handlers = new IdentityHashMap<>();
private final Map<Fluid, FluidRenderHandler> modHandlers = new IdentityHashMap<>();
private final Map<Block, Boolean> overlayBlocks = new IdentityHashMap<>();
private final ConcurrentMap<Block, Boolean> overlayBlocks = new ConcurrentHashMap<>();
private FluidRenderer fluidRenderer;
{
handlers.put(Fluids.WATER, WaterRenderHandler.INSTANCE);
handlers.put(Fluids.FLOWING_WATER, WaterRenderHandler.INSTANCE);
handlers.put(Fluids.LAVA, LavaRenderHandler.INSTANCE);
handlers.put(Fluids.FLOWING_LAVA, LavaRenderHandler.INSTANCE);
}
public FluidRenderHandlerRegistryImpl() {
}
@ -81,42 +82,14 @@ public class FluidRenderHandlerRegistryImpl implements FluidRenderHandlerRegistr
@Override
public boolean isBlockTransparent(Block block) {
return overlayBlocks.computeIfAbsent(block, k -> k instanceof TransparentBlock || k instanceof LeavesBlock);
return overlayBlocks.computeIfAbsent(block, k -> k instanceof TranslucentBlock || k instanceof LeavesBlock);
}
public void onFluidRendererReload(FluidRenderer renderer, Sprite[] waterSprites, Sprite[] lavaSprites, Sprite waterOverlay) {
fluidRenderer = renderer;
FluidRenderingImpl.setVanillaRenderer(renderer);
Sprite[] waterSpritesFull = {waterSprites[0], waterSprites[1], waterOverlay};
FluidRenderHandler waterHandler = new FluidRenderHandler() {
@Override
public Sprite[] getFluidSprites(BlockRenderView view, BlockPos pos, FluidState state) {
return waterSpritesFull;
}
@Override
public int getFluidColor(BlockRenderView view, BlockPos pos, FluidState state) {
if (view != null && pos != null) {
return BiomeColors.getWaterColor(view, pos);
} else {
return DEFAULT_WATER_COLOR;
}
}
};
//noinspection Convert2Lambda
FluidRenderHandler lavaHandler = new FluidRenderHandler() {
@Override
public Sprite[] getFluidSprites(BlockRenderView view, BlockPos pos, FluidState state) {
return lavaSprites;
}
};
handlers.put(Fluids.WATER, waterHandler);
handlers.put(Fluids.FLOWING_WATER, waterHandler);
handlers.put(Fluids.LAVA, lavaHandler);
handlers.put(Fluids.FLOWING_LAVA, lavaHandler);
handlers.putAll(modHandlers);
WaterRenderHandler.INSTANCE.updateSprites(waterSprites, waterOverlay);
LavaRenderHandler.INSTANCE.updateSprites(lavaSprites);
SpriteAtlasTexture texture = MinecraftClient.getInstance()
.getBakedModelManager()
@ -127,7 +100,49 @@ public class FluidRenderHandlerRegistryImpl implements FluidRenderHandlerRegistr
}
}
public void renderFluid(BlockPos pos, BlockRenderView world, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
fluidRenderer.render(world, pos, vertexConsumer, blockState, fluidState);
private static class WaterRenderHandler implements FluidRenderHandler {
public static final WaterRenderHandler INSTANCE = new WaterRenderHandler();
/**
* The water color of {@link BiomeKeys#OCEAN}.
*/
private static final int DEFAULT_WATER_COLOR = 0x3f76e4;
private final Sprite[] sprites = new Sprite[3];
@Override
public Sprite[] getFluidSprites(@Nullable BlockRenderView view, @Nullable BlockPos pos, FluidState state) {
return sprites;
}
@Override
public int getFluidColor(@Nullable BlockRenderView view, @Nullable BlockPos pos, FluidState state) {
if (view != null && pos != null) {
return BiomeColors.getWaterColor(view, pos);
} else {
return DEFAULT_WATER_COLOR;
}
}
public void updateSprites(Sprite[] waterSprites, Sprite waterOverlay) {
sprites[0] = waterSprites[0];
sprites[1] = waterSprites[1];
sprites[2] = waterOverlay;
}
}
private static class LavaRenderHandler implements FluidRenderHandler {
public static final LavaRenderHandler INSTANCE = new LavaRenderHandler();
private Sprite[] sprites;
@Override
public Sprite[] getFluidSprites(@Nullable BlockRenderView view, @Nullable BlockPos pos, FluidState state) {
return sprites;
}
public void updateSprites(Sprite[] lavaSprites) {
sprites = lavaSprites;
}
}
}

View file

@ -0,0 +1,76 @@
/*
* 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.fluid;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.block.FluidRenderer;
import net.minecraft.fluid.FluidState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRendering;
public class FluidRenderingImpl {
private static final ThreadLocal<FluidRendering.DefaultRenderer> CURRENT_DEFAULT_RENDERER = new ThreadLocal<>();
private static final ThreadLocal<FluidRenderHandlerInfo> CURRENT_INFO = ThreadLocal.withInitial(FluidRenderHandlerInfo::new);
private static FluidRenderer vanillaRenderer;
// Only invoked manually from FluidRendering#render
public static void render(FluidRenderHandler handler, BlockRenderView world, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState, FluidRendering.DefaultRenderer defaultRenderer) {
CURRENT_DEFAULT_RENDERER.set(defaultRenderer);
try {
handler.renderFluid(pos, world, vertexConsumer, blockState, fluidState);
} finally {
CURRENT_DEFAULT_RENDERER.remove();
}
}
// Only invoked when FluidRenderHandler#renderFluid calls super
public static void renderDefault(FluidRenderHandler handler, BlockRenderView world, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
FluidRendering.DefaultRenderer renderer = CURRENT_DEFAULT_RENDERER.get();
if (renderer != null) {
renderer.render(handler, world, pos, vertexConsumer, blockState, fluidState);
} else {
renderVanillaDefault(handler, world, pos, vertexConsumer, blockState, fluidState);
}
}
// Invoked when FluidRenderHandler#renderFluid is called directly without using FluidRendering#render (such as
// from vanilla FluidRenderer#render via mixin) or from the default implementation of DefaultRenderer#render
public static void renderVanillaDefault(FluidRenderHandler handler, BlockRenderView world, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
FluidRenderHandlerInfo info = CURRENT_INFO.get();
info.setup(handler, world, pos, fluidState);
try {
vanillaRenderer.render(world, pos, vertexConsumer, blockState, fluidState);
} finally {
info.clear();
}
}
public static void setVanillaRenderer(FluidRenderer vanillaRenderer) {
FluidRenderingImpl.vanillaRenderer = vanillaRenderer;
}
public static FluidRenderHandlerInfo getCurrentInfo() {
return CURRENT_INFO.get();
}
}

View file

@ -20,7 +20,6 @@ import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
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.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@ -38,8 +37,9 @@ import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRenderHandlerInfo;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRenderHandlerRegistryImpl;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRendererHookContainer;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRenderingImpl;
@Mixin(FluidRenderer.class)
public class FluidRendererMixin {
@ -52,8 +52,6 @@ public class FluidRendererMixin {
@Shadow
private Sprite waterOverlaySprite;
private final ThreadLocal<FluidRendererHookContainer> fabric_renderHandler = ThreadLocal.withInitial(FluidRendererHookContainer::new);
private final ThreadLocal<Boolean> fabric_customRendering = ThreadLocal.withInitial(() -> false);
private final ThreadLocal<Block> fabric_neighborBlock = new ThreadLocal<>();
@Inject(at = @At("RETURN"), method = "onResourceReload")
@ -63,78 +61,50 @@ public class FluidRendererMixin {
}
@Inject(at = @At("HEAD"), method = "render", cancellable = true)
public void tesselate(BlockRenderView view, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState, CallbackInfo info) {
if (!fabric_customRendering.get()) {
// Prevent recursively looking up custom fluid renderers when default behavior is being invoked
try {
fabric_customRendering.set(true);
tessellateViaHandler(view, pos, vertexConsumer, blockState, fluidState, info);
} finally {
fabric_customRendering.set(false);
public void onHeadRender(BlockRenderView view, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState, CallbackInfo ci) {
FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
if (info.handler == null) {
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluidState.getFluid());
if (handler != null) {
handler.renderFluid(pos, view, vertexConsumer, blockState, fluidState);
ci.cancel();
}
}
if (info.isCancelled()) {
return;
}
FluidRendererHookContainer ctr = fabric_renderHandler.get();
ctr.getSprites(view, pos, fluidState);
}
@Unique
private void tessellateViaHandler(BlockRenderView view, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState, CallbackInfo info) {
FluidRendererHookContainer ctr = fabric_renderHandler.get();
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluidState.getFluid());
ctr.view = view;
ctr.pos = pos;
ctr.blockState = blockState;
ctr.fluidState = fluidState;
ctr.handler = handler;
if (handler != null) {
handler.renderFluid(pos, view, vertexConsumer, blockState, fluidState);
info.cancel();
}
}
@Inject(at = @At("RETURN"), method = "render")
public void tesselateReturn(BlockRenderView world, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState, CallbackInfo ci) {
fabric_renderHandler.get().clear();
}
@ModifyVariable(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/FluidRenderer;isSameFluid(Lnet/minecraft/fluid/FluidState;Lnet/minecraft/fluid/FluidState;)Z"), method = "render", ordinal = 0)
public boolean modLavaCheck(boolean chk) {
// First boolean local is set by vanilla according to 'matches lava'
// but uses the negation consistent with 'matches water'
// for determining if special water sprite should be used behind glass.
// for determining if overlay water sprite should be used behind glass.
// Has other uses but those are overridden by this mixin and have
// already happened by the time this hook is called
// already happened by the time this hook is called.
// If this fluid has an overlay texture, set this boolean too false
final FluidRendererHookContainer ctr = fabric_renderHandler.get();
return !ctr.hasOverlay;
// If this fluid has an overlay texture, set this boolean to false.
final FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
return info.handler != null ? !info.hasOverlay : chk;
}
@ModifyVariable(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/FluidRenderer;isSameFluid(Lnet/minecraft/fluid/FluidState;Lnet/minecraft/fluid/FluidState;)Z"), method = "render", ordinal = 0)
public Sprite[] modSpriteArray(Sprite[] chk) {
FluidRendererHookContainer ctr = fabric_renderHandler.get();
return ctr.handler != null ? ctr.sprites : chk;
FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
return info.handler != null ? info.sprites : chk;
}
@ModifyVariable(at = @At(value = "CONSTANT", args = "intValue=16", ordinal = 0, shift = At.Shift.BEFORE), method = "render", ordinal = 0)
public int modTintColor(int chk, BlockRenderView world, BlockPos pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
return info.handler != null ? info.handler.getFluidColor(world, pos, fluidState) : chk;
}
// Redirect redirects all 'waterOverlaySprite' gets in 'render' to this method, this is correct
@Redirect(at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/client/render/block/FluidRenderer;waterOverlaySprite:Lnet/minecraft/client/texture/Sprite;"), method = "render")
public Sprite modWaterOverlaySprite(FluidRenderer self) {
FluidRendererHookContainer ctr = fabric_renderHandler.get();
return ctr.handler != null && ctr.hasOverlay ? ctr.overlay : waterOverlaySprite;
}
@ModifyVariable(at = @At(value = "CONSTANT", args = "intValue=16", ordinal = 0, shift = At.Shift.BEFORE), method = "render", ordinal = 0)
public int modTintColor(int chk) {
FluidRendererHookContainer ctr = fabric_renderHandler.get();
return ctr.handler != null ? ctr.handler.getFluidColor(ctr.view, ctr.pos, ctr.fluidState) : chk;
FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
return info.handler != null && info.hasOverlay ? info.overlaySprite : waterOverlaySprite;
}
@Redirect(at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;getBlock()Lnet/minecraft/block/Block;"), method = "render")
@ -152,8 +122,8 @@ public class FluidRendererMixin {
Block block = fabric_neighborBlock.get();
if (FluidRenderHandlerRegistry.INSTANCE.isBlockTransparent(block)) {
FluidRendererHookContainer ctr = fabric_renderHandler.get();
return ctr.handler != null && ctr.hasOverlay ? ctr.overlay : waterOverlaySprite;
FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
return info.handler != null && info.hasOverlay ? info.overlaySprite : waterOverlaySprite;
}
return chk;

View file

@ -36,8 +36,8 @@ public class CustomizedFluidRenderer extends SimpleFluidRenderHandler {
int light = getLight(world, pos);
float u1 = sprites[2].getFrameU(0);
float v1 = sprites[2].getFrameV(0);
float u2 = sprites[2].getFrameU(16);
float v2 = sprites[2].getFrameV(16 * fluidState.getHeight(world, pos));
float u2 = sprites[2].getFrameU(1);
float v2 = sprites[2].getFrameV(fluidState.getHeight(world, pos));
float x1 = (pos.getX() & 15) + 0.1f;
float y1 = pos.getY() & 15;