package net.minecraft.client.renderer.entity; import com.google.common.collect.Lists; import com.google.common.collect.ImmutableList.Builder; 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.EntityRendererProvider.Context; import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; import net.minecraft.client.renderer.entity.layers.RenderLayer; import net.minecraft.client.renderer.entity.state.HitboxRenderState; import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; import net.minecraft.client.renderer.item.ItemModelResolver; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.core.Direction; import net.minecraft.core.component.DataComponents; 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.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.BlockItem; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.AbstractSkullBlock; import net.minecraft.world.phys.AABB; import net.minecraft.world.scores.Team; 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 ItemModelResolver itemModelResolver; protected final List> layers = Lists.>newArrayList(); public LivingEntityRenderer(Context context, M model, float shadowRadius) { super(context); this.itemModelResolver = context.getItemModelResolver(); 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 livingEntity) { AABB aABB = super.getBoundingBoxForCulling(livingEntity); if (livingEntity.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 livingEntityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { poseStack.pushPose(); if (livingEntityRenderState.hasPose(Pose.SLEEPING)) { Direction direction = livingEntityRenderState.bedOrientation; if (direction != null) { float f = livingEntityRenderState.eyeHeight - 0.1F; poseStack.translate(-direction.getStepX() * f, 0.0F, -direction.getStepZ() * f); } } float g = livingEntityRenderState.scale; poseStack.scale(g, g, g); this.setupRotations(livingEntityRenderState, poseStack, livingEntityRenderState.bodyRot, g); poseStack.scale(-1.0F, -1.0F, 1.0F); this.scale(livingEntityRenderState, poseStack); poseStack.translate(0.0F, -1.501F, 0.0F); this.model.setupAnim(livingEntityRenderState); boolean bl = this.isBodyVisible(livingEntityRenderState); boolean bl2 = !bl && !livingEntityRenderState.isInvisibleToPlayer; RenderType renderType = this.getRenderType(livingEntityRenderState, bl, bl2, livingEntityRenderState.appearsGlowing); if (renderType != null) { VertexConsumer vertexConsumer = multiBufferSource.getBuffer(renderType); int j = getOverlayCoords(livingEntityRenderState, this.getWhiteOverlayProgress(livingEntityRenderState)); int k = bl2 ? 654311423 : -1; int l = ARGB.multiply(k, this.getModelTint(livingEntityRenderState)); this.model.renderToBuffer(poseStack, vertexConsumer, i, j, l); } if (this.shouldRenderLayers(livingEntityRenderState)) { for (RenderLayer renderLayer : this.layers) { renderLayer.render(poseStack, multiBufferSource, i, livingEntityRenderState, livingEntityRenderState.yRot, livingEntityRenderState.xRot); } } poseStack.popPose(); super.render(livingEntityRenderState, poseStack, multiBufferSource, i); } 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 livingEntity, double d) { if (livingEntity.isDiscrete()) { float f = 32.0F; if (d >= 1024.0) { return false; } } Minecraft minecraft = Minecraft.getInstance(); LocalPlayer localPlayer = minecraft.player; boolean bl = !livingEntity.isInvisibleTo(localPlayer); if (livingEntity != localPlayer) { Team team = livingEntity.getTeam(); Team team2 = localPlayer.getTeam(); if (team != null) { Team.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() && livingEntity != minecraft.getCameraEntity() && bl && !livingEntity.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 && !player.isModelPartShown(PlayerModelPart.CAPE)); } } return false; } protected float getShadowRadius(S livingEntityRenderState) { return super.getShadowRadius(livingEntityRenderState) * livingEntityRenderState.scale; } public void extractRenderState(T livingEntity, S livingEntityRenderState, float f) { super.extractRenderState(livingEntity, livingEntityRenderState, f); float g = Mth.rotLerp(f, livingEntity.yHeadRotO, livingEntity.yHeadRot); livingEntityRenderState.bodyRot = solveBodyRot(livingEntity, g, f); livingEntityRenderState.yRot = Mth.wrapDegrees(g - livingEntityRenderState.bodyRot); livingEntityRenderState.xRot = livingEntity.getXRot(f); livingEntityRenderState.customName = livingEntity.getCustomName(); livingEntityRenderState.isUpsideDown = isEntityUpsideDown(livingEntity); if (livingEntityRenderState.isUpsideDown) { livingEntityRenderState.xRot *= -1.0F; livingEntityRenderState.yRot *= -1.0F; } if (!livingEntity.isPassenger() && livingEntity.isAlive()) { livingEntityRenderState.walkAnimationPos = livingEntity.walkAnimation.position(f); livingEntityRenderState.walkAnimationSpeed = livingEntity.walkAnimation.speed(f); } else { livingEntityRenderState.walkAnimationPos = 0.0F; livingEntityRenderState.walkAnimationSpeed = 0.0F; } if (livingEntity.getVehicle() instanceof LivingEntity livingEntity2) { livingEntityRenderState.wornHeadAnimationPos = livingEntity2.walkAnimation.position(f); } else { livingEntityRenderState.wornHeadAnimationPos = livingEntityRenderState.walkAnimationPos; } livingEntityRenderState.scale = livingEntity.getScale(); livingEntityRenderState.ageScale = livingEntity.getAgeScale(); livingEntityRenderState.pose = livingEntity.getPose(); livingEntityRenderState.bedOrientation = livingEntity.getBedOrientation(); if (livingEntityRenderState.bedOrientation != null) { livingEntityRenderState.eyeHeight = livingEntity.getEyeHeight(Pose.STANDING); } livingEntityRenderState.isFullyFrozen = livingEntity.isFullyFrozen(); livingEntityRenderState.isBaby = livingEntity.isBaby(); livingEntityRenderState.isInWater = livingEntity.isInWater(); livingEntityRenderState.isAutoSpinAttack = livingEntity.isAutoSpinAttack(); livingEntityRenderState.hasRedOverlay = livingEntity.hurtTime > 0 || livingEntity.deathTime > 0; ItemStack itemStack = livingEntity.getItemBySlot(EquipmentSlot.HEAD); if (itemStack.getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof AbstractSkullBlock abstractSkullBlock) { livingEntityRenderState.wornHeadType = abstractSkullBlock.getType(); livingEntityRenderState.wornHeadProfile = itemStack.get(DataComponents.PROFILE); livingEntityRenderState.headItem.clear(); } else { livingEntityRenderState.wornHeadType = null; livingEntityRenderState.wornHeadProfile = null; if (!HumanoidArmorLayer.shouldRender(itemStack, EquipmentSlot.HEAD)) { this.itemModelResolver.updateForLiving(livingEntityRenderState.headItem, itemStack, ItemDisplayContext.HEAD, livingEntity); } else { livingEntityRenderState.headItem.clear(); } } livingEntityRenderState.deathTime = livingEntity.deathTime > 0 ? livingEntity.deathTime + f : 0.0F; Minecraft minecraft = Minecraft.getInstance(); livingEntityRenderState.isInvisibleToPlayer = livingEntityRenderState.isInvisible && livingEntity.isInvisibleTo(minecraft.player); livingEntityRenderState.appearsGlowing = minecraft.shouldEntityAppearGlowing(livingEntity); } protected void extractAdditionalHitboxes(T livingEntity, Builder builder, float f) { AABB aABB = livingEntity.getBoundingBox(); float g = 0.01F; HitboxRenderState hitboxRenderState = new HitboxRenderState( aABB.minX - livingEntity.getX(), livingEntity.getEyeHeight() - 0.01F, aABB.minZ - livingEntity.getZ(), aABB.maxX - livingEntity.getX(), livingEntity.getEyeHeight() + 0.01F, aABB.maxZ - livingEntity.getZ(), 1.0F, 0.0F, 0.0F ); builder.add(hitboxRenderState); } 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); } } }