350 lines
13 KiB
Java
350 lines
13 KiB
Java
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<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 ItemModelResolver itemModelResolver;
|
|
protected final List<RenderLayer<S, M>> layers = Lists.<RenderLayer<S, M>>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<S, M> 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<S, M> 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<HitboxRenderState> 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);
|
|
}
|
|
}
|
|
}
|