minecraft-src/net/minecraft/client/renderer/entity/EntityRenderer.java
2025-09-18 12:27:44 +00:00

407 lines
15 KiB
Java

package net.minecraft.client.renderer.entity;
import com.google.common.collect.ImmutableList.Builder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.ArrayList;
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.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.renderer.entity.state.HitboxRenderState;
import net.minecraft.client.renderer.entity.state.HitboxesRenderState;
import net.minecraft.client.server.IntegratedServer;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityAttachment;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.entity.vehicle.NewMinecartBehavior;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
@Environment(EnvType.CLIENT)
public abstract class EntityRenderer<T extends Entity, S extends EntityRenderState> {
protected static final float NAMETAG_SCALE = 0.025F;
public static final int LEASH_RENDER_STEPS = 24;
public static final float LEASH_WIDTH = 0.05F;
protected final EntityRenderDispatcher entityRenderDispatcher;
private final Font font;
protected float shadowRadius;
protected float shadowStrength = 1.0F;
private final S reusedState = this.createRenderState();
protected EntityRenderer(EntityRendererProvider.Context context) {
this.entityRenderDispatcher = context.getEntityRenderDispatcher();
this.font = context.getFont();
}
public final int getPackedLightCoords(T entity, float partialTicks) {
BlockPos blockPos = BlockPos.containing(entity.getLightProbePosition(partialTicks));
return LightTexture.pack(this.getBlockLightLevel(entity, blockPos), this.getSkyLightLevel(entity, blockPos));
}
protected int getSkyLightLevel(T entity, BlockPos pos) {
return entity.level().getBrightness(LightLayer.SKY, pos);
}
protected int getBlockLightLevel(T entity, BlockPos pos) {
return entity.isOnFire() ? 15 : entity.level().getBrightness(LightLayer.BLOCK, pos);
}
public boolean shouldRender(T livingEntity, Frustum camera, double camX, double camY, double camZ) {
if (!livingEntity.shouldRender(camX, camY, camZ)) {
return false;
} else if (!this.affectedByCulling(livingEntity)) {
return true;
} else {
AABB aABB = this.getBoundingBoxForCulling(livingEntity).inflate(0.5);
if (aABB.hasNaN() || aABB.getSize() == 0.0) {
aABB = new AABB(
livingEntity.getX() - 2.0,
livingEntity.getY() - 2.0,
livingEntity.getZ() - 2.0,
livingEntity.getX() + 2.0,
livingEntity.getY() + 2.0,
livingEntity.getZ() + 2.0
);
}
if (camera.isVisible(aABB)) {
return true;
} else {
if (livingEntity instanceof Leashable leashable) {
Entity entity = leashable.getLeashHolder();
if (entity != null) {
AABB aABB2 = this.entityRenderDispatcher.getRenderer(entity).getBoundingBoxForCulling(entity);
return camera.isVisible(aABB2) || camera.isVisible(aABB.minmax(aABB2));
}
}
return false;
}
}
}
protected AABB getBoundingBoxForCulling(T minecraft) {
return minecraft.getBoundingBox();
}
protected boolean affectedByCulling(T display) {
return true;
}
public Vec3 getRenderOffset(S renderState) {
return renderState.passengerOffset != null ? renderState.passengerOffset : Vec3.ZERO;
}
public void render(S renderState, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
if (renderState.leashStates != null) {
for (EntityRenderState.LeashState leashState : renderState.leashStates) {
renderLeash(poseStack, bufferSource, leashState);
}
}
if (renderState.nameTag != null) {
this.renderNameTag(renderState, renderState.nameTag, poseStack, bufferSource, packedLight);
}
}
private static void renderLeash(PoseStack poseStack, MultiBufferSource buffer, EntityRenderState.LeashState leashState) {
float f = (float)(leashState.end.x - leashState.start.x);
float g = (float)(leashState.end.y - leashState.start.y);
float h = (float)(leashState.end.z - leashState.start.z);
float i = Mth.invSqrt(f * f + h * h) * 0.05F / 2.0F;
float j = h * i;
float k = f * i;
poseStack.pushPose();
poseStack.translate(leashState.offset);
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.leash());
Matrix4f matrix4f = poseStack.last().pose();
for (int l = 0; l <= 24; l++) {
addVertexPair(vertexConsumer, matrix4f, f, g, h, 0.05F, 0.05F, j, k, l, false, leashState);
}
for (int l = 24; l >= 0; l--) {
addVertexPair(vertexConsumer, matrix4f, f, g, h, 0.05F, 0.0F, j, k, l, true, leashState);
}
poseStack.popPose();
}
private static void addVertexPair(
VertexConsumer consumer,
Matrix4f pose,
float startX,
float startY,
float startZ,
float yOffset,
float dy,
float dx,
float dz,
int index,
boolean reverse,
EntityRenderState.LeashState leashState
) {
float f = index / 24.0F;
int i = (int)Mth.lerp(f, (float)leashState.startBlockLight, (float)leashState.endBlockLight);
int j = (int)Mth.lerp(f, (float)leashState.startSkyLight, (float)leashState.endSkyLight);
int k = LightTexture.pack(i, j);
float g = index % 2 == (reverse ? 1 : 0) ? 0.7F : 1.0F;
float h = 0.5F * g;
float l = 0.4F * g;
float m = 0.3F * g;
float n = startX * f;
float o;
if (leashState.slack) {
o = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f);
} else {
o = startY * f;
}
float p = startZ * f;
consumer.addVertex(pose, n - dx, o + dy, p + dz).setColor(h, l, m, 1.0F).setLight(k);
consumer.addVertex(pose, n + dx, o + yOffset - dy, p - dz).setColor(h, l, m, 1.0F).setLight(k);
}
protected boolean shouldShowName(T entity, double distanceToCameraSq) {
return entity.shouldShowName() || entity.hasCustomName() && entity == this.entityRenderDispatcher.crosshairPickEntity;
}
/**
* Returns the font renderer from the set render manager
*/
public Font getFont() {
return this.font;
}
protected void renderNameTag(S renderState, Component displayName, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
Vec3 vec3 = renderState.nameTagAttachment;
if (vec3 != null) {
boolean bl = !renderState.isDiscrete;
int i = "deadmau5".equals(displayName.getString()) ? -10 : 0;
poseStack.pushPose();
poseStack.translate(vec3.x, vec3.y + 0.5, vec3.z);
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
poseStack.scale(0.025F, -0.025F, 0.025F);
Matrix4f matrix4f = poseStack.last().pose();
Font font = this.getFont();
float f = -font.width(displayName) / 2.0F;
int j = (int)(Minecraft.getInstance().options.getBackgroundOpacity(0.25F) * 255.0F) << 24;
font.drawInBatch(
displayName, f, (float)i, -2130706433, false, matrix4f, bufferSource, bl ? Font.DisplayMode.SEE_THROUGH : Font.DisplayMode.NORMAL, j, packedLight
);
if (bl) {
font.drawInBatch(
displayName, f, (float)i, -1, false, matrix4f, bufferSource, Font.DisplayMode.NORMAL, 0, LightTexture.lightCoordsWithEmission(packedLight, 2)
);
}
poseStack.popPose();
}
}
@Nullable
protected Component getNameTag(T entity) {
return entity.getDisplayName();
}
protected float getShadowRadius(S renderState) {
return this.shadowRadius;
}
protected float getShadowStrength(S renderState) {
return this.shadowStrength;
}
public abstract S createRenderState();
public final S createRenderState(T entity, float partialTick) {
S entityRenderState = this.reusedState;
this.extractRenderState(entity, entityRenderState, partialTick);
return entityRenderState;
}
public void extractRenderState(T entity, S reusedState, float partialTick) {
reusedState.entityType = entity.getType();
reusedState.x = Mth.lerp((double)partialTick, entity.xOld, entity.getX());
reusedState.y = Mth.lerp((double)partialTick, entity.yOld, entity.getY());
reusedState.z = Mth.lerp((double)partialTick, entity.zOld, entity.getZ());
reusedState.isInvisible = entity.isInvisible();
reusedState.ageInTicks = entity.tickCount + partialTick;
reusedState.boundingBoxWidth = entity.getBbWidth();
reusedState.boundingBoxHeight = entity.getBbHeight();
reusedState.eyeHeight = entity.getEyeHeight();
if (entity.isPassenger()
&& entity.getVehicle() instanceof AbstractMinecart abstractMinecart
&& abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior
&& newMinecartBehavior.cartHasPosRotLerp()) {
double d = Mth.lerp((double)partialTick, abstractMinecart.xOld, abstractMinecart.getX());
double e = Mth.lerp((double)partialTick, abstractMinecart.yOld, abstractMinecart.getY());
double f = Mth.lerp((double)partialTick, abstractMinecart.zOld, abstractMinecart.getZ());
reusedState.passengerOffset = newMinecartBehavior.getCartLerpPosition(partialTick).subtract(new Vec3(d, e, f));
} else {
reusedState.passengerOffset = null;
}
reusedState.distanceToCameraSq = this.entityRenderDispatcher.distanceToSqr(entity);
boolean bl = reusedState.distanceToCameraSq < 4096.0 && this.shouldShowName(entity, reusedState.distanceToCameraSq);
if (bl) {
reusedState.nameTag = this.getNameTag(entity);
reusedState.nameTagAttachment = entity.getAttachments().getNullable(EntityAttachment.NAME_TAG, 0, entity.getYRot(partialTick));
} else {
reusedState.nameTag = null;
}
label77: {
reusedState.isDiscrete = entity.isDiscrete();
if (entity instanceof Leashable leashable) {
Entity g = leashable.getLeashHolder();
if (g instanceof Entity) {
float gx = entity.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
Vec3 vec3 = leashable.getLeashOffset(partialTick);
BlockPos blockPos = BlockPos.containing(entity.getEyePosition(partialTick));
BlockPos blockPos2 = BlockPos.containing(g.getEyePosition(partialTick));
int i = this.getBlockLightLevel(entity, blockPos);
int j = this.entityRenderDispatcher.getRenderer(g).getBlockLightLevel(g, blockPos2);
int k = entity.level().getBrightness(LightLayer.SKY, blockPos);
int l = entity.level().getBrightness(LightLayer.SKY, blockPos2);
boolean bl2 = g.supportQuadLeashAsHolder() && leashable.supportQuadLeash();
int m = bl2 ? 4 : 1;
if (reusedState.leashStates == null || reusedState.leashStates.size() != m) {
reusedState.leashStates = new ArrayList(m);
for (int n = 0; n < m; n++) {
reusedState.leashStates.add(new EntityRenderState.LeashState());
}
}
if (bl2) {
float h = g.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
Vec3 vec32 = g.getPosition(partialTick);
Vec3[] vec3s = leashable.getQuadLeashOffsets();
Vec3[] vec3s2 = g.getQuadLeashHolderOffsets();
int o = 0;
while (true) {
if (o >= m) {
break label77;
}
EntityRenderState.LeashState leashState = (EntityRenderState.LeashState)reusedState.leashStates.get(o);
leashState.offset = vec3s[o].yRot(-gx);
leashState.start = entity.getPosition(partialTick).add(leashState.offset);
leashState.end = vec32.add(vec3s2[o].yRot(-h));
leashState.startBlockLight = i;
leashState.endBlockLight = j;
leashState.startSkyLight = k;
leashState.endSkyLight = l;
leashState.slack = false;
o++;
}
} else {
Vec3 vec33 = vec3.yRot(-gx);
EntityRenderState.LeashState leashState2 = (EntityRenderState.LeashState)reusedState.leashStates.getFirst();
leashState2.offset = vec33;
leashState2.start = entity.getPosition(partialTick).add(vec33);
leashState2.end = g.getRopeHoldPosition(partialTick);
leashState2.startBlockLight = i;
leashState2.endBlockLight = j;
leashState2.startSkyLight = k;
leashState2.endSkyLight = l;
break label77;
}
}
}
reusedState.leashStates = null;
}
reusedState.displayFireAnimation = entity.displayFireAnimation();
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.getEntityRenderDispatcher().shouldRenderHitBoxes() && !reusedState.isInvisible && !minecraft.showOnlyReducedInfo()) {
this.extractHitboxes(entity, reusedState, partialTick);
} else {
reusedState.hitboxesRenderState = null;
reusedState.serverHitboxesRenderState = null;
}
}
private void extractHitboxes(T entity, S reusedState, float partialTick) {
reusedState.hitboxesRenderState = this.extractHitboxes(entity, partialTick, false);
reusedState.serverHitboxesRenderState = null;
}
private HitboxesRenderState extractHitboxes(T entity, float partialTick, boolean green) {
Builder<HitboxRenderState> builder = new Builder<>();
AABB aABB = entity.getBoundingBox();
HitboxRenderState hitboxRenderState;
if (green) {
hitboxRenderState = new HitboxRenderState(
aABB.minX - entity.getX(),
aABB.minY - entity.getY(),
aABB.minZ - entity.getZ(),
aABB.maxX - entity.getX(),
aABB.maxY - entity.getY(),
aABB.maxZ - entity.getZ(),
0.0F,
1.0F,
0.0F
);
} else {
hitboxRenderState = new HitboxRenderState(
aABB.minX - entity.getX(),
aABB.minY - entity.getY(),
aABB.minZ - entity.getZ(),
aABB.maxX - entity.getX(),
aABB.maxY - entity.getY(),
aABB.maxZ - entity.getZ(),
1.0F,
1.0F,
1.0F
);
}
builder.add(hitboxRenderState);
Entity entity2 = entity.getVehicle();
if (entity2 != null) {
float f = Math.min(entity2.getBbWidth(), entity.getBbWidth()) / 2.0F;
float g = 0.0625F;
Vec3 vec3 = entity2.getPassengerRidingPosition(entity).subtract(entity.position());
HitboxRenderState hitboxRenderState2 = new HitboxRenderState(vec3.x - f, vec3.y, vec3.z - f, vec3.x + f, vec3.y + 0.0625, vec3.z + f, 1.0F, 1.0F, 0.0F);
builder.add(hitboxRenderState2);
}
this.extractAdditionalHitboxes(entity, builder, partialTick);
Vec3 vec32 = entity.getViewVector(partialTick);
return new HitboxesRenderState(vec32.x, vec32.y, vec32.z, builder.build());
}
protected void extractAdditionalHitboxes(T entity, Builder<HitboxRenderState> hitboxes, float partialTick) {
}
@Nullable
private static Entity getServerSideEntity(Entity entity) {
IntegratedServer integratedServer = Minecraft.getInstance().getSingleplayerServer();
if (integratedServer != null) {
ServerLevel serverLevel = integratedServer.getLevel(entity.level().dimension());
if (serverLevel != null) {
return serverLevel.getEntity(entity.getId());
}
}
return null;
}
}