package net.minecraft.client.renderer.entity; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; 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.core.BlockPos; import net.minecraft.network.chat.Component; 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 { protected static final float NAMETAG_SCALE = 0.025F; public static final int LEASH_RENDER_STEPS = 24; 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) { return camera.isVisible(this.entityRenderDispatcher.getRenderer(entity).getBoundingBoxForCulling(entity)); } } return false; } } } protected AABB getBoundingBoxForCulling(T entity) { return entity.getBoundingBox(); } protected boolean affectedByCulling(T entity) { return true; } public Vec3 getRenderOffset(S entityRenderState) { return entityRenderState.passengerOffset != null ? entityRenderState.passengerOffset : Vec3.ZERO; } public void render(S entityRenderState, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { EntityRenderState.LeashState leashState = entityRenderState.leashState; if (leashState != null) { renderLeash(poseStack, multiBufferSource, leashState); } if (entityRenderState.nameTag != null) { this.renderNameTag(entityRenderState, entityRenderState.nameTag, poseStack, multiBufferSource, i); } } private static void renderLeash(PoseStack poseStack, MultiBufferSource multiBufferSource, EntityRenderState.LeashState leashState) { float f = 0.025F; float g = (float)(leashState.end.x - leashState.start.x); float h = (float)(leashState.end.y - leashState.start.y); float i = (float)(leashState.end.z - leashState.start.z); float j = Mth.invSqrt(g * g + i * i) * 0.025F / 2.0F; float k = i * j; float l = g * j; poseStack.pushPose(); poseStack.translate(leashState.offset); VertexConsumer vertexConsumer = multiBufferSource.getBuffer(RenderType.leash()); Matrix4f matrix4f = poseStack.last().pose(); for (int m = 0; m <= 24; m++) { addVertexPair( vertexConsumer, matrix4f, g, h, i, leashState.startBlockLight, leashState.endBlockLight, leashState.startSkyLight, leashState.endSkyLight, 0.025F, 0.025F, k, l, m, false ); } for (int m = 24; m >= 0; m--) { addVertexPair( vertexConsumer, matrix4f, g, h, i, leashState.startBlockLight, leashState.endBlockLight, leashState.startSkyLight, leashState.endSkyLight, 0.025F, 0.0F, k, l, m, true ); } poseStack.popPose(); } private static void addVertexPair( VertexConsumer buffer, Matrix4f pose, float startX, float startY, float startZ, int entityBlockLight, int holderBlockLight, int entitySkyLight, int holderSkyLight, float yOffset, float dy, float dx, float dz, int index, boolean reverse ) { float f = index / 24.0F; int i = (int)Mth.lerp(f, (float)entityBlockLight, (float)holderBlockLight); int j = (int)Mth.lerp(f, (float)entitySkyLight, (float)holderSkyLight); 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 = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f); float p = startZ * f; buffer.addVertex(pose, n - dx, o + dy, p + dz).setColor(h, l, m, 1.0F).setLight(k); buffer.addVertex(pose, n + dx, o + yOffset - dy, p - dz).setColor(h, l, m, 1.0F).setLight(k); } protected boolean shouldShowName(T entity, double d) { 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 entityRenderState, Component component, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { Vec3 vec3 = entityRenderState.nameTagAttachment; if (vec3 != null) { boolean bl = !entityRenderState.isDiscrete; int j = "deadmau5".equals(component.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(component) / 2.0F; int k = (int)(Minecraft.getInstance().options.getBackgroundOpacity(0.25F) * 255.0F) << 24; font.drawInBatch(component, f, (float)j, -2130706433, false, matrix4f, multiBufferSource, bl ? Font.DisplayMode.SEE_THROUGH : Font.DisplayMode.NORMAL, k, i); if (bl) { font.drawInBatch(component, f, (float)j, -1, false, matrix4f, multiBufferSource, Font.DisplayMode.NORMAL, 0, LightTexture.lightCoordsWithEmission(i, 2)); } poseStack.popPose(); } } @Nullable protected Component getNameTag(T entity) { return entity.getDisplayName(); } protected float getShadowRadius(S entityRenderState) { return this.shadowRadius; } public abstract S createRenderState(); public final S createRenderState(T entity, float f) { S entityRenderState = this.reusedState; this.extractRenderState(entity, entityRenderState, f); return entityRenderState; } public void extractRenderState(T entity, S entityRenderState, float f) { entityRenderState.x = Mth.lerp((double)f, entity.xOld, entity.getX()); entityRenderState.y = Mth.lerp((double)f, entity.yOld, entity.getY()); entityRenderState.z = Mth.lerp((double)f, entity.zOld, entity.getZ()); entityRenderState.isInvisible = entity.isInvisible(); entityRenderState.ageInTicks = entity.tickCount + f; entityRenderState.boundingBoxWidth = entity.getBbWidth(); entityRenderState.boundingBoxHeight = entity.getBbHeight(); entityRenderState.eyeHeight = entity.getEyeHeight(); if (entity.isPassenger() && entity.getVehicle() instanceof AbstractMinecart abstractMinecart && abstractMinecart.getBehavior() instanceof NewMinecartBehavior newMinecartBehavior && newMinecartBehavior.cartHasPosRotLerp()) { double d = Mth.lerp((double)f, abstractMinecart.xOld, abstractMinecart.getX()); double e = Mth.lerp((double)f, abstractMinecart.yOld, abstractMinecart.getY()); double g = Mth.lerp((double)f, abstractMinecart.zOld, abstractMinecart.getZ()); entityRenderState.passengerOffset = newMinecartBehavior.getCartLerpPosition(f).subtract(new Vec3(d, e, g)); } else { entityRenderState.passengerOffset = null; } entityRenderState.distanceToCameraSq = this.entityRenderDispatcher.distanceToSqr(entity); boolean bl = entityRenderState.distanceToCameraSq < 4096.0 && this.shouldShowName(entity, entityRenderState.distanceToCameraSq); if (bl) { entityRenderState.nameTag = this.getNameTag(entity); entityRenderState.nameTagAttachment = entity.getAttachments().getNullable(EntityAttachment.NAME_TAG, 0, entity.getYRot(f)); } else { entityRenderState.nameTag = null; } entityRenderState.isDiscrete = entity.isDiscrete(); Entity entity2 = entity instanceof Leashable leashable ? leashable.getLeashHolder() : null; if (entity2 != null) { float h = entity.getPreciseBodyRotation(f) * (float) (Math.PI / 180.0); Vec3 vec3 = entity.getLeashOffset(f).yRot(-h); BlockPos blockPos = BlockPos.containing(entity.getEyePosition(f)); BlockPos blockPos2 = BlockPos.containing(entity2.getEyePosition(f)); if (entityRenderState.leashState == null) { entityRenderState.leashState = new EntityRenderState.LeashState(); } EntityRenderState.LeashState leashState = entityRenderState.leashState; leashState.offset = vec3; leashState.start = entity.getPosition(f).add(vec3); leashState.end = entity2.getRopeHoldPosition(f); leashState.startBlockLight = this.getBlockLightLevel(entity, blockPos); leashState.endBlockLight = this.entityRenderDispatcher.getRenderer(entity2).getBlockLightLevel(entity2, blockPos2); leashState.startSkyLight = entity.level().getBrightness(LightLayer.SKY, blockPos); leashState.endSkyLight = entity.level().getBrightness(LightLayer.SKY, blockPos2); } else { entityRenderState.leashState = null; } entityRenderState.displayFireAnimation = entity.displayFireAnimation(); } }