Skip to content

Commit

Permalink
Port fabric-rendering-fluids-v1
Browse files Browse the repository at this point in the history
  • Loading branch information
Su5eD committed Jun 21, 2024
1 parent eb725d5 commit 857185b
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package net.fabricmc.fabric.api.client.render.fluid.v1;

import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRenderHandlerRegistryImpl;
import net.minecraft.world.level.block.Block;
Expand Down Expand Up @@ -105,4 +109,11 @@ default void register(Fluid still, Fluid flow, FluidRenderHandler renderer) {
* or not.
*/
boolean isBlockTransparent(Block block);

/**
* FFAPI: Forge-sensitive version of {@link #isBlockTransparent(Block)}
*/
default boolean isBlockTransparent(BlockState state, BlockAndTintGetter level, BlockPos pos, FluidState fluidState) {
return isBlockTransparent(state.getBlock());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package net.fabricmc.fabric.impl.client.rendering.fluid;

import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions;
import org.jetbrains.annotations.Nullable;

public record FabricClientFluidTypeExtensions(Fluid fluid, FluidRenderHandler handler) implements IClientFluidTypeExtensions {
private TextureAtlasSprite[] getSprites() {
return handler.getFluidSprites(null, null, fluid.defaultFluidState());
}

@Override
public ResourceLocation getStillTexture() {
TextureAtlasSprite[] sprites = getSprites();
return sprites[0].contents().name();
}

@Override
public ResourceLocation getFlowingTexture() {
TextureAtlasSprite[] sprites = getSprites();
return sprites[1].contents().name();
}

@Nullable
@Override
public ResourceLocation getOverlayTexture() {
TextureAtlasSprite[] sprites = getSprites();
return sprites.length > 2 ? sprites[2].contents().name() : null;
}

@Override
public int getTintColor() {
int baseColor = handler.getFluidColor(null, null, fluid.defaultFluidState());
return 0xFF000000 | baseColor;
}

@Override
public int getTintColor(FluidState state, BlockAndTintGetter getter, BlockPos pos) {
int baseColor = handler.getFluidColor(getter, pos, state);
return 0xFF000000 | baseColor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
Expand Down Expand Up @@ -67,6 +68,10 @@ public FluidRenderHandler getOverride(Fluid fluid) {
return modHandlers.get(fluid);
}

public void registerHandlerOnly(Fluid fluid, FluidRenderHandler renderer) {
handlers.put(fluid, renderer);
}

@Override
public void register(Fluid fluid, FluidRenderHandler renderer) {
handlers.put(fluid, renderer);
Expand All @@ -80,7 +85,15 @@ public void setBlockTransparency(Block block, boolean transparent) {

@Override
public boolean isBlockTransparent(Block block) {
return overlayBlocks.computeIfAbsent(block, k -> k instanceof HalfTransparentBlock || k instanceof LeavesBlock);
if (overlayBlocks.containsKey(block)) {
return overlayBlocks.get(block);
}
return block instanceof HalfTransparentBlock || block instanceof LeavesBlock;
}

@Override
public boolean isBlockTransparent(BlockState state, BlockAndTintGetter level, BlockPos pos, FluidState fluidState) {
return overlayBlocks.computeIfAbsent(state.getBlock(), block -> block.shouldDisplayFluidOverlay(state, level, pos, fluidState));
}

public void onFluidRendererReload(LiquidBlockRenderer renderer, TextureAtlasSprite[] waterSprites, TextureAtlasSprite[] lavaSprites, TextureAtlasSprite waterOverlay) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package net.fabricmc.fabric.impl.client.rendering.fluid;

import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions;
import net.neoforged.neoforge.client.textures.FluidSpriteCache;
import net.neoforged.neoforge.common.NeoForgeMod;
import net.neoforged.neoforge.fluids.FluidType;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public final class FluidRendererCompat {

public static void onClientSetup(FMLClientSetupEvent event) {
// Register forge handlers only to the "handlers" map and not "modHandlers"
// This allows fabric mods to access render handlers for forge mods' fluids without them being
// used for rendering fluids, as that should remain handled by forge
Map<FluidType, FluidRenderHandler> forgeHandlers = new HashMap<>();
for (Map.Entry<ResourceKey<Fluid>, Fluid> entry : BuiltInRegistries.FLUID.entrySet()) {
Fluid fluid = entry.getValue();
if (fluid != Fluids.EMPTY && fluid.getFluidType() != NeoForgeMod.EMPTY_TYPE.value() && FluidRenderHandlerRegistry.INSTANCE.get(fluid) == null) {
FluidRenderHandler handler = forgeHandlers.computeIfAbsent(fluid.getFluidType(), ForgeFluidRenderHandler::new);
((FluidRenderHandlerRegistryImpl) FluidRenderHandlerRegistry.INSTANCE).registerHandlerOnly(fluid, handler);
}
}
}

private record ForgeFluidRenderHandler(FluidType fluidType) implements FluidRenderHandler {
@Override
public TextureAtlasSprite[] getFluidSprites(@Nullable BlockAndTintGetter view, @Nullable BlockPos pos, FluidState state) {
TextureAtlasSprite[] forgeSprites = FluidSpriteCache.getFluidSprites(view, pos, state);
return forgeSprites[2] == null ? Arrays.copyOfRange(forgeSprites, 0, 2) : forgeSprites;
}

@Override
public int getFluidColor(@Nullable BlockAndTintGetter view, @Nullable BlockPos pos, FluidState state) {
int color = IClientFluidTypeExtensions.of(this.fluidType).getTintColor(state, view, pos);
return 0x00FFFFFF & color;
}
}

private FluidRendererCompat() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,6 @@

package net.fabricmc.fabric.mixin.client.rendering.fluid;

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.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
Expand All @@ -38,6 +29,12 @@
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(LiquidBlockRenderer.class)
public class FluidRendererMixin {
Expand All @@ -50,8 +47,6 @@ public class FluidRendererMixin {
@Shadow
private TextureAtlasSprite waterOverlay;

private final ThreadLocal<Block> fabric_neighborBlock = new ThreadLocal<>();

@Inject(at = @At("RETURN"), method = "setupSprites")
public void onResourceReloadReturn(CallbackInfo info) {
LiquidBlockRenderer self = (LiquidBlockRenderer) (Object) this;
Expand All @@ -71,59 +66,4 @@ public void onHeadRender(BlockAndTintGetter view, BlockPos pos, VertexConsumer v
}
}
}

@ModifyVariable(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/block/LiquidBlockRenderer;isNeighborSameFluid(Lnet/minecraft/world/level/material/FluidState;Lnet/minecraft/world/level/material/FluidState;)Z"), method = "tesselate", 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 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.

// 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/renderer/block/LiquidBlockRenderer;isNeighborSameFluid(Lnet/minecraft/world/level/material/FluidState;Lnet/minecraft/world/level/material/FluidState;)Z"), method = "tesselate", ordinal = 0)
public TextureAtlasSprite[] modSpriteArray(TextureAtlasSprite[] 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 = "tesselate", ordinal = 0)
public int modTintColor(int chk, BlockAndTintGetter 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/renderer/block/LiquidBlockRenderer;waterOverlay:Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;"), method = "tesselate")
public TextureAtlasSprite modWaterOverlaySprite(LiquidBlockRenderer self) {
FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
return info.handler != null && info.hasOverlay ? info.overlaySprite : waterOverlay;
}

@Redirect(at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;getBlock()Lnet/minecraft/world/level/block/Block;"), method = "tesselate")
public Block getOverlayBlock(BlockState state) {
Block block = state.getBlock();
fabric_neighborBlock.set(block);

// An if-statement follows, we don't want this anymore and 'null' makes
// its condition always false (due to instanceof)
return null;
}

@ModifyVariable(at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;getBlock()Lnet/minecraft/world/level/block/Block;", shift = At.Shift.BY, by = 2), method = "tesselate", ordinal = 0)
public TextureAtlasSprite modSideSpriteForOverlay(TextureAtlasSprite chk) {
Block block = fabric_neighborBlock.get();

if (FluidRenderHandlerRegistry.INSTANCE.isBlockTransparent(block)) {
FluidRenderHandlerInfo info = FluidRenderingImpl.getCurrentInfo();
return info.handler != null && info.hasOverlay ? info.overlaySprite : waterOverlay;
}

return chk;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.fabricmc.fabric.mixin.client.rendering.fluid;

import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.neoforged.neoforge.common.extensions.IBlockStateExtension;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(IBlockStateExtension.class)
public interface IBlockStateExtensionMixin {

@Inject(method = "shouldDisplayFluidOverlay", at = @At("HEAD"), cancellable = true)
default void shouldDisplayFluidOverlay(BlockAndTintGetter level, BlockPos pos, FluidState fluidState, CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(FluidRenderHandlerRegistry.INSTANCE.isBlockTransparent((BlockState) this, level, pos, fluidState));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.fabricmc.fabric.mixin.client.rendering.fluid;

import com.llamalad7.mixinextras.injector.ModifyReturnValue;
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.FabricClientFluidTypeExtensions;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(IClientFluidTypeExtensions.class)
public interface IClientFluidTypeExtensionsMixin {

@ModifyReturnValue(method = "of(Lnet/minecraft/world/level/material/FluidState;)Lnet/neoforged/neoforge/client/extensions/common/IClientFluidTypeExtensions;", at = @At("RETURN"))
private static IClientFluidTypeExtensions of(IClientFluidTypeExtensions ret, FluidState state) {
if (ret == IClientFluidTypeExtensions.DEFAULT) {
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.getOverride(state.getType());
if (handler != null) {
return new FabricClientFluidTypeExtensions(state.getType(), handler);
}
}
return ret;
}

@ModifyReturnValue(method = "of(Lnet/minecraft/world/level/material/Fluid;)Lnet/neoforged/neoforge/client/extensions/common/IClientFluidTypeExtensions;", at = @At("RETURN"))
private static IClientFluidTypeExtensions of(IClientFluidTypeExtensions ret, Fluid fluid) {
if (ret == IClientFluidTypeExtensions.DEFAULT) {
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.getOverride(fluid);
if (handler != null) {
return new FabricClientFluidTypeExtensions(fluid, handler);
}
}
return ret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.sinytra.fabric.rendering_fluids;

import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.impl.client.rendering.fluid.FluidRendererCompat;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModLoadingContext;

public class FabricRenderingFabricV1 implements ClientModInitializer {

@Override
public void onInitializeClient() {
IEventBus bus = ModLoadingContext.get().getActiveContainer().getEventBus();
bus.addListener(FluidRendererCompat::onClientSetup);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"package": "net.fabricmc.fabric.mixin.client.rendering.fluid",
"compatibilityLevel": "JAVA_17",
"client": [
"FluidRendererMixin"
"FluidRendererMixin",
"IBlockStateExtensionMixin",
"IClientFluidTypeExtensionsMixin"
],
"injectors": {
"defaultRequire": 1,
Expand Down
5 changes: 5 additions & 0 deletions fabric-rendering-fluids-v1/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@
],
"custom": {
"fabric-api:module-lifecycle": "stable"
},
"entrypoints": {
"client": [
"org.sinytra.fabric.rendering_fluids.FabricRenderingFabricV1"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package net.fabricmc.fabric.test.client.rendering.fluid;

import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.sounds.SoundEvent;
Expand All @@ -35,6 +34,10 @@
import net.minecraft.world.level.material.FlowingFluid;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.FluidState;
import net.neoforged.neoforge.common.NeoForgeMod;
import net.neoforged.neoforge.fluids.FluidType;

import java.util.Optional;

public abstract class CustomFluid extends FlowingFluid {
public CustomFluid() {
Expand Down Expand Up @@ -106,6 +109,11 @@ public Optional<SoundEvent> getPickupSound() {
return Optional.of(SoundEvents.BUCKET_FILL);
}

@Override
public FluidType getFluidType() {
return NeoForgeMod.EMPTY_TYPE.value();
}

public static class Flowing extends CustomFluid {
public Flowing() {
}
Expand Down
Loading

0 comments on commit 857185b

Please sign in to comment.