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> extends EntityRenderer implements RenderLayerParent { private static final float EYE_BED_OFFSET = 0.1F; protected M model; protected final ItemRenderer itemRenderer; protected final List> layers = Lists.>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 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 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); } } }