package net.minecraft.client.renderer.blockentity; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import java.util.List; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.Font.DisplayMode; import net.minecraft.client.model.Model; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider.Context; import net.minecraft.client.resources.model.Material; import net.minecraft.core.BlockPos; import net.minecraft.util.ARGB; import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.block.SignBlock; import net.minecraft.world.level.block.entity.SignBlockEntity; import net.minecraft.world.level.block.entity.SignText; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.WoodType; import net.minecraft.world.phys.Vec3; @Environment(EnvType.CLIENT) public abstract class AbstractSignRenderer implements BlockEntityRenderer { private static final int BLACK_TEXT_OUTLINE_COLOR = -988212; private static final int OUTLINE_RENDER_DISTANCE = Mth.square(16); private final Font font; public AbstractSignRenderer(Context context) { this.font = context.getFont(); } protected abstract Model getSignModel(BlockState state, WoodType woodType); protected abstract Material getSignMaterial(WoodType woodType); protected abstract float getSignModelRenderScale(); protected abstract float getSignTextRenderScale(); protected abstract Vec3 getTextOffset(); protected abstract void translateSign(PoseStack poseStack, float yRot, BlockState state); public void render(SignBlockEntity signBlockEntity, float f, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, int j, Vec3 vec3) { BlockState blockState = signBlockEntity.getBlockState(); SignBlock signBlock = (SignBlock)blockState.getBlock(); Model model = this.getSignModel(blockState, signBlock.type()); this.renderSignWithText(signBlockEntity, poseStack, multiBufferSource, i, j, blockState, signBlock, signBlock.type(), model); } private void renderSignWithText( SignBlockEntity blockEntity, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, BlockState state, SignBlock sign, WoodType woodType, Model model ) { poseStack.pushPose(); this.translateSign(poseStack, -sign.getYRotationDegrees(state), state); this.renderSign(poseStack, bufferSource, packedLight, packedOverlay, woodType, model); this.renderSignText( blockEntity.getBlockPos(), blockEntity.getFrontText(), poseStack, bufferSource, packedLight, blockEntity.getTextLineHeight(), blockEntity.getMaxTextLineWidth(), true ); this.renderSignText( blockEntity.getBlockPos(), blockEntity.getBackText(), poseStack, bufferSource, packedLight, blockEntity.getTextLineHeight(), blockEntity.getMaxTextLineWidth(), false ); poseStack.popPose(); } protected void renderSign(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, WoodType woodType, Model model) { poseStack.pushPose(); float f = this.getSignModelRenderScale(); poseStack.scale(f, -f, -f); Material material = this.getSignMaterial(woodType); VertexConsumer vertexConsumer = material.buffer(bufferSource, model::renderType); model.renderToBuffer(poseStack, vertexConsumer, packedLight, packedOverlay); poseStack.popPose(); } private void renderSignText( BlockPos pos, SignText text, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int lineHeight, int maxLineWidth, boolean isFront ) { poseStack.pushPose(); this.translateSignText(poseStack, isFront, this.getTextOffset()); int i = getDarkColor(text); int j = 4 * lineHeight / 2; FormattedCharSequence[] formattedCharSequences = text.getRenderMessages(Minecraft.getInstance().isTextFilteringEnabled(), component -> { List list = this.font.split(component, maxLineWidth); return list.isEmpty() ? FormattedCharSequence.EMPTY : (FormattedCharSequence)list.get(0); }); int k; boolean bl; int l; if (text.hasGlowingText()) { k = text.getColor().getTextColor(); bl = isOutlineVisible(pos, k); l = 15728880; } else { k = i; bl = false; l = packedLight; } for (int m = 0; m < 4; m++) { FormattedCharSequence formattedCharSequence = formattedCharSequences[m]; float f = -this.font.width(formattedCharSequence) / 2; if (bl) { this.font.drawInBatch8xOutline(formattedCharSequence, f, m * lineHeight - j, k, i, poseStack.last().pose(), bufferSource, l); } else { this.font .drawInBatch(formattedCharSequence, f, (float)(m * lineHeight - j), k, false, poseStack.last().pose(), bufferSource, DisplayMode.POLYGON_OFFSET, 0, l); } } poseStack.popPose(); } private void translateSignText(PoseStack poseStack, boolean isFront, Vec3 offset) { if (!isFront) { poseStack.mulPose(Axis.YP.rotationDegrees(180.0F)); } float f = 0.015625F * this.getSignTextRenderScale(); poseStack.translate(offset); poseStack.scale(f, -f, f); } private static boolean isOutlineVisible(BlockPos pos, int color) { if (color == DyeColor.BLACK.getTextColor()) { return true; } else { Minecraft minecraft = Minecraft.getInstance(); LocalPlayer localPlayer = minecraft.player; if (localPlayer != null && minecraft.options.getCameraType().isFirstPerson() && localPlayer.isScoping()) { return true; } else { Entity entity = minecraft.getCameraEntity(); return entity != null && entity.distanceToSqr(Vec3.atCenterOf(pos)) < OUTLINE_RENDER_DISTANCE; } } } public static int getDarkColor(SignText text) { int i = text.getColor().getTextColor(); if (i == DyeColor.BLACK.getTextColor() && text.hasGlowingText()) { return -988212; } else { double d = 0.4; int j = (int)(ARGB.red(i) * 0.4); int k = (int)(ARGB.green(i) * 0.4); int l = (int)(ARGB.blue(i) * 0.4); return ARGB.color(0, j, k, l); } } }