RyanHub – file viewer
filename: common/src/main/java/rearth/oritech/client/renderers/BlockOutlineRenderer.java
branch: 1.21
back to repo
package rearth.oritech.client.renderers;

import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.*;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.property.Properties;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3i;
import net.minecraft.util.shape.VoxelShapes;
import rearth.oritech.Oritech;
import rearth.oritech.block.base.block.MultiblockMachine;
import rearth.oritech.block.blocks.augmenter.AugmentResearchStationBlock;
import rearth.oritech.block.blocks.storage.LargeStorageBlock;
import rearth.oritech.block.blocks.storage.SmallStorageBlock;
import rearth.oritech.init.BlockContent;
import rearth.oritech.init.ItemContent;
import rearth.oritech.init.TagContent;
import rearth.oritech.init.ToolsContent;
import rearth.oritech.item.tools.harvesting.PromethiumPickaxeItem;
import rearth.oritech.util.Geometry;
import rearth.oritech.util.MultiblockMachineController;

import java.util.ArrayList;

public class BlockOutlineRenderer {
    public static void render(ClientWorld world, Camera camera, MatrixStack matrixStack, VertexConsumerProvider consumer) {
        if (world == null) return;
        
        var client = MinecraftClient.getInstance();
        var player = client.player;
        if (player == null || player.isSneaking()) return;
        if (client.crosshairTarget == null || client.crosshairTarget.getType() != HitResult.Type.BLOCK) return;
        
        var itemStack = player.getMainHandStack();
        var blockPos = ((BlockHitResult) client.crosshairTarget).getBlockPos();
        
        if (Oritech.CONFIG.showMachinePreview())
            renderBlockPlacementPreviewOutline(world, camera, matrixStack, consumer, itemStack, player, blockPos);
        
        renderPromethiumPickaxeOutline(world, camera, matrixStack, consumer, itemStack, player, blockPos);
    }
    
    private static void renderBlockPlacementPreviewOutline(ClientWorld world, Camera camera, MatrixStack matrixStack, VertexConsumerProvider consumer, ItemStack itemStack, ClientPlayerEntity player, BlockPos blockPos) {
        
        var hasBlockItem = itemStack.getItem() instanceof BlockItem || itemStack.getItem().equals(ItemContent.UNSTABLE_CONTAINER);
        
        if (!hasBlockItem) return;
        
        var block = itemStack.getItem() instanceof BlockItem ? ((BlockItem) itemStack.getItem()).getBlock() : BlockContent.UNSTABLE_CONTAINER;
        
        if (!(block instanceof BlockEntityProvider entityProvider) || !block.getDefaultState().contains(MultiblockMachine.ASSEMBLED))
            return;
        
        var machinePos = blockPos.add(((BlockHitResult) player.client.crosshairTarget).getSide().getVector());
        if (itemStack.getItem().equals(ItemContent.UNSTABLE_CONTAINER))
            machinePos = blockPos;
        var placementState = block.getPlacementState(new ItemPlacementContext(player, player.preferredHand, itemStack, (BlockHitResult) player.client.crosshairTarget));
        var entity = entityProvider.createBlockEntity(machinePos, placementState);
        if (!(entity instanceof MultiblockMachineController multiblockController)) return;
        
        if (itemStack.getItem().equals(ItemContent.UNSTABLE_CONTAINER)) {
            var blockState = world.getBlockState(machinePos);
            var isValid = blockState.isIn(TagContent.UNSTABLE_CONTAINER_SOURCES_LOW) || blockState.isIn(TagContent.UNSTABLE_CONTAINER_SOURCES_MEDIUM) || blockState.isIn(TagContent.UNSTABLE_CONTAINER_SOURCES_HIGH);
            if (!isValid) return;
        }
        
        var coreOffsets = multiblockController.getCorePositions();
        var machineFacing = getFacingFromState(placementState);
        
        if (block instanceof LargeStorageBlock) {    // the large block is weird
            machineFacing = player.getHorizontalFacing().getOpposite();
        } else if (block instanceof AugmentResearchStationBlock) {
            machineFacing = player.getFacing();
        } else if (!(block instanceof MultiblockMachine)) {
            machineFacing = machineFacing.getOpposite();
        }
        
        var fullList = new ArrayList<>(coreOffsets);
        fullList.add(Vec3i.ZERO);
        
        matrixStack.push();
        var cameraPos = camera.getPos();
        matrixStack.translate(-cameraPos.getX(), -cameraPos.getY(), -cameraPos.getZ());
        matrixStack.translate(0.005f, 0.005f, 0.005f); // slight offset to avoid z fighting
        
        var shape = VoxelShapes.fullCube();
        for (var coreOffset : fullList) {
            var fixedOffset = new Vec3i(coreOffset.getX(), coreOffset.getY(), coreOffset.getZ());
            var worldOffset = Geometry.offsetToWorldPosition(machineFacing, fixedOffset, machinePos);
            shape = VoxelShapes.union(shape, VoxelShapes.cuboid(worldOffset.getX(), worldOffset.getY(), worldOffset.getZ(), worldOffset.getX() + 1, worldOffset.getY() + 1, worldOffset.getZ() + 1));
        }
        
        WorldRenderer.drawCuboidShapeOutline(matrixStack, consumer.getBuffer(RenderLayer.getLines()), shape, 0, 0, 0, 1f, 1f, 1f, 0.7F);
        matrixStack.pop();
        
    }
    
    private static Direction getFacingFromState(BlockState state) {
        if (state.contains(Properties.HORIZONTAL_FACING)) {
            return state.get(Properties.HORIZONTAL_FACING);
        } else if (state.contains(Properties.FACING)) {
            return state.get(Properties.FACING);
        } else if (state.contains(SmallStorageBlock.TARGET_DIR)) {
            return state.get(SmallStorageBlock.TARGET_DIR);
        }
        
        return Direction.NORTH;
    }
    
    private static void renderPromethiumPickaxeOutline(ClientWorld world, Camera camera, MatrixStack matrixStack, VertexConsumerProvider consumer, ItemStack itemStack, ClientPlayerEntity player, BlockPos blockPos) {
        if (!itemStack.isOf(ToolsContent.PROMETHIUM_PICKAXE)) return;
        
        var offsetBlocks = PromethiumPickaxeItem.getOffsetBlocks(world, player, blockPos);
        
        matrixStack.push();
        var cameraPos = camera.getPos();
        matrixStack.translate(-cameraPos.getX(), -cameraPos.getY(), -cameraPos.getZ());
        
        for (var offsetPos : offsetBlocks) {
            var offsetState = world.getBlockState(offsetPos);
            var renderShape = offsetState.getOutlineShape(world, offsetPos);
            
            matrixStack.push();
            matrixStack.translate(offsetPos.getX(), offsetPos.getY(), offsetPos.getZ());
            WorldRenderer.drawCuboidShapeOutline(matrixStack, consumer.getBuffer(RenderLayer.getLines()), renderShape, 0, 0, 0, 0.0F, 0.0F, 0.0F, 0.35F);
            matrixStack.pop();
        }
        
        matrixStack.pop();
    }
}