349 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			349 lines
		
	
	
	
		
			12 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.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(EntityRendererProvider.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 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) {
 | |
| 				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() && 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 && !player.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);
 | |
| 		if (itemStack.getItem() instanceof BlockItem blockItem && blockItem.getBlock() instanceof AbstractSkullBlock abstractSkullBlock) {
 | |
| 			reusedState.wornHeadType = abstractSkullBlock.getType();
 | |
| 			reusedState.wornHeadProfile = itemStack.get(DataComponents.PROFILE);
 | |
| 			reusedState.headItem.clear();
 | |
| 		} else {
 | |
| 			reusedState.wornHeadType = null;
 | |
| 			reusedState.wornHeadProfile = null;
 | |
| 			if (!HumanoidArmorLayer.shouldRender(itemStack, EquipmentSlot.HEAD)) {
 | |
| 				this.itemModelResolver.updateForLiving(reusedState.headItem, itemStack, ItemDisplayContext.HEAD, entity);
 | |
| 			} else {
 | |
| 				reusedState.headItem.clear();
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		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);
 | |
| 	}
 | |
| 
 | |
| 	protected void extractAdditionalHitboxes(T entity, Builder<HitboxRenderState> hitboxes, float partialTick) {
 | |
| 		AABB aABB = entity.getBoundingBox();
 | |
| 		float f = 0.01F;
 | |
| 		HitboxRenderState hitboxRenderState = new HitboxRenderState(
 | |
| 			aABB.minX - entity.getX(),
 | |
| 			entity.getEyeHeight() - 0.01F,
 | |
| 			aABB.minZ - entity.getZ(),
 | |
| 			aABB.maxX - entity.getX(),
 | |
| 			entity.getEyeHeight() + 0.01F,
 | |
| 			aABB.maxZ - entity.getZ(),
 | |
| 			1.0F,
 | |
| 			0.0F,
 | |
| 			0.0F
 | |
| 		);
 | |
| 		hitboxes.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);
 | |
| 		}
 | |
| 	}
 | |
| }
 |