407 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.client.renderer.entity;
 | |
| 
 | |
| import com.google.common.collect.ImmutableList.Builder;
 | |
| import com.mojang.blaze3d.vertex.PoseStack;
 | |
| import com.mojang.blaze3d.vertex.VertexConsumer;
 | |
| import java.util.ArrayList;
 | |
| import net.fabricmc.api.EnvType;
 | |
| import net.fabricmc.api.Environment;
 | |
| import net.minecraft.client.Minecraft;
 | |
| import net.minecraft.client.gui.Font;
 | |
| import net.minecraft.client.renderer.LightTexture;
 | |
| import net.minecraft.client.renderer.MultiBufferSource;
 | |
| import net.minecraft.client.renderer.RenderType;
 | |
| import net.minecraft.client.renderer.culling.Frustum;
 | |
| import net.minecraft.client.renderer.entity.state.EntityRenderState;
 | |
| import net.minecraft.client.renderer.entity.state.HitboxRenderState;
 | |
| import net.minecraft.client.renderer.entity.state.HitboxesRenderState;
 | |
| import net.minecraft.client.server.IntegratedServer;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.network.chat.Component;
 | |
| import net.minecraft.server.level.ServerLevel;
 | |
| import net.minecraft.util.Mth;
 | |
| import net.minecraft.world.entity.Entity;
 | |
| import net.minecraft.world.entity.EntityAttachment;
 | |
| import net.minecraft.world.entity.Leashable;
 | |
| import net.minecraft.world.entity.vehicle.AbstractMinecart;
 | |
| import net.minecraft.world.entity.vehicle.NewMinecartBehavior;
 | |
| import net.minecraft.world.level.LightLayer;
 | |
| import net.minecraft.world.phys.AABB;
 | |
| import net.minecraft.world.phys.Vec3;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| import org.joml.Matrix4f;
 | |
| 
 | |
| @Environment(EnvType.CLIENT)
 | |
| public abstract class EntityRenderer<T extends Entity, S extends EntityRenderState> {
 | |
| 	protected static final float NAMETAG_SCALE = 0.025F;
 | |
| 	public static final int LEASH_RENDER_STEPS = 24;
 | |
| 	public static final float LEASH_WIDTH = 0.05F;
 | |
| 	protected final EntityRenderDispatcher entityRenderDispatcher;
 | |
| 	private final Font font;
 | |
| 	protected float shadowRadius;
 | |
| 	protected float shadowStrength = 1.0F;
 | |
| 	private final S reusedState = this.createRenderState();
 | |
| 
 | |
| 	protected EntityRenderer(EntityRendererProvider.Context context) {
 | |
| 		this.entityRenderDispatcher = context.getEntityRenderDispatcher();
 | |
| 		this.font = context.getFont();
 | |
| 	}
 | |
| 
 | |
| 	public final int getPackedLightCoords(T entity, float partialTicks) {
 | |
| 		BlockPos blockPos = BlockPos.containing(entity.getLightProbePosition(partialTicks));
 | |
| 		return LightTexture.pack(this.getBlockLightLevel(entity, blockPos), this.getSkyLightLevel(entity, blockPos));
 | |
| 	}
 | |
| 
 | |
| 	protected int getSkyLightLevel(T entity, BlockPos pos) {
 | |
| 		return entity.level().getBrightness(LightLayer.SKY, pos);
 | |
| 	}
 | |
| 
 | |
| 	protected int getBlockLightLevel(T entity, BlockPos pos) {
 | |
| 		return entity.isOnFire() ? 15 : entity.level().getBrightness(LightLayer.BLOCK, pos);
 | |
| 	}
 | |
| 
 | |
| 	public boolean shouldRender(T livingEntity, Frustum camera, double camX, double camY, double camZ) {
 | |
| 		if (!livingEntity.shouldRender(camX, camY, camZ)) {
 | |
| 			return false;
 | |
| 		} else if (!this.affectedByCulling(livingEntity)) {
 | |
| 			return true;
 | |
| 		} else {
 | |
| 			AABB aABB = this.getBoundingBoxForCulling(livingEntity).inflate(0.5);
 | |
| 			if (aABB.hasNaN() || aABB.getSize() == 0.0) {
 | |
| 				aABB = new AABB(
 | |
| 					livingEntity.getX() - 2.0,
 | |
| 					livingEntity.getY() - 2.0,
 | |
| 					livingEntity.getZ() - 2.0,
 | |
| 					livingEntity.getX() + 2.0,
 | |
| 					livingEntity.getY() + 2.0,
 | |
| 					livingEntity.getZ() + 2.0
 | |
| 				);
 | |
| 			}
 | |
| 
 | |
| 			if (camera.isVisible(aABB)) {
 | |
| 				return true;
 | |
| 			} else {
 | |
| 				if (livingEntity instanceof Leashable leashable) {
 | |
| 					Entity entity = leashable.getLeashHolder();
 | |
| 					if (entity != null) {
 | |
| 						AABB aABB2 = this.entityRenderDispatcher.getRenderer(entity).getBoundingBoxForCulling(entity);
 | |
| 						return camera.isVisible(aABB2) || camera.isVisible(aABB.minmax(aABB2));
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return false;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected AABB getBoundingBoxForCulling(T minecraft) {
 | |
| 		return minecraft.getBoundingBox();
 | |
| 	}
 | |
| 
 | |
| 	protected boolean affectedByCulling(T display) {
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	public Vec3 getRenderOffset(S renderState) {
 | |
| 		return renderState.passengerOffset != null ? renderState.passengerOffset : Vec3.ZERO;
 | |
| 	}
 | |
| 
 | |
| 	public void render(S renderState, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
 | |
| 		if (renderState.leashStates != null) {
 | |
| 			for (EntityRenderState.LeashState leashState : renderState.leashStates) {
 | |
| 				renderLeash(poseStack, bufferSource, leashState);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (renderState.nameTag != null) {
 | |
| 			this.renderNameTag(renderState, renderState.nameTag, poseStack, bufferSource, packedLight);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private static void renderLeash(PoseStack poseStack, MultiBufferSource buffer, EntityRenderState.LeashState leashState) {
 | |
| 		float f = (float)(leashState.end.x - leashState.start.x);
 | |
| 		float g = (float)(leashState.end.y - leashState.start.y);
 | |
| 		float h = (float)(leashState.end.z - leashState.start.z);
 | |
| 		float i = Mth.invSqrt(f * f + h * h) * 0.05F / 2.0F;
 | |
| 		float j = h * i;
 | |
| 		float k = f * i;
 | |
| 		poseStack.pushPose();
 | |
| 		poseStack.translate(leashState.offset);
 | |
| 		VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.leash());
 | |
| 		Matrix4f matrix4f = poseStack.last().pose();
 | |
| 
 | |
| 		for (int l = 0; l <= 24; l++) {
 | |
| 			addVertexPair(vertexConsumer, matrix4f, f, g, h, 0.05F, 0.05F, j, k, l, false, leashState);
 | |
| 		}
 | |
| 
 | |
| 		for (int l = 24; l >= 0; l--) {
 | |
| 			addVertexPair(vertexConsumer, matrix4f, f, g, h, 0.05F, 0.0F, j, k, l, true, leashState);
 | |
| 		}
 | |
| 
 | |
| 		poseStack.popPose();
 | |
| 	}
 | |
| 
 | |
| 	private static void addVertexPair(
 | |
| 		VertexConsumer consumer,
 | |
| 		Matrix4f pose,
 | |
| 		float startX,
 | |
| 		float startY,
 | |
| 		float startZ,
 | |
| 		float yOffset,
 | |
| 		float dy,
 | |
| 		float dx,
 | |
| 		float dz,
 | |
| 		int index,
 | |
| 		boolean reverse,
 | |
| 		EntityRenderState.LeashState leashState
 | |
| 	) {
 | |
| 		float f = index / 24.0F;
 | |
| 		int i = (int)Mth.lerp(f, (float)leashState.startBlockLight, (float)leashState.endBlockLight);
 | |
| 		int j = (int)Mth.lerp(f, (float)leashState.startSkyLight, (float)leashState.endSkyLight);
 | |
| 		int k = LightTexture.pack(i, j);
 | |
| 		float g = index % 2 == (reverse ? 1 : 0) ? 0.7F : 1.0F;
 | |
| 		float h = 0.5F * g;
 | |
| 		float l = 0.4F * g;
 | |
| 		float m = 0.3F * g;
 | |
| 		float n = startX * f;
 | |
| 		float o;
 | |
| 		if (leashState.slack) {
 | |
| 			o = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f);
 | |
| 		} else {
 | |
| 			o = startY * f;
 | |
| 		}
 | |
| 
 | |
| 		float p = startZ * f;
 | |
| 		consumer.addVertex(pose, n - dx, o + dy, p + dz).setColor(h, l, m, 1.0F).setLight(k);
 | |
| 		consumer.addVertex(pose, n + dx, o + yOffset - dy, p - dz).setColor(h, l, m, 1.0F).setLight(k);
 | |
| 	}
 | |
| 
 | |
| 	protected boolean shouldShowName(T entity, double distanceToCameraSq) {
 | |
| 		return entity.shouldShowName() || entity.hasCustomName() && entity == this.entityRenderDispatcher.crosshairPickEntity;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the font renderer from the set render manager
 | |
| 	 */
 | |
| 	public Font getFont() {
 | |
| 		return this.font;
 | |
| 	}
 | |
| 
 | |
| 	protected void renderNameTag(S renderState, Component displayName, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
 | |
| 		Vec3 vec3 = renderState.nameTagAttachment;
 | |
| 		if (vec3 != null) {
 | |
| 			boolean bl = !renderState.isDiscrete;
 | |
| 			int i = "deadmau5".equals(displayName.getString()) ? -10 : 0;
 | |
| 			poseStack.pushPose();
 | |
| 			poseStack.translate(vec3.x, vec3.y + 0.5, vec3.z);
 | |
| 			poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
 | |
| 			poseStack.scale(0.025F, -0.025F, 0.025F);
 | |
| 			Matrix4f matrix4f = poseStack.last().pose();
 | |
| 			Font font = this.getFont();
 | |
| 			float f = -font.width(displayName) / 2.0F;
 | |
| 			int j = (int)(Minecraft.getInstance().options.getBackgroundOpacity(0.25F) * 255.0F) << 24;
 | |
| 			font.drawInBatch(
 | |
| 				displayName, f, (float)i, -2130706433, false, matrix4f, bufferSource, bl ? Font.DisplayMode.SEE_THROUGH : Font.DisplayMode.NORMAL, j, packedLight
 | |
| 			);
 | |
| 			if (bl) {
 | |
| 				font.drawInBatch(
 | |
| 					displayName, f, (float)i, -1, false, matrix4f, bufferSource, Font.DisplayMode.NORMAL, 0, LightTexture.lightCoordsWithEmission(packedLight, 2)
 | |
| 				);
 | |
| 			}
 | |
| 
 | |
| 			poseStack.popPose();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	protected Component getNameTag(T entity) {
 | |
| 		return entity.getDisplayName();
 | |
| 	}
 | |
| 
 | |
| 	protected float getShadowRadius(S renderState) {
 | |
| 		return this.shadowRadius;
 | |
| 	}
 | |
| 
 | |
| 	protected float getShadowStrength(S renderState) {
 | |
| 		return this.shadowStrength;
 | |
| 	}
 | |
| 
 | |
| 	public abstract S createRenderState();
 | |
| 
 | |
| 	public final S createRenderState(T entity, float partialTick) {
 | |
| 		S entityRenderState = this.reusedState;
 | |
| 		this.extractRenderState(entity, entityRenderState, partialTick);
 | |
| 		return entityRenderState;
 | |
| 	}
 | |
| 
 | |
| 	public void extractRenderState(T entity, S reusedState, float partialTick) {
 | |
| 		reusedState.entityType = entity.getType();
 | |
| 		reusedState.x = Mth.lerp((double)partialTick, entity.xOld, entity.getX());
 | |
| 		reusedState.y = Mth.lerp((double)partialTick, entity.yOld, entity.getY());
 | |
| 		reusedState.z = Mth.lerp((double)partialTick, entity.zOld, entity.getZ());
 | |
| 		reusedState.isInvisible = entity.isInvisible();
 | |
| 		reusedState.ageInTicks = entity.tickCount + partialTick;
 | |
| 		reusedState.boundingBoxWidth = entity.getBbWidth();
 | |
| 		reusedState.boundingBoxHeight = entity.getBbHeight();
 | |
| 		reusedState.eyeHeight = entity.getEyeHeight();
 | |
| 		if (entity.isPassenger()
 | |
| 			&& entity.getVehicle() instanceof AbstractMinecart abstractMinecart
 | |
| 			&& abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior
 | |
| 			&& newMinecartBehavior.cartHasPosRotLerp()) {
 | |
| 			double d = Mth.lerp((double)partialTick, abstractMinecart.xOld, abstractMinecart.getX());
 | |
| 			double e = Mth.lerp((double)partialTick, abstractMinecart.yOld, abstractMinecart.getY());
 | |
| 			double f = Mth.lerp((double)partialTick, abstractMinecart.zOld, abstractMinecart.getZ());
 | |
| 			reusedState.passengerOffset = newMinecartBehavior.getCartLerpPosition(partialTick).subtract(new Vec3(d, e, f));
 | |
| 		} else {
 | |
| 			reusedState.passengerOffset = null;
 | |
| 		}
 | |
| 
 | |
| 		reusedState.distanceToCameraSq = this.entityRenderDispatcher.distanceToSqr(entity);
 | |
| 		boolean bl = reusedState.distanceToCameraSq < 4096.0 && this.shouldShowName(entity, reusedState.distanceToCameraSq);
 | |
| 		if (bl) {
 | |
| 			reusedState.nameTag = this.getNameTag(entity);
 | |
| 			reusedState.nameTagAttachment = entity.getAttachments().getNullable(EntityAttachment.NAME_TAG, 0, entity.getYRot(partialTick));
 | |
| 		} else {
 | |
| 			reusedState.nameTag = null;
 | |
| 		}
 | |
| 
 | |
| 		label77: {
 | |
| 			reusedState.isDiscrete = entity.isDiscrete();
 | |
| 			if (entity instanceof Leashable leashable) {
 | |
| 				Entity g = leashable.getLeashHolder();
 | |
| 				if (g instanceof Entity) {
 | |
| 					float gx = entity.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
 | |
| 					Vec3 vec3 = leashable.getLeashOffset(partialTick);
 | |
| 					BlockPos blockPos = BlockPos.containing(entity.getEyePosition(partialTick));
 | |
| 					BlockPos blockPos2 = BlockPos.containing(g.getEyePosition(partialTick));
 | |
| 					int i = this.getBlockLightLevel(entity, blockPos);
 | |
| 					int j = this.entityRenderDispatcher.getRenderer(g).getBlockLightLevel(g, blockPos2);
 | |
| 					int k = entity.level().getBrightness(LightLayer.SKY, blockPos);
 | |
| 					int l = entity.level().getBrightness(LightLayer.SKY, blockPos2);
 | |
| 					boolean bl2 = g.supportQuadLeashAsHolder() && leashable.supportQuadLeash();
 | |
| 					int m = bl2 ? 4 : 1;
 | |
| 					if (reusedState.leashStates == null || reusedState.leashStates.size() != m) {
 | |
| 						reusedState.leashStates = new ArrayList(m);
 | |
| 
 | |
| 						for (int n = 0; n < m; n++) {
 | |
| 							reusedState.leashStates.add(new EntityRenderState.LeashState());
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					if (bl2) {
 | |
| 						float h = g.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
 | |
| 						Vec3 vec32 = g.getPosition(partialTick);
 | |
| 						Vec3[] vec3s = leashable.getQuadLeashOffsets();
 | |
| 						Vec3[] vec3s2 = g.getQuadLeashHolderOffsets();
 | |
| 						int o = 0;
 | |
| 
 | |
| 						while (true) {
 | |
| 							if (o >= m) {
 | |
| 								break label77;
 | |
| 							}
 | |
| 
 | |
| 							EntityRenderState.LeashState leashState = (EntityRenderState.LeashState)reusedState.leashStates.get(o);
 | |
| 							leashState.offset = vec3s[o].yRot(-gx);
 | |
| 							leashState.start = entity.getPosition(partialTick).add(leashState.offset);
 | |
| 							leashState.end = vec32.add(vec3s2[o].yRot(-h));
 | |
| 							leashState.startBlockLight = i;
 | |
| 							leashState.endBlockLight = j;
 | |
| 							leashState.startSkyLight = k;
 | |
| 							leashState.endSkyLight = l;
 | |
| 							leashState.slack = false;
 | |
| 							o++;
 | |
| 						}
 | |
| 					} else {
 | |
| 						Vec3 vec33 = vec3.yRot(-gx);
 | |
| 						EntityRenderState.LeashState leashState2 = (EntityRenderState.LeashState)reusedState.leashStates.getFirst();
 | |
| 						leashState2.offset = vec33;
 | |
| 						leashState2.start = entity.getPosition(partialTick).add(vec33);
 | |
| 						leashState2.end = g.getRopeHoldPosition(partialTick);
 | |
| 						leashState2.startBlockLight = i;
 | |
| 						leashState2.endBlockLight = j;
 | |
| 						leashState2.startSkyLight = k;
 | |
| 						leashState2.endSkyLight = l;
 | |
| 						break label77;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			reusedState.leashStates = null;
 | |
| 		}
 | |
| 
 | |
| 		reusedState.displayFireAnimation = entity.displayFireAnimation();
 | |
| 		Minecraft minecraft = Minecraft.getInstance();
 | |
| 		if (minecraft.getEntityRenderDispatcher().shouldRenderHitBoxes() && !reusedState.isInvisible && !minecraft.showOnlyReducedInfo()) {
 | |
| 			this.extractHitboxes(entity, reusedState, partialTick);
 | |
| 		} else {
 | |
| 			reusedState.hitboxesRenderState = null;
 | |
| 			reusedState.serverHitboxesRenderState = null;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void extractHitboxes(T entity, S reusedState, float partialTick) {
 | |
| 		reusedState.hitboxesRenderState = this.extractHitboxes(entity, partialTick, false);
 | |
| 		reusedState.serverHitboxesRenderState = null;
 | |
| 	}
 | |
| 
 | |
| 	private HitboxesRenderState extractHitboxes(T entity, float partialTick, boolean green) {
 | |
| 		Builder<HitboxRenderState> builder = new Builder<>();
 | |
| 		AABB aABB = entity.getBoundingBox();
 | |
| 		HitboxRenderState hitboxRenderState;
 | |
| 		if (green) {
 | |
| 			hitboxRenderState = new HitboxRenderState(
 | |
| 				aABB.minX - entity.getX(),
 | |
| 				aABB.minY - entity.getY(),
 | |
| 				aABB.minZ - entity.getZ(),
 | |
| 				aABB.maxX - entity.getX(),
 | |
| 				aABB.maxY - entity.getY(),
 | |
| 				aABB.maxZ - entity.getZ(),
 | |
| 				0.0F,
 | |
| 				1.0F,
 | |
| 				0.0F
 | |
| 			);
 | |
| 		} else {
 | |
| 			hitboxRenderState = new HitboxRenderState(
 | |
| 				aABB.minX - entity.getX(),
 | |
| 				aABB.minY - entity.getY(),
 | |
| 				aABB.minZ - entity.getZ(),
 | |
| 				aABB.maxX - entity.getX(),
 | |
| 				aABB.maxY - entity.getY(),
 | |
| 				aABB.maxZ - entity.getZ(),
 | |
| 				1.0F,
 | |
| 				1.0F,
 | |
| 				1.0F
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		builder.add(hitboxRenderState);
 | |
| 		Entity entity2 = entity.getVehicle();
 | |
| 		if (entity2 != null) {
 | |
| 			float f = Math.min(entity2.getBbWidth(), entity.getBbWidth()) / 2.0F;
 | |
| 			float g = 0.0625F;
 | |
| 			Vec3 vec3 = entity2.getPassengerRidingPosition(entity).subtract(entity.position());
 | |
| 			HitboxRenderState hitboxRenderState2 = new HitboxRenderState(vec3.x - f, vec3.y, vec3.z - f, vec3.x + f, vec3.y + 0.0625, vec3.z + f, 1.0F, 1.0F, 0.0F);
 | |
| 			builder.add(hitboxRenderState2);
 | |
| 		}
 | |
| 
 | |
| 		this.extractAdditionalHitboxes(entity, builder, partialTick);
 | |
| 		Vec3 vec32 = entity.getViewVector(partialTick);
 | |
| 		return new HitboxesRenderState(vec32.x, vec32.y, vec32.z, builder.build());
 | |
| 	}
 | |
| 
 | |
| 	protected void extractAdditionalHitboxes(T entity, Builder<HitboxRenderState> hitboxes, float partialTick) {
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	private static Entity getServerSideEntity(Entity entity) {
 | |
| 		IntegratedServer integratedServer = Minecraft.getInstance().getSingleplayerServer();
 | |
| 		if (integratedServer != null) {
 | |
| 			ServerLevel serverLevel = integratedServer.getLevel(entity.level().dimension());
 | |
| 			if (serverLevel != null) {
 | |
| 				return serverLevel.getEntity(entity.getId());
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return null;
 | |
| 	}
 | |
| }
 |