minecraft-src/net/minecraft/client/renderer/entity/DisplayRenderer.java
2025-07-04 02:49:36 +03:00

281 lines
12 KiB
Java

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.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.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.texture.OverlayTexture;
import net.minecraft.client.resources.model.BakedModel;
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<T extends Display, S, ST extends DisplayEntityRenderState> extends EntityRenderer<T, ST> {
private final EntityRenderDispatcher entityRenderDispatcher;
protected DisplayRenderer(EntityRendererProvider.Context context) {
super(context);
this.entityRenderDispatcher = context.getEntityRenderDispatcher();
}
protected AABB getBoundingBoxForCulling(T minecraft) {
return minecraft.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 entity, BlockPos pos) {
int i = getBrightnessOverride(entity);
return i != -1 ? LightTexture.sky(i) : super.getSkyLightLevel(entity, pos);
}
protected int getBlockLightLevel(T entity, BlockPos pos) {
int i = getBrightnessOverride(entity);
return i != -1 ? LightTexture.block(i) : super.getBlockLightLevel(entity, pos);
}
public void render(ST renderState, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
Display.RenderState renderState2 = renderState.renderState;
if (renderState2 != null) {
if (renderState.hasSubState()) {
float f = renderState.interpolationProgress;
this.shadowRadius = renderState2.shadowRadius().get(f);
this.shadowStrength = renderState2.shadowStrength().get(f);
super.render(renderState, poseStack, bufferSource, packedLight);
poseStack.pushPose();
poseStack.mulPose(this.calculateOrientation(renderState2, renderState, new Quaternionf()));
Transformation transformation = renderState2.transformation().get(f);
poseStack.mulPose(transformation.getMatrix());
this.renderInner(renderState, poseStack, bufferSource, packedLight, 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 <T extends Display> float entityYRot(T entity, float partialTick) {
return entity.getYRot(partialTick);
}
private static <T extends Display> 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 entity, ST reusedState, float partialTick) {
super.extractRenderState(entity, reusedState, partialTick);
reusedState.renderState = entity.renderState();
reusedState.interpolationProgress = entity.calculateInterpolationProgress(partialTick);
reusedState.entityYRot = entityYRot(entity, partialTick);
reusedState.entityXRot = entityXRot(entity, partialTick);
}
@Environment(EnvType.CLIENT)
public static class BlockDisplayRenderer extends DisplayRenderer<Display.BlockDisplay, BlockRenderState, BlockDisplayEntityRenderState> {
private final BlockRenderDispatcher blockRenderer;
protected BlockDisplayRenderer(EntityRendererProvider.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<Display.ItemDisplay, ItemRenderState, ItemDisplayEntityRenderState> {
private final ItemRenderer itemRenderer;
protected ItemDisplayRenderer(EntityRendererProvider.Context context) {
super(context);
this.itemRenderer = context.getItemRenderer();
}
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) {
itemDisplayEntityRenderState.itemRenderState = itemRenderState;
itemDisplayEntityRenderState.itemModel = this.itemRenderer
.getModel(itemDisplayEntityRenderState.itemRenderState.itemStack(), itemDisplay.level(), null, itemDisplay.getId());
} else {
itemDisplayEntityRenderState.itemRenderState = null;
itemDisplayEntityRenderState.itemModel = null;
}
}
public void renderInner(ItemDisplayEntityRenderState itemDisplayEntityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, float f) {
ItemRenderState itemRenderState = itemDisplayEntityRenderState.itemRenderState;
BakedModel bakedModel = itemDisplayEntityRenderState.itemModel;
if (itemRenderState != null && bakedModel != null) {
poseStack.mulPose(Axis.YP.rotation((float) Math.PI));
this.itemRenderer
.render(itemRenderState.itemStack(), itemRenderState.itemTransform(), false, poseStack, multiBufferSource, i, OverlayTexture.NO_OVERLAY, bakedModel);
}
}
}
@Environment(EnvType.CLIENT)
public static class TextDisplayRenderer extends DisplayRenderer<Display.TextDisplay, TextRenderState, TextDisplayEntityRenderState> {
private final Font font;
protected TextDisplayRenderer(EntityRendererProvider.Context context) {
super(context);
this.font = context.getFont();
}
public TextDisplayEntityRenderState createRenderState() {
return new TextDisplayEntityRenderState();
}
public void extractRenderState(Display.TextDisplay entity, TextDisplayEntityRenderState reusedState, float partialTick) {
super.extractRenderState(entity, reusedState, partialTick);
reusedState.textRenderState = entity.textRenderState();
reusedState.cachedInfo = entity.cacheDisplay(this::splitLines);
}
private CachedInfo splitLines(Component text, int maxWidth) {
List<FormattedCharSequence> list = this.font.split(text, maxWidth);
List<CachedLine> 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 renderState, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, float interpolationProgress
) {
TextRenderState textRenderState = renderState.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(interpolationProgress);
int i;
if (bl2) {
float f = Minecraft.getInstance().options.getBackgroundOpacity(0.25F);
i = (int)(f * 255.0F) << 24;
} else {
i = textRenderState.backgroundColor().get(interpolationProgress);
}
float f = 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 = renderState.cachedInfo;
int j = 1;
int k = 9 + 1;
int l = cachedInfo.width();
int m = cachedInfo.lines().size() * k - 1;
matrix4f.translate(1.0F - l / 2.0F, -m, 0.0F);
if (i != 0) {
VertexConsumer vertexConsumer = bufferSource.getBuffer(bl ? RenderType.textBackgroundSeeThrough() : RenderType.textBackground());
vertexConsumer.addVertex(matrix4f, -1.0F, -1.0F, 0.0F).setColor(i).setLight(packedLight);
vertexConsumer.addVertex(matrix4f, -1.0F, (float)m, 0.0F).setColor(i).setLight(packedLight);
vertexConsumer.addVertex(matrix4f, (float)l, (float)m, 0.0F).setColor(i).setLight(packedLight);
vertexConsumer.addVertex(matrix4f, (float)l, -1.0F, 0.0F).setColor(i).setLight(packedLight);
}
for (CachedLine cachedLine : cachedInfo.lines()) {
float g = switch (align) {
case LEFT -> 0.0F;
case RIGHT -> l - cachedLine.width();
case CENTER -> l / 2.0F - cachedLine.width() / 2.0F;
default -> throw new MatchException(null, null);
};
this.font
.drawInBatch(
cachedLine.contents(),
g,
f,
c << 24 | 16777215,
bl3,
matrix4f,
bufferSource,
bl ? Font.DisplayMode.SEE_THROUGH : Font.DisplayMode.POLYGON_OFFSET,
0,
packedLight
);
f += k;
}
}
}
}