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

322 lines
12 KiB
Java

package net.minecraft.client.renderer.entity;
import com.google.common.collect.Lists;
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.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.client.renderer.entity.state.LivingEntityRenderState;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.PlayerModelPart;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.scores.Team;
import net.minecraft.world.scores.Team.Visibility;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public abstract class LivingEntityRenderer<T extends LivingEntity, S extends LivingEntityRenderState, M extends EntityModel<? super S>>
extends EntityRenderer<T, S>
implements RenderLayerParent<S, M> {
private static final float EYE_BED_OFFSET = 0.1F;
protected M model;
protected final ItemRenderer itemRenderer;
protected final List<RenderLayer<S, M>> layers = Lists.<RenderLayer<S, M>>newArrayList();
public LivingEntityRenderer(EntityRendererProvider.Context context, M model, float shadowRadius) {
super(context);
this.itemRenderer = context.getItemRenderer();
this.model = model;
this.shadowRadius = shadowRadius;
}
protected final boolean addLayer(RenderLayer<S, M> layer) {
return this.layers.add(layer);
}
@Override
public M getModel() {
return this.model;
}
protected AABB getBoundingBoxForCulling(T minecraft) {
AABB aABB = super.getBoundingBoxForCulling(minecraft);
if (minecraft.getItemBySlot(EquipmentSlot.HEAD).is(Items.DRAGON_HEAD)) {
float f = 0.5F;
return aABB.inflate(0.5, 0.5, 0.5);
} else {
return aABB;
}
}
public void render(S renderState, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
poseStack.pushPose();
if (renderState.hasPose(Pose.SLEEPING)) {
Direction direction = renderState.bedOrientation;
if (direction != null) {
float f = renderState.eyeHeight - 0.1F;
poseStack.translate(-direction.getStepX() * f, 0.0F, -direction.getStepZ() * f);
}
}
float g = renderState.scale;
poseStack.scale(g, g, g);
this.setupRotations(renderState, poseStack, renderState.bodyRot, g);
poseStack.scale(-1.0F, -1.0F, 1.0F);
this.scale(renderState, poseStack);
poseStack.translate(0.0F, -1.501F, 0.0F);
this.model.setupAnim(renderState);
boolean bl = this.isBodyVisible(renderState);
boolean bl2 = !bl && !renderState.isInvisibleToPlayer;
RenderType renderType = this.getRenderType(renderState, bl, bl2, renderState.appearsGlowing);
if (renderType != null) {
VertexConsumer vertexConsumer = bufferSource.getBuffer(renderType);
int i = getOverlayCoords(renderState, this.getWhiteOverlayProgress(renderState));
int j = bl2 ? 654311423 : -1;
int k = ARGB.multiply(j, this.getModelTint(renderState));
this.model.renderToBuffer(poseStack, vertexConsumer, packedLight, i, k);
}
if (this.shouldRenderLayers(renderState)) {
for (RenderLayer<S, M> renderLayer : this.layers) {
renderLayer.render(poseStack, bufferSource, packedLight, renderState, renderState.yRot, renderState.xRot);
}
}
poseStack.popPose();
super.render(renderState, poseStack, bufferSource, packedLight);
}
protected boolean shouldRenderLayers(S renderState) {
return true;
}
protected int getModelTint(S renderState) {
return -1;
}
public abstract ResourceLocation getTextureLocation(S renderState);
@Nullable
protected RenderType getRenderType(S renderState, boolean isVisible, boolean renderTranslucent, boolean appearsGlowing) {
ResourceLocation resourceLocation = this.getTextureLocation(renderState);
if (renderTranslucent) {
return RenderType.itemEntityTranslucentCull(resourceLocation);
} else if (isVisible) {
return this.model.renderType(resourceLocation);
} else {
return appearsGlowing ? RenderType.outline(resourceLocation) : null;
}
}
public static int getOverlayCoords(LivingEntityRenderState renderState, float overlay) {
return OverlayTexture.pack(OverlayTexture.u(overlay), OverlayTexture.v(renderState.hasRedOverlay));
}
protected boolean isBodyVisible(S renderState) {
return !renderState.isInvisible;
}
private static float sleepDirectionToRotation(Direction facing) {
switch (facing) {
case SOUTH:
return 90.0F;
case WEST:
return 0.0F;
case NORTH:
return 270.0F;
case EAST:
return 180.0F;
default:
return 0.0F;
}
}
protected boolean isShaking(S renderState) {
return renderState.isFullyFrozen;
}
protected void setupRotations(S renderState, PoseStack poseStack, float bodyRot, float scale) {
if (this.isShaking(renderState)) {
bodyRot += (float)(Math.cos(Mth.floor(renderState.ageInTicks) * 3.25F) * Math.PI * 0.4F);
}
if (!renderState.hasPose(Pose.SLEEPING)) {
poseStack.mulPose(Axis.YP.rotationDegrees(180.0F - bodyRot));
}
if (renderState.deathTime > 0.0F) {
float f = (renderState.deathTime - 1.0F) / 20.0F * 1.6F;
f = Mth.sqrt(f);
if (f > 1.0F) {
f = 1.0F;
}
poseStack.mulPose(Axis.ZP.rotationDegrees(f * this.getFlipDegrees()));
} else if (renderState.isAutoSpinAttack) {
poseStack.mulPose(Axis.XP.rotationDegrees(-90.0F - renderState.xRot));
poseStack.mulPose(Axis.YP.rotationDegrees(renderState.ageInTicks * -75.0F));
} else if (renderState.hasPose(Pose.SLEEPING)) {
Direction direction = renderState.bedOrientation;
float g = direction != null ? sleepDirectionToRotation(direction) : bodyRot;
poseStack.mulPose(Axis.YP.rotationDegrees(g));
poseStack.mulPose(Axis.ZP.rotationDegrees(this.getFlipDegrees()));
poseStack.mulPose(Axis.YP.rotationDegrees(270.0F));
} else if (renderState.isUpsideDown) {
poseStack.translate(0.0F, (renderState.boundingBoxHeight + 0.1F) / scale, 0.0F);
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
}
}
protected float getFlipDegrees() {
return 90.0F;
}
protected float getWhiteOverlayProgress(S renderState) {
return 0.0F;
}
protected void scale(S renderState, PoseStack poseStack) {
}
protected boolean shouldShowName(T entity, double distanceToCameraSq) {
if (entity.isDiscrete()) {
float f = 32.0F;
if (distanceToCameraSq >= 1024.0) {
return false;
}
}
Minecraft minecraft = Minecraft.getInstance();
LocalPlayer localPlayer = minecraft.player;
boolean bl = !entity.isInvisibleTo(localPlayer);
if (entity != localPlayer) {
Team team = entity.getTeam();
Team team2 = localPlayer.getTeam();
if (team != null) {
Visibility visibility = team.getNameTagVisibility();
switch (visibility) {
case ALWAYS:
return bl;
case NEVER:
return false;
case HIDE_FOR_OTHER_TEAMS:
return team2 == null ? bl : team.isAlliedTo(team2) && (team.canSeeFriendlyInvisibles() || bl);
case HIDE_FOR_OWN_TEAM:
return team2 == null ? bl : !team.isAlliedTo(team2) && bl;
default:
return true;
}
}
}
return Minecraft.renderNames() && entity != minecraft.getCameraEntity() && bl && !entity.isVehicle();
}
public static boolean isEntityUpsideDown(LivingEntity entity) {
if (entity instanceof Player || entity.hasCustomName()) {
String string = ChatFormatting.stripFormatting(entity.getName().getString());
if ("Dinnerbone".equals(string) || "Grumm".equals(string)) {
return !(entity instanceof Player) || ((Player)entity).isModelPartShown(PlayerModelPart.CAPE);
}
}
return false;
}
protected float getShadowRadius(S renderState) {
return super.getShadowRadius(renderState) * renderState.scale;
}
public void extractRenderState(T entity, S reusedState, float partialTick) {
super.extractRenderState(entity, reusedState, partialTick);
float f = Mth.rotLerp(partialTick, entity.yHeadRotO, entity.yHeadRot);
reusedState.bodyRot = solveBodyRot(entity, f, partialTick);
reusedState.yRot = Mth.wrapDegrees(f - reusedState.bodyRot);
reusedState.xRot = entity.getXRot(partialTick);
reusedState.customName = entity.getCustomName();
reusedState.isUpsideDown = isEntityUpsideDown(entity);
if (reusedState.isUpsideDown) {
reusedState.xRot *= -1.0F;
reusedState.yRot *= -1.0F;
}
if (!entity.isPassenger() && entity.isAlive()) {
reusedState.walkAnimationPos = entity.walkAnimation.position(partialTick);
reusedState.walkAnimationSpeed = entity.walkAnimation.speed(partialTick);
} else {
reusedState.walkAnimationPos = 0.0F;
reusedState.walkAnimationSpeed = 0.0F;
}
if (entity.getVehicle() instanceof LivingEntity livingEntity) {
reusedState.wornHeadAnimationPos = livingEntity.walkAnimation.position(partialTick);
} else {
reusedState.wornHeadAnimationPos = reusedState.walkAnimationPos;
}
reusedState.scale = entity.getScale();
reusedState.ageScale = entity.getAgeScale();
reusedState.pose = entity.getPose();
reusedState.bedOrientation = entity.getBedOrientation();
if (reusedState.bedOrientation != null) {
reusedState.eyeHeight = entity.getEyeHeight(Pose.STANDING);
}
reusedState.isFullyFrozen = entity.isFullyFrozen();
reusedState.isBaby = entity.isBaby();
reusedState.isInWater = entity.isInWater();
reusedState.isAutoSpinAttack = entity.isAutoSpinAttack();
reusedState.hasRedOverlay = entity.hurtTime > 0 || entity.deathTime > 0;
ItemStack itemStack = entity.getItemBySlot(EquipmentSlot.HEAD);
reusedState.headItem = itemStack.copy();
reusedState.headItemModel = this.itemRenderer.resolveItemModel(itemStack, entity, ItemDisplayContext.HEAD);
reusedState.mainArm = entity.getMainArm();
ItemStack itemStack2 = entity.getItemHeldByArm(HumanoidArm.RIGHT);
ItemStack itemStack3 = entity.getItemHeldByArm(HumanoidArm.LEFT);
reusedState.rightHandItem = itemStack2.copy();
reusedState.leftHandItem = itemStack3.copy();
reusedState.rightHandItemModel = this.itemRenderer.resolveItemModel(itemStack2, entity, ItemDisplayContext.THIRD_PERSON_RIGHT_HAND);
reusedState.leftHandItemModel = this.itemRenderer.resolveItemModel(itemStack3, entity, ItemDisplayContext.THIRD_PERSON_LEFT_HAND);
reusedState.deathTime = entity.deathTime > 0 ? entity.deathTime + partialTick : 0.0F;
Minecraft minecraft = Minecraft.getInstance();
reusedState.isInvisibleToPlayer = reusedState.isInvisible && entity.isInvisibleTo(minecraft.player);
reusedState.appearsGlowing = minecraft.shouldEntityAppearGlowing(entity);
}
private static float solveBodyRot(LivingEntity entity, float yHeadRot, float partialTick) {
if (entity.getVehicle() instanceof LivingEntity livingEntity) {
float f = Mth.rotLerp(partialTick, livingEntity.yBodyRotO, livingEntity.yBodyRot);
float g = 85.0F;
float h = Mth.clamp(Mth.wrapDegrees(yHeadRot - f), -85.0F, 85.0F);
f = yHeadRot - h;
if (Math.abs(h) > 50.0F) {
f += h * 0.2F;
}
return f;
} else {
return Mth.rotLerp(partialTick, entity.yBodyRotO, entity.yBodyRot);
}
}
}