From 76d184ea21991ff084b32158408ef2702b468aaa Mon Sep 17 00:00:00 2001 From: asie Date: Fri, 17 May 2019 23:24:37 +0200 Subject: [PATCH] [#111] fluid rendering hooks --- fabric-rendering-fluids-v1/build.gradle | 6 ++ .../render/fluid/v1/FluidRenderHandler.java | 57 ++++++++++++ .../fluid/v1/FluidRenderHandlerRegistry.java | 47 ++++++++++ .../fluid/FluidRenderHandlerRegistryImpl.java | 87 ++++++++++++++++++ .../fluid/FluidRendererHookContainer.java | 36 ++++++++ .../render/fluid/MixinFluidRenderer.java | 91 +++++++++++++++++++ .../fabric-rendering-fluids-v1.mixins.json | 11 +++ .../src/main/resources/fabric.mod.json | 16 ++++ .../fluidrender/FluidRendererModClient.java | 46 ++++++++++ settings.gradle | 1 + 10 files changed, 398 insertions(+) create mode 100644 fabric-rendering-fluids-v1/build.gradle create mode 100644 fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandler.java create mode 100644 fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandlerRegistry.java create mode 100644 fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRenderHandlerRegistryImpl.java create mode 100644 fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRendererHookContainer.java create mode 100644 fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/mixin/client/render/fluid/MixinFluidRenderer.java create mode 100644 fabric-rendering-fluids-v1/src/main/resources/fabric-rendering-fluids-v1.mixins.json create mode 100644 fabric-rendering-fluids-v1/src/main/resources/fabric.mod.json create mode 100644 fabric-testmods/java/net/fabricmc/fabric/fluidrender/FluidRendererModClient.java diff --git a/fabric-rendering-fluids-v1/build.gradle b/fabric-rendering-fluids-v1/build.gradle new file mode 100644 index 0000000000..3eaabdd1b5 --- /dev/null +++ b/fabric-rendering-fluids-v1/build.gradle @@ -0,0 +1,6 @@ +archivesBaseName = "fabric-rendering-fluids-v1" +version = getSubprojectVersion(project, "0.1.0") + +dependencies { + compile project(path: ':fabric-api-base', configuration: 'dev') +} diff --git a/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandler.java b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandler.java new file mode 100644 index 0000000000..af2748585d --- /dev/null +++ b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.client.texture.Sprite; +import net.minecraft.fluid.FluidState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; + +/** + * Interface for handling the rendering of a FluidState. + */ +public interface FluidRenderHandler { + /** + * Get the sprites for a fluid being rendered at a given position. + * For optimal performance, the sprites should be loaded as part of a + * resource reload and *not* looked up every time the method is called! + * + * The "fabric-textures" module contains sprite rendering facilities, which may come in handy here. + * + * @param view The world view pertaining to the fluid. May be null! + * @param pos The position of the fluid in the world. May be null! + * @param state The current state of the fluid. + * @return An array of size two: the first entry contains the "still" sprite, + * while the second entry contains the "flowing" sprite. + */ + Sprite[] getFluidSprites(/* Nullable */ ExtendedBlockView view, /* Nullable */ BlockPos pos, FluidState state); + + /** + * Get the tint color for a fluid being rendered at a given position. + * + * NOTE: As of right now, our hook cannot handle setting a custom alpha + * tint here - as such, it must be contained in the texture itself! + * + * @param view The world view pertaining to the fluid. May be null! + * @param pos The position of the fluid in the world. May be null! + * @param state The current state of the fluid. + * @return The tint color of the fluid. + */ + default int getFluidColor(ExtendedBlockView view, BlockPos pos, FluidState state) { + return -1; + } +} diff --git a/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandlerRegistry.java b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandlerRegistry.java new file mode 100644 index 0000000000..5115a900c6 --- /dev/null +++ b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/api/client/render/fluid/v1/FluidRenderHandlerRegistry.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.fabricmc.fabric.impl.client.render.fluid.FluidRenderHandlerRegistryImpl; +import net.minecraft.fluid.Fluid; + +/** + * Registry for {@link FluidRenderHandler} instances. + * + * Notably, this supports querying, overriding and wrapping vanilla fluid + * rendering. + */ +public interface FluidRenderHandlerRegistry { + FluidRenderHandlerRegistry INSTANCE = FluidRenderHandlerRegistryImpl.INSTANCE; + + /** + * Get a {@link FluidRenderHandler} for a given Fluid. + * Supports vanilla and Fabric fluids. + * + * @param fluid The Fluid. + * @return The FluidRenderHandler. + */ + FluidRenderHandler get(Fluid fluid); + + /** + * Register a {@link FluidRenderHandler} for a given Fluid. + * + * @param fluid The Fluid. + * @param renderer The FluidRenderHandler. + */ + void register(Fluid fluid, FluidRenderHandler renderer); +} diff --git a/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRenderHandlerRegistryImpl.java b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRenderHandlerRegistryImpl.java new file mode 100644 index 0000000000..58102902ea --- /dev/null +++ b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRenderHandlerRegistryImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.render.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.color.world.BiomeColors; +import net.minecraft.client.texture.Sprite; +import net.minecraft.fluid.Fluid; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; +import net.minecraft.world.biome.Biomes; + +import java.util.IdentityHashMap; +import java.util.Map; + +public class FluidRenderHandlerRegistryImpl implements FluidRenderHandlerRegistry { + public static final FluidRenderHandlerRegistryImpl INSTANCE = new FluidRenderHandlerRegistryImpl(); + private final Map handlers = new IdentityHashMap<>(); + private final Map modHandlers = new IdentityHashMap<>(); + + private FluidRenderHandlerRegistryImpl() { + } + + @Override + public FluidRenderHandler get(Fluid fluid) { + return handlers.get(fluid); + } + + public FluidRenderHandler getOverride(Fluid fluid) { + return modHandlers.get(fluid); + } + + @Override + public void register(Fluid fluid, FluidRenderHandler renderer) { + handlers.put(fluid, renderer); + modHandlers.put(fluid, renderer); + } + + public void onFluidRendererReload(Sprite[] waterSprites, Sprite[] lavaSprites) { + FluidRenderHandler waterHandler = new FluidRenderHandler() { + @Override + public Sprite[] getFluidSprites(ExtendedBlockView view, BlockPos pos, FluidState state) { + return waterSprites; + } + + @Override + public int getFluidColor(ExtendedBlockView view, BlockPos pos, FluidState state) { + if (view != null && pos != null) { + return BiomeColors.getWaterColor(view, pos); + } else { + return Biomes.DEFAULT.getWaterColor(); + } + } + }; + + //noinspection Convert2Lambda + FluidRenderHandler lavaHandler = new FluidRenderHandler() { + @Override + public Sprite[] getFluidSprites(ExtendedBlockView view, BlockPos pos, FluidState state) { + return lavaSprites; + } + }; + + register(Fluids.WATER, waterHandler); + register(Fluids.FLOWING_WATER, waterHandler); + register(Fluids.LAVA, lavaHandler); + register(Fluids.FLOWING_LAVA, lavaHandler); + handlers.putAll(modHandlers); + } +} diff --git a/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRendererHookContainer.java b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRendererHookContainer.java new file mode 100644 index 0000000000..15d78fb5b1 --- /dev/null +++ b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/impl/client/render/fluid/FluidRendererHookContainer.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.render.fluid; + +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler; +import net.minecraft.fluid.FluidState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; + +public class FluidRendererHookContainer { + public ExtendedBlockView view; + public BlockPos pos; + public FluidState state; + public FluidRenderHandler handler; + + public void clear() { + view = null; + pos = null; + state = null; + handler = null; + } +} diff --git a/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/mixin/client/render/fluid/MixinFluidRenderer.java b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/mixin/client/render/fluid/MixinFluidRenderer.java new file mode 100644 index 0000000000..cf7e7fbb0c --- /dev/null +++ b/fabric-rendering-fluids-v1/src/main/java/net/fabricmc/fabric/mixin/client/render/fluid/MixinFluidRenderer.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.mixin.client.render.fluid; + +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler; +import net.fabricmc.fabric.impl.client.render.fluid.FluidRendererHookContainer; +import net.fabricmc.fabric.impl.client.render.fluid.FluidRenderHandlerRegistryImpl; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.block.FluidRenderer; +import net.minecraft.client.texture.Sprite; +import net.minecraft.fluid.FluidState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(FluidRenderer.class) +public class MixinFluidRenderer { + @Shadow + private Sprite[] lavaSprites; + @Shadow + private Sprite[] waterSprites; + + private final ThreadLocal fabric_renderHandler = ThreadLocal.withInitial(FluidRendererHookContainer::new); + + @Inject(at = @At("RETURN"), method = "onResourceReload") + public void onResourceReloadReturn(CallbackInfo info) { + FluidRenderHandlerRegistryImpl.INSTANCE.onFluidRendererReload(waterSprites, lavaSprites); + } + + @Inject(at = @At("HEAD"), method = "tesselate", cancellable = true) + public void tesselate(ExtendedBlockView view, BlockPos pos, BufferBuilder bufferBuilder, FluidState state, CallbackInfoReturnable info) { + FluidRendererHookContainer ctr = fabric_renderHandler.get(); + FluidRenderHandler handler = FluidRenderHandlerRegistryImpl.INSTANCE.getOverride(state.getFluid()); + if (handler == null) { + return; + } + + /* ActionResult hResult = handler.tesselate(view, pos, bufferBuilder, state); + if (hResult != ActionResult.PASS) { + info.setReturnValue(hResult == ActionResult.SUCCESS); + return; + } */ + + ctr.view = view; + ctr.pos = pos; + ctr.state = state; + ctr.handler = handler; + } + + @Inject(at = @At("RETURN"), method = "tesselate") + public void tesselateReturn(ExtendedBlockView view, BlockPos pos, BufferBuilder bufferBuilder, FluidState state, CallbackInfoReturnable info) { + fabric_renderHandler.get().clear(); + } + + @ModifyVariable(at = @At(value = "INVOKE", target = "net/minecraft/client/render/block/FluidRenderer.isSameFluid(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/fluid/FluidState;)Z"), method = "tesselate", ordinal = 0) + public boolean modLavaCheck(boolean chk) { + return fabric_renderHandler.get().handler != null || chk; + } + + @ModifyVariable(at = @At(value = "INVOKE", target = "net/minecraft/client/render/block/FluidRenderer.isSameFluid(Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/fluid/FluidState;)Z"), method = "tesselate", ordinal = 0) + public Sprite[] modSpriteArray(Sprite[] chk) { + FluidRendererHookContainer ctr = fabric_renderHandler.get(); + return ctr.handler != null ? ctr.handler.getFluidSprites(ctr.view, ctr.pos, ctr.state) : chk; + } + + @ModifyVariable(at = @At(value = "CONSTANT", args = "intValue=16", ordinal = 0, shift = At.Shift.BEFORE), method = "tesselate", ordinal = 0) + public int modTintColor(int chk) { + FluidRendererHookContainer ctr = fabric_renderHandler.get(); + return ctr.handler != null ? ctr.handler.getFluidColor(ctr.view, ctr.pos, ctr.state) : chk; + } +} diff --git a/fabric-rendering-fluids-v1/src/main/resources/fabric-rendering-fluids-v1.mixins.json b/fabric-rendering-fluids-v1/src/main/resources/fabric-rendering-fluids-v1.mixins.json new file mode 100644 index 0000000000..5f5432ab6d --- /dev/null +++ b/fabric-rendering-fluids-v1/src/main/resources/fabric-rendering-fluids-v1.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.fabricmc.fabric.mixin.client.render.fluid", + "compatibilityLevel": "JAVA_8", + "client": [ + "MixinFluidRenderer" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/fabric-rendering-fluids-v1/src/main/resources/fabric.mod.json b/fabric-rendering-fluids-v1/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..7b838edc15 --- /dev/null +++ b/fabric-rendering-fluids-v1/src/main/resources/fabric.mod.json @@ -0,0 +1,16 @@ +{ + "schemaVersion": 1, + "id": "fabric-rendering-fluids-v1", + "version": "${version}", + "license": "Apache-2.0", + "depends": { + "fabricloader": ">=0.4.0", + "fabric-api-base": "*" + }, + "recommends": { + "fabric-textures-v0": "*" + }, + "mixins": [ + "fabric-rendering-fluids-v1.mixins.json" + ] +} diff --git a/fabric-testmods/java/net/fabricmc/fabric/fluidrender/FluidRendererModClient.java b/fabric-testmods/java/net/fabricmc/fabric/fluidrender/FluidRendererModClient.java new file mode 100644 index 0000000000..98cba6df83 --- /dev/null +++ b/fabric-testmods/java/net/fabricmc/fabric/fluidrender/FluidRendererModClient.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.fluidrender; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.render.FluidRenderHandler; +import net.fabricmc.fabric.api.client.render.FluidRenderHandlerRegistry; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; +import net.minecraft.fluid.Fluids; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ResourceType; +import net.minecraft.util.Identifier; + +public class FluidRendererModClient implements ClientModInitializer, SimpleSynchronousResourceReloadListener { + @Override + public void onInitializeClient() { + ResourceManagerHelper.get(ResourceType.ASSETS).registerReloadListener(this); + } + + @Override + public Identifier getFabricId() { + return new Identifier("fabric:fluid_renderer_test"); + } + + @Override + public void apply(ResourceManager rm) { + FluidRenderHandler lavaHandler = FluidRenderHandlerRegistry.INSTANCE.get(Fluids.LAVA); + FluidRenderHandlerRegistry.INSTANCE.register(Fluids.WATER, lavaHandler); + FluidRenderHandlerRegistry.INSTANCE.register(Fluids.FLOWING_WATER, lavaHandler); + } +} diff --git a/settings.gradle b/settings.gradle index 79a5c21ab8..cb0dd82c7d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,6 +29,7 @@ include 'fabric-networking-blockentity-v0' include 'fabric-object-builders-v0' include 'fabric-registry-sync-v0' include 'fabric-rendering-v0' +include 'fabric-rendering-fluids-v1' include 'fabric-resource-loader-v0' include 'fabric-tag-extensions-v0' include 'fabric-textures-v0'