minecraft-src/net/minecraft/client/renderer/entity/EntityRenderer.java
2025-07-04 03:45:38 +03:00

390 lines
14 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 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.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.EntityRendererProvider.Context;
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;
protected final EntityRenderDispatcher entityRenderDispatcher;
private final Font font;
protected float shadowRadius;
protected float shadowStrength = 1.0F;
private final S reusedState = this.createRenderState();
protected EntityRenderer(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) {
return camera.isVisible(this.entityRenderDispatcher.getRenderer(entity).getBoundingBoxForCulling(entity));
}
}
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) {
EntityRenderState.LeashState leashState = renderState.leashState;
if (leashState != null) {
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 = 0.025F;
float g = (float)(leashState.end.x - leashState.start.x);
float h = (float)(leashState.end.y - leashState.start.y);
float i = (float)(leashState.end.z - leashState.start.z);
float j = Mth.invSqrt(g * g + i * i) * 0.025F / 2.0F;
float k = i * j;
float l = g * j;
poseStack.pushPose();
poseStack.translate(leashState.offset);
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.leash());
Matrix4f matrix4f = poseStack.last().pose();
for (int m = 0; m <= 24; m++) {
addVertexPair(
vertexConsumer,
matrix4f,
g,
h,
i,
leashState.startBlockLight,
leashState.endBlockLight,
leashState.startSkyLight,
leashState.endSkyLight,
0.025F,
0.025F,
k,
l,
m,
false
);
}
for (int m = 24; m >= 0; m--) {
addVertexPair(
vertexConsumer,
matrix4f,
g,
h,
i,
leashState.startBlockLight,
leashState.endBlockLight,
leashState.startSkyLight,
leashState.endSkyLight,
0.025F,
0.0F,
k,
l,
m,
true
);
}
poseStack.popPose();
}
private static void addVertexPair(
VertexConsumer buffer,
Matrix4f pose,
float startX,
float startY,
float startZ,
int entityBlockLight,
int holderBlockLight,
int entitySkyLight,
int holderSkyLight,
float yOffset,
float dy,
float dx,
float dz,
int index,
boolean reverse
) {
float f = index / 24.0F;
int i = (int)Mth.lerp(f, (float)entityBlockLight, (float)holderBlockLight);
int j = (int)Mth.lerp(f, (float)entitySkyLight, (float)holderSkyLight);
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 = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f);
float p = startZ * f;
buffer.addVertex(pose, n - dx, o + dy, p + dz).setColor(h, l, m, 1.0F).setLight(k);
buffer.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 ? DisplayMode.SEE_THROUGH : DisplayMode.NORMAL, j, packedLight);
if (bl) {
font.drawInBatch(displayName, f, (float)i, -1, false, matrix4f, bufferSource, 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;
}
reusedState.isDiscrete = entity.isDiscrete();
Entity entity2 = entity instanceof Leashable leashable ? leashable.getLeashHolder() : null;
if (entity2 != null) {
float g = entity.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
Vec3 vec3 = entity.getLeashOffset(partialTick).yRot(-g);
BlockPos blockPos = BlockPos.containing(entity.getEyePosition(partialTick));
BlockPos blockPos2 = BlockPos.containing(entity2.getEyePosition(partialTick));
if (reusedState.leashState == null) {
reusedState.leashState = new EntityRenderState.LeashState();
}
EntityRenderState.LeashState leashState = reusedState.leashState;
leashState.offset = vec3;
leashState.start = entity.getPosition(partialTick).add(vec3);
leashState.end = entity2.getRopeHoldPosition(partialTick);
leashState.startBlockLight = this.getBlockLightLevel(entity, blockPos);
leashState.endBlockLight = this.entityRenderDispatcher.getRenderer(entity2).getBlockLightLevel(entity2, blockPos2);
leashState.startSkyLight = entity.level().getBrightness(LightLayer.SKY, blockPos);
leashState.endSkyLight = entity.level().getBrightness(LightLayer.SKY, blockPos2);
} else {
reusedState.leashState = 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;
}
}