package net.minecraft.client.renderer.entity; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import com.mojang.math.Transformation; import java.util.ArrayList; import java.util.List; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.Font.DisplayMode; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.entity.EntityRendererProvider.Context; import net.minecraft.client.renderer.entity.state.BlockDisplayEntityRenderState; import net.minecraft.client.renderer.entity.state.DisplayEntityRenderState; import net.minecraft.client.renderer.entity.state.ItemDisplayEntityRenderState; import net.minecraft.client.renderer.entity.state.TextDisplayEntityRenderState; import net.minecraft.client.renderer.item.ItemModelResolver; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.util.FormattedCharSequence; import net.minecraft.world.entity.Display; import net.minecraft.world.entity.Display.BlockDisplay.BlockRenderState; import net.minecraft.world.entity.Display.ItemDisplay.ItemRenderState; import net.minecraft.world.entity.Display.TextDisplay.Align; import net.minecraft.world.entity.Display.TextDisplay.CachedInfo; import net.minecraft.world.entity.Display.TextDisplay.CachedLine; import net.minecraft.world.entity.Display.TextDisplay.TextRenderState; import net.minecraft.world.phys.AABB; import org.joml.Matrix4f; import org.joml.Quaternionf; @Environment(EnvType.CLIENT) public abstract class DisplayRenderer extends EntityRenderer { private final EntityRenderDispatcher entityRenderDispatcher; protected DisplayRenderer(Context context) { super(context); this.entityRenderDispatcher = context.getEntityRenderDispatcher(); } protected AABB getBoundingBoxForCulling(T display) { return display.getBoundingBoxForCulling(); } protected boolean affectedByCulling(T display) { return display.affectedByCulling(); } private static int getBrightnessOverride(Display display) { Display.RenderState renderState = display.renderState(); return renderState != null ? renderState.brightnessOverride() : -1; } protected int getSkyLightLevel(T display, BlockPos blockPos) { int i = getBrightnessOverride(display); return i != -1 ? LightTexture.sky(i) : super.getSkyLightLevel(display, blockPos); } protected int getBlockLightLevel(T display, BlockPos blockPos) { int i = getBrightnessOverride(display); return i != -1 ? LightTexture.block(i) : super.getBlockLightLevel(display, blockPos); } protected float getShadowRadius(ST displayEntityRenderState) { Display.RenderState renderState = displayEntityRenderState.renderState; return renderState == null ? 0.0F : renderState.shadowRadius().get(displayEntityRenderState.interpolationProgress); } protected float getShadowStrength(ST displayEntityRenderState) { Display.RenderState renderState = displayEntityRenderState.renderState; return renderState == null ? 0.0F : renderState.shadowStrength().get(displayEntityRenderState.interpolationProgress); } public void render(ST displayEntityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { Display.RenderState renderState = displayEntityRenderState.renderState; if (renderState != null && displayEntityRenderState.hasSubState()) { float f = displayEntityRenderState.interpolationProgress; super.render(displayEntityRenderState, poseStack, multiBufferSource, i); poseStack.pushPose(); poseStack.mulPose(this.calculateOrientation(renderState, displayEntityRenderState, new Quaternionf())); Transformation transformation = renderState.transformation().get(f); poseStack.mulPose(transformation.getMatrix()); this.renderInner(displayEntityRenderState, poseStack, multiBufferSource, i, f); poseStack.popPose(); } } private Quaternionf calculateOrientation(Display.RenderState renderState, ST entityRenderState, Quaternionf quaternion) { Camera camera = this.entityRenderDispatcher.camera; return switch (renderState.billboardConstraints()) { case FIXED -> quaternion.rotationYXZ( (float) (-Math.PI / 180.0) * entityRenderState.entityYRot, (float) (Math.PI / 180.0) * entityRenderState.entityXRot, 0.0F ); case HORIZONTAL -> quaternion.rotationYXZ((float) (-Math.PI / 180.0) * entityRenderState.entityYRot, (float) (Math.PI / 180.0) * cameraXRot(camera), 0.0F); case VERTICAL -> quaternion.rotationYXZ((float) (-Math.PI / 180.0) * cameraYrot(camera), (float) (Math.PI / 180.0) * entityRenderState.entityXRot, 0.0F); case CENTER -> quaternion.rotationYXZ((float) (-Math.PI / 180.0) * cameraYrot(camera), (float) (Math.PI / 180.0) * cameraXRot(camera), 0.0F); }; } private static float cameraYrot(Camera camera) { return camera.getYRot() - 180.0F; } private static float cameraXRot(Camera camera) { return -camera.getXRot(); } private static float entityYRot(T entity, float partialTick) { return entity.getYRot(partialTick); } private static float entityXRot(T entity, float partialTick) { return entity.getXRot(partialTick); } protected abstract void renderInner(ST renderState, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, float interpolationProgress); public void extractRenderState(T display, ST displayEntityRenderState, float f) { super.extractRenderState(display, displayEntityRenderState, f); displayEntityRenderState.renderState = display.renderState(); displayEntityRenderState.interpolationProgress = display.calculateInterpolationProgress(f); displayEntityRenderState.entityYRot = entityYRot(display, f); displayEntityRenderState.entityXRot = entityXRot(display, f); } @Environment(EnvType.CLIENT) public static class BlockDisplayRenderer extends DisplayRenderer { private final BlockRenderDispatcher blockRenderer; protected BlockDisplayRenderer(Context context) { super(context); this.blockRenderer = context.getBlockRenderDispatcher(); } public BlockDisplayEntityRenderState createRenderState() { return new BlockDisplayEntityRenderState(); } public void extractRenderState(Display.BlockDisplay blockDisplay, BlockDisplayEntityRenderState blockDisplayEntityRenderState, float f) { super.extractRenderState(blockDisplay, blockDisplayEntityRenderState, f); blockDisplayEntityRenderState.blockRenderState = blockDisplay.blockRenderState(); } public void renderInner(BlockDisplayEntityRenderState blockDisplayEntityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, float f) { this.blockRenderer .renderSingleBlock(blockDisplayEntityRenderState.blockRenderState.blockState(), poseStack, multiBufferSource, i, OverlayTexture.NO_OVERLAY); } } @Environment(EnvType.CLIENT) public static class ItemDisplayRenderer extends DisplayRenderer { private final ItemModelResolver itemModelResolver; protected ItemDisplayRenderer(Context context) { super(context); this.itemModelResolver = context.getItemModelResolver(); } public ItemDisplayEntityRenderState createRenderState() { return new ItemDisplayEntityRenderState(); } public void extractRenderState(Display.ItemDisplay itemDisplay, ItemDisplayEntityRenderState itemDisplayEntityRenderState, float f) { super.extractRenderState(itemDisplay, itemDisplayEntityRenderState, f); ItemRenderState itemRenderState = itemDisplay.itemRenderState(); if (itemRenderState != null) { this.itemModelResolver.updateForNonLiving(itemDisplayEntityRenderState.item, itemRenderState.itemStack(), itemRenderState.itemTransform(), itemDisplay); } else { itemDisplayEntityRenderState.item.clear(); } } public void renderInner(ItemDisplayEntityRenderState itemDisplayEntityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, float f) { if (!itemDisplayEntityRenderState.item.isEmpty()) { poseStack.mulPose(Axis.YP.rotation((float) Math.PI)); itemDisplayEntityRenderState.item.render(poseStack, multiBufferSource, i, OverlayTexture.NO_OVERLAY); } } } @Environment(EnvType.CLIENT) public static class TextDisplayRenderer extends DisplayRenderer { private final Font font; protected TextDisplayRenderer(Context context) { super(context); this.font = context.getFont(); } public TextDisplayEntityRenderState createRenderState() { return new TextDisplayEntityRenderState(); } public void extractRenderState(Display.TextDisplay textDisplay, TextDisplayEntityRenderState textDisplayEntityRenderState, float f) { super.extractRenderState(textDisplay, textDisplayEntityRenderState, f); textDisplayEntityRenderState.textRenderState = textDisplay.textRenderState(); textDisplayEntityRenderState.cachedInfo = textDisplay.cacheDisplay(this::splitLines); } private CachedInfo splitLines(Component text, int maxWidth) { List list = this.font.split(text, maxWidth); List list2 = new ArrayList(list.size()); int i = 0; for (FormattedCharSequence formattedCharSequence : list) { int j = this.font.width(formattedCharSequence); i = Math.max(i, j); list2.add(new CachedLine(formattedCharSequence, j)); } return new CachedInfo(list2, i); } public void renderInner(TextDisplayEntityRenderState textDisplayEntityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, float f) { TextRenderState textRenderState = textDisplayEntityRenderState.textRenderState; byte b = textRenderState.flags(); boolean bl = (b & 2) != 0; boolean bl2 = (b & 4) != 0; boolean bl3 = (b & 1) != 0; Align align = Display.TextDisplay.getAlign(b); byte c = (byte)textRenderState.textOpacity().get(f); int j; if (bl2) { float g = Minecraft.getInstance().options.getBackgroundOpacity(0.25F); j = (int)(g * 255.0F) << 24; } else { j = textRenderState.backgroundColor().get(f); } float g = 0.0F; Matrix4f matrix4f = poseStack.last().pose(); matrix4f.rotate((float) Math.PI, 0.0F, 1.0F, 0.0F); matrix4f.scale(-0.025F, -0.025F, -0.025F); CachedInfo cachedInfo = textDisplayEntityRenderState.cachedInfo; int k = 1; int l = 9 + 1; int m = cachedInfo.width(); int n = cachedInfo.lines().size() * l - 1; matrix4f.translate(1.0F - m / 2.0F, -n, 0.0F); if (j != 0) { VertexConsumer vertexConsumer = multiBufferSource.getBuffer(bl ? RenderType.textBackgroundSeeThrough() : RenderType.textBackground()); vertexConsumer.addVertex(matrix4f, -1.0F, -1.0F, 0.0F).setColor(j).setLight(i); vertexConsumer.addVertex(matrix4f, -1.0F, (float)n, 0.0F).setColor(j).setLight(i); vertexConsumer.addVertex(matrix4f, (float)m, (float)n, 0.0F).setColor(j).setLight(i); vertexConsumer.addVertex(matrix4f, (float)m, -1.0F, 0.0F).setColor(j).setLight(i); } for (CachedLine cachedLine : cachedInfo.lines()) { float h = switch (align) { case LEFT -> 0.0F; case RIGHT -> m - cachedLine.width(); case CENTER -> m / 2.0F - cachedLine.width() / 2.0F; default -> throw new MatchException(null, null); }; this.font .drawInBatch( cachedLine.contents(), h, g, c << 24 | 16777215, bl3, matrix4f, multiBufferSource, bl ? DisplayMode.SEE_THROUGH : DisplayMode.POLYGON_OFFSET, 0, i ); g += l; } } } }