Fabric API

Fabric API

106M Downloads

client.getBlockRenderManager().renderFluid Doesn't Work In BlockEntityRenderer

TheBrokenRail opened this issue ยท 9 comments

commented

It doesn't crash, or break obviously, it just doesn't render anything. this is in a block entity. I know for sure that there is water there because it is forming obsidian with the lava.

My Code:

public class MiniaturizerBlockEntityRenderer extends BlockEntityRenderer<MiniaturizerBlockEntity> {
    private Random random = new Random();

    public MiniaturizerBlockEntityRenderer(BlockEntityRenderDispatcher dispatcher) {
        super(dispatcher);
    }

    @Override
    public void render(MiniaturizerBlockEntity blockEntity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
        matrices.push();
        matrices.scale(1f / MiniaturizerBlock.BLOCK_SIZE, 1f / MiniaturizerBlock.BLOCK_SIZE, 1f / MiniaturizerBlock.BLOCK_SIZE);
        ServerWorld world = blockEntity.getMiniturizerWorld();
        if (world != null) {
            for (int x = 0; x < MiniaturizerBlock.BLOCK_SIZE; x++) {
                for (int y = 0; y < MiniaturizerBlock.BLOCK_SIZE; y++) {
                    for (int z = 0; z < MiniaturizerBlock.BLOCK_SIZE; z++) {
                        matrices.push();
                        matrices.translate(x, y, z);
                        MinecraftClient client = MinecraftClient.getInstance();
                        BlockPos pos = new BlockPos(x,y, z);
                        BlockState blockState = world.getBlockState(pos);
                        if (blockState.getRenderType() != BlockRenderType.INVISIBLE) {
                            client.getBlockRenderManager().renderBlock(blockState, pos, blockEntity.getMiniturizerWorld(), matrices, vertexConsumers.getBuffer(RenderLayers.getBlockLayer(blockState)), false, random);
                        }
                        if (blockState.getBlock().hasBlockEntity()) {
                            BlockEntity entity = world.getBlockEntity(pos);
                            if (entity != null) {
                                BlockEntityRenderDispatcher.INSTANCE.render(entity, tickDelta, matrices, vertexConsumers);
                            }
                        }
                        matrices.pop();
                        FluidState fluidState = blockState.getFluidState();
                        if (!fluidState.isEmpty()) {
                            client.getBlockRenderManager().renderFluid(pos, world, vertexConsumers.getBuffer(RenderLayers.getFluidLayer(fluidState)), fluidState);
                        }
                    }
                }
            }
        }
        matrices.pop();
    }
}

I also did try this with different RenderLayers, it didn't work. I also tried removing fabric-rendering-fluid-v1, that also didn't o anything.

commented

does the block render? maybe move that matrices.pop() before FluidState fluidState = blockState.getFluidState(); to after the fluid is rendered

commented

The block renders, as seen in the screenshot. You can also see the result of the water I laced and lava I placed interacting, just no water or lava. I only translated the block render because the fluid render code as far as I can tell, doe the translation on its own, and ChunkBuilder also only translates the block render. I did test though and it still doesn't render.

commented

Sorry forgot the screenshot.

2020-03-28_22 56 11

commented

I still can't figure it out, I can't find annything else that uses renderFluid(), I think it might have something to do with VertexConsumerProvider.Immediate, and LibBlockAttributes even uses a different one https://github.com/AlexIIL/LibBlockAttributes/blob/001f6cd6a2498964f447585a5744a330dcfae54d/src/main/java/alexiil/mc/lib/attributes/fluid/render/FluidVolumeRenderer.java#L195, which I tried to use, but also didn't render. I have no ida why this isn't working. Do you know any other (current) mods that use renderFluid()?

This is my code now:

public class MiniaturizerBlockEntityRenderer extends BlockEntityRenderer<MiniaturizerBlockEntity> {
    private Random random = new Random();
    private ExpandingVcp VCPS = new ExpandingVcp();

    public MiniaturizerBlockEntityRenderer(BlockEntityRenderDispatcher dispatcher) {
        super(dispatcher);
    }

    @Override
    public void render(MiniaturizerBlockEntity blockEntity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {
        matrices.push();
        matrices.scale(1f / MiniaturizerBlock.BLOCK_SIZE, 1f / MiniaturizerBlock.BLOCK_SIZE, 1f / MiniaturizerBlock.BLOCK_SIZE);
        ServerWorld world = blockEntity.getMiniturizerWorld();
        if (world != null) {
            for (int x = 0; x < MiniaturizerBlock.BLOCK_SIZE; x++) {
                for (int y = 0; y < MiniaturizerBlock.BLOCK_SIZE; y++) {
                    for (int z = 0; z < MiniaturizerBlock.BLOCK_SIZE; z++) {
                        matrices.push();
                        matrices.translate(x, y, z);
                        MinecraftClient client = MinecraftClient.getInstance();
                        BlockPos pos = new BlockPos(x,y, z).add(blockEntity.getOffset());
                        BlockState blockState = world.getBlockState(pos);
                        if (blockState.getRenderType() != BlockRenderType.INVISIBLE) {
                            client.getBlockRenderManager().renderBlock(blockState, pos, blockEntity.getMiniturizerWorld(), matrices, vertexConsumers.getBuffer(RenderLayers.getBlockLayer(blockState)), false, random);
                        }
                        if (blockState.getBlock().hasBlockEntity()) {
                            BlockEntity entity = world.getBlockEntity(pos);
                            if (entity != null) {
                                BlockEntityRenderDispatcher.INSTANCE.render(entity, tickDelta, matrices, vertexConsumers);
                            }
                        }
                        matrices.pop();
                        FluidState fluidState = blockState.getFluidState();
                        if (!fluidState.isEmpty()) {
                            RenderLayer layer = RenderLayers.getFluidLayer(fluidState);
                            client.getBlockRenderManager().renderFluid(pos, world, VCPS.getBuffer(layer), fluidState);
                        }
                        VCPS.draw();
                    }
                }
            }
        }
        matrices.pop();
    }

    /** A simple, auto-expanding {@link VertexConsumerProvider} that can render any number of {@link RenderLayer}'s at
     * once, rather than {@link net.minecraft.client.render.VertexConsumerProvider.Immediate
     * VertexConsumerProvider.Immediate} which can only render the ones provided to it in a map, and 1 other. */
    public static final class ExpandingVcp implements VertexConsumerProvider {
        private final List<RenderLayer> before = new ArrayList<>();
        private final List<RenderLayer> solid = new ArrayList<>();
        private final List<RenderLayer> middle = new ArrayList<>();
        private final List<RenderLayer> translucent = new ArrayList<>();
        private final List<RenderLayer> after = new ArrayList<>();

        private final List<BufferBuilder> availableBuffers = new ArrayList<>();
        private final Map<RenderLayer, BufferBuilder> activeBuffers = new HashMap<>();
        private final Set<RenderLayer> knownLayers = new HashSet<>();

        public ExpandingVcp() {
            addLayer(RenderLayer.getSolid());
            addLayer(RenderLayer.getCutout());
            addLayer(RenderLayer.getCutoutMipped());
            addLayer(RenderLayer.getTranslucent());
            addLayer(RenderLayer.getTranslucentNoCrumbling());
            addLayerAfter(RenderLayer.getGlint());
            addLayerAfter(RenderLayer.getEntityGlint());
        }

        public void addLayer(RenderLayer layer) {
            if (knownLayers.add(layer)) {
                if (((RenderLayerAccessor) layer).isTranslucent()) {
                    translucent.add(layer);
                } else {
                    solid.add(layer);
                }
            }
        }

        public void addLayerBefore(RenderLayer layer) {
            if (knownLayers.add(layer)) {
                before.add(layer);
            }
        }

        public void addLayerMiddle(RenderLayer layer) {
            if (knownLayers.add(layer)) {
                middle.add(layer);
            }
        }

        public void addLayerAfter(RenderLayer layer) {
            if (knownLayers.add(layer)) {
                after.add(layer);
            }
        }

        @Override
        public VertexConsumer getBuffer(RenderLayer layer) {
            addLayer(layer);
            BufferBuilder buffer = activeBuffers.get(layer);
            if (buffer == null) {
                if (availableBuffers.isEmpty()) {
                    buffer = new BufferBuilder(1 << 12);
                } else {
                    buffer = availableBuffers.remove(availableBuffers.size() - 1);
                }
                activeBuffers.put(layer, buffer);
            }
            if (!buffer.isBuilding()) {
                buffer.begin(layer.getDrawMode(), layer.getVertexFormat());
            }
            return buffer;
        }

        public void draw() {
            draw(before);
            draw(solid);
            draw(middle);
            draw(translucent);
            draw(after);
            assert activeBuffers.isEmpty();
        }

        private void draw(List<RenderLayer> layers) {
            for (RenderLayer layer : layers) {
                BufferBuilder buffer = activeBuffers.remove(layer);
                if (buffer != null) {
                    layer.draw(buffer, 0, 0, 0);
                }
            }
        }
    }
}
commented

How about other blocks in the translucent render layer, like stained glasses, slime block, ice

commented

Other blocks use RenderLayers.getBlockLayer(blockState), if I try to use this with fluids, also nothing shows up.

commented

I am now using:

package com.thebrokenrail.gulliverreloaded.mixin;

import com.thebrokenrail.gulliverreloaded.block.MiniaturizerBlockEntity;
import com.thebrokenrail.gulliverreloaded.client.block.MiniaturizerBlockEntityRenderer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilderStorage;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.TexturedRenderLayers;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.math.Matrix4f;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
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;

import java.util.Set;

@SuppressWarnings({"SynchronizeOnNonFinalField", "unused"})
@Mixin(WorldRenderer.class)
@Environment(EnvType.CLIENT)
public abstract class MixinWorldRenderer {
    @Shadow
    @Final
    private Set<BlockEntity> noCullingBlockEntities;

    @Shadow
    @Final
    private BufferBuilderStorage bufferBuilders;

    @Shadow
    @Final
    private MinecraftClient client;

    @Shadow
    protected abstract void checkEmpty(MatrixStack matrix);

    @Inject(at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V", args = {"ldc=blockentities"}), method = "render")
    public void render(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo info) {
        Vec3d vec3d = camera.getPos();
        double d = vec3d.getX();
        double e = vec3d.getY();
        double f = vec3d.getZ();

        VertexConsumerProvider.Immediate immediate = bufferBuilders.getEntityVertexConsumers();

        synchronized (noCullingBlockEntities) {
            for (BlockEntity blockEntity : noCullingBlockEntities) {
                System.out.println(blockEntity);
                if (blockEntity instanceof MiniaturizerBlockEntity) {
                    BlockPos blockPos = blockEntity.getPos();
                    matrices.push();
                    matrices.translate((double) blockPos.getX() - d, (double) blockPos.getY() - e, (double) blockPos.getZ() - f);
                    MiniaturizerBlockEntityRenderer.render((MiniaturizerBlockEntity) blockEntity, tickDelta, matrices, bufferBuilders);
                    matrices.pop();
                }
            }
        }

        checkEmpty(matrices);
    }
}

and

@Environment(EnvType.CLIENT)
public class MiniaturizerBlockEntityRenderer {
    private static Random random = new Random();

    public static void render(MiniaturizerBlockEntity blockEntity, float tickDelta, MatrixStack matrices, BufferBuilderStorage vertexConsumers) {
        MinecraftClient client = MinecraftClient.getInstance();
        matrices.push();
        matrices.push();
        matrices.scale(1f / MiniaturizerBlock.BLOCK_SIZE, 1f / MiniaturizerBlock.BLOCK_SIZE, 1f / MiniaturizerBlock.BLOCK_SIZE);
        ServerWorld world = blockEntity.getMiniaturizerWorld();
        if (world != null) {
            for (int x = 0; x < MiniaturizerBlock.BLOCK_SIZE; x++) {
                for (int y = 0; y < MiniaturizerBlock.BLOCK_SIZE; y++) {
                    for (int z = 0; z < MiniaturizerBlock.BLOCK_SIZE; z++) {
                        matrices.push();
                        matrices.translate(x, y, z);
                        BlockPos pos = new BlockPos(x,y, z).add(blockEntity.getOffset());
                        BlockState blockState = world.getBlockState(pos);
                        if (blockState.getRenderType() != BlockRenderType.INVISIBLE) {
                            RenderLayer layer = RenderLayers.getBlockLayer(blockState);
                            client.getBlockRenderManager().renderBlock(blockState, pos, blockEntity.getMiniaturizerWorld(), matrices, getBuffer(vertexConsumers, layer), false, random);
                            drawBuffer(vertexConsumers, layer);
                        }
                        if (blockState.getBlock().hasBlockEntity()) {
                            BlockEntity entity = world.getBlockEntity(pos);
                            if (entity != null) {
                                BlockEntityRenderDispatcher.INSTANCE.render(entity, tickDelta, matrices, vertexConsumers.getEntityVertexConsumers());
                            }
                        }
                        matrices.pop();
                        matrices.push();
                        FluidState fluidState = blockState.getFluidState();
                        if (!fluidState.isEmpty()) {
                            RenderLayer layer = RenderLayers.getFluidLayer(fluidState);
                            client.getBlockRenderManager().renderFluid(pos, world, getBuffer(vertexConsumers, layer), fluidState);
                            drawBuffer(vertexConsumers, layer);
                        }
                        matrices.pop();
                    }
                }
            }
        }
        matrices.pop();
        if (client.player != null && client.player.getMainHandStack().getItem() == GulliverReloaded.MINIATURIZER_ITEM) {
            WorldRenderer.drawBox(matrices, vertexConsumers.getEntityVertexConsumers().getBuffer(RenderLayer.getLines()), 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f);
        }
        matrices.pop();
    }

    private static BufferBuilder getBuffer(BufferBuilderStorage bufferBuilders, RenderLayer layer) {
        BufferBuilder buffer = bufferBuilders.getBlockBufferBuilders().get(layer);
        if (!buffer.isBuilding()) {
            buffer.begin(7, VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL);
        }
        return buffer;
    }

    private static void drawBuffer(BufferBuilderStorage bufferBuilders, RenderLayer layer) {
        BufferBuilder buffer = bufferBuilders.getBlockBufferBuilders().get(layer);
        layer.draw(buffer, 0, 0, 0);
        if (buffer.isBuilding()) {
            buffer.end();
        }
    }
}

and I am getting:

java.lang.IllegalStateException: Not filled all elements of the vertex
	at net.minecraft.client.render.BufferBuilder.next(BufferBuilder.java:273)
	at net.minecraft.client.render.block.FluidRenderer.vertex(FluidRenderer.java:289)
	at net.minecraft.client.render.block.FluidRenderer.render(FluidRenderer.java:162)
	at net.minecraft.client.render.block.BlockRenderManager.renderFluid(BlockRenderManager.java:73)
	at com.thebrokenrail.gulliverreloaded.client.block.MiniaturizerBlockEntityRenderer.render(MiniaturizerBlockEntityRenderer.java:60)
	at net.minecraft.client.render.WorldRenderer.handler$zbe000$render(WorldRenderer.java:2994)
	at net.minecraft.client.render.WorldRenderer.render(WorldRenderer.java:1154)
	at net.minecraft.client.render.GameRenderer.renderWorld(GameRenderer.java:718)
	at net.minecraft.client.render.GameRenderer.render(GameRenderer.java:543)
	at net.minecraft.client.MinecraftClient.render(MinecraftClient.java:1003)
	at net.minecraft.client.MinecraftClient.run(MinecraftClient.java:631)
	at net.minecraft.client.main.Main.main(Main.java:204)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at net.fabricmc.loader.game.MinecraftGameProvider.launch(MinecraftGameProvider.java:192)
	at net.fabricmc.loader.launch.knot.Knot.init(Knot.java:138)
	at net.fabricmc.loader.launch.knot.KnotClient.main(KnotClient.java:26)
	at net.fabricmc.devlaunchinjector.Main.main(Main.java:86)
commented

Any updates on this?

commented

Closing since this doesn't sound like an issue with fabric's api or rendering. I'd advise going to discord or IRC for assistance there.

If this does not function properly due to fabric's rendering implementation and or api, please open a new issue.