package net.minecraft.client.renderer.entity; import com.google.common.collect.ImmutableMap; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import java.util.Map; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.Options; import net.minecraft.client.gui.Font; import net.minecraft.client.model.geom.EntityModelSet; import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.client.renderer.ItemInHandRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.Sheets; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.debug.DebugRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.resources.PlayerSkin; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.server.IntegratedServer; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManagerReloadListener; import net.minecraft.util.FastColor; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; @Environment(EnvType.CLIENT) public class EntityRenderDispatcher implements ResourceManagerReloadListener { private static final RenderType SHADOW_RENDER_TYPE = RenderType.entityShadow(ResourceLocation.withDefaultNamespace("textures/misc/shadow.png")); private static final float MAX_SHADOW_RADIUS = 32.0F; private static final float SHADOW_POWER_FALLOFF_Y = 0.5F; private Map, EntityRenderer> renderers = ImmutableMap.of(); /** * lists the various player skin types with their associated Renderer class instances. */ private Map> playerRenderers = Map.of(); public final TextureManager textureManager; private Level level; public Camera camera; private Quaternionf cameraOrientation; public Entity crosshairPickEntity; private final ItemRenderer itemRenderer; private final BlockRenderDispatcher blockRenderDispatcher; private final ItemInHandRenderer itemInHandRenderer; private final Font font; public final Options options; private final EntityModelSet entityModels; private boolean shouldRenderShadow = true; private boolean renderHitBoxes; public int getPackedLightCoords(E entity, float partialTicks) { return this.getRenderer(entity).getPackedLightCoords(entity, partialTicks); } public EntityRenderDispatcher( Minecraft minecraft, TextureManager textureManager, ItemRenderer itemRenderer, BlockRenderDispatcher blockRenderDispatcher, Font font, Options options, EntityModelSet entityModels ) { this.textureManager = textureManager; this.itemRenderer = itemRenderer; this.itemInHandRenderer = new ItemInHandRenderer(minecraft, this, itemRenderer); this.blockRenderDispatcher = blockRenderDispatcher; this.font = font; this.options = options; this.entityModels = entityModels; } public EntityRenderer getRenderer(T entity) { if (entity instanceof AbstractClientPlayer abstractClientPlayer) { PlayerSkin.Model model = abstractClientPlayer.getSkin().model(); EntityRenderer entityRenderer = (EntityRenderer)this.playerRenderers.get(model); return (EntityRenderer)(entityRenderer != null ? entityRenderer : (EntityRenderer)this.playerRenderers.get(PlayerSkin.Model.WIDE)); } else { return (EntityRenderer)this.renderers.get(entity.getType()); } } public void prepare(Level level, Camera activeRenderInfo, Entity entity) { this.level = level; this.camera = activeRenderInfo; this.cameraOrientation = activeRenderInfo.rotation(); this.crosshairPickEntity = entity; } public void overrideCameraOrientation(Quaternionf cameraOrientation) { this.cameraOrientation = cameraOrientation; } public void setRenderShadow(boolean renderShadow) { this.shouldRenderShadow = renderShadow; } public void setRenderHitBoxes(boolean debugBoundingBox) { this.renderHitBoxes = debugBoundingBox; } public boolean shouldRenderHitBoxes() { return this.renderHitBoxes; } public boolean shouldRender(E entity, Frustum frustum, double camX, double camY, double camZ) { EntityRenderer entityRenderer = this.getRenderer(entity); return entityRenderer.shouldRender(entity, frustum, camX, camY, camZ); } public void render( E entity, double x, double y, double z, float rotationYaw, float partialTicks, PoseStack poseStack, MultiBufferSource buffer, int packedLight ) { EntityRenderer entityRenderer = this.getRenderer(entity); try { Vec3 vec3 = entityRenderer.getRenderOffset(entity, partialTicks); double d = x + vec3.x(); double e = y + vec3.y(); double f = z + vec3.z(); poseStack.pushPose(); poseStack.translate(d, e, f); entityRenderer.render(entity, rotationYaw, partialTicks, poseStack, buffer, packedLight); if (entity.displayFireAnimation()) { this.renderFlame(poseStack, buffer, entity, Mth.rotationAroundAxis(Mth.Y_AXIS, this.cameraOrientation, new Quaternionf())); } poseStack.translate(-vec3.x(), -vec3.y(), -vec3.z()); if (this.options.entityShadows().get() && this.shouldRenderShadow && !entity.isInvisible()) { float g = entityRenderer.getShadowRadius(entity); if (g > 0.0F) { double h = this.distanceToSqr(entity.getX(), entity.getY(), entity.getZ()); float i = (float)((1.0 - h / 256.0) * entityRenderer.shadowStrength); if (i > 0.0F) { renderShadow(poseStack, buffer, entity, i, partialTicks, this.level, Math.min(g, 32.0F)); } } } if (this.renderHitBoxes && !entity.isInvisible() && !Minecraft.getInstance().showOnlyReducedInfo()) { renderHitbox(poseStack, buffer.getBuffer(RenderType.lines()), entity, partialTicks, 1.0F, 1.0F, 1.0F); } poseStack.popPose(); } catch (Throwable var25) { CrashReport crashReport = CrashReport.forThrowable(var25, "Rendering entity in world"); CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being rendered"); entity.fillCrashReportCategory(crashReportCategory); CrashReportCategory crashReportCategory2 = crashReport.addCategory("Renderer details"); crashReportCategory2.setDetail("Assigned renderer", entityRenderer); crashReportCategory2.setDetail("Location", CrashReportCategory.formatLocation(this.level, x, y, z)); crashReportCategory2.setDetail("Rotation", rotationYaw); crashReportCategory2.setDetail("Delta", partialTicks); throw new ReportedException(crashReport); } } private static void renderServerSideHitbox(PoseStack poseStack, Entity entity, MultiBufferSource bufferSource) { Entity entity2 = getServerSideEntity(entity); if (entity2 == null) { DebugRenderer.renderFloatingText(poseStack, bufferSource, "Missing", entity.getX(), entity.getBoundingBox().maxY + 1.5, entity.getZ(), -65536); } else { poseStack.pushPose(); poseStack.translate(entity2.getX() - entity.getX(), entity2.getY() - entity.getY(), entity2.getZ() - entity.getZ()); renderHitbox(poseStack, bufferSource.getBuffer(RenderType.lines()), entity2, 1.0F, 0.0F, 1.0F, 0.0F); renderVector(poseStack, bufferSource.getBuffer(RenderType.lines()), new Vector3f(), entity2.getDeltaMovement(), -256); poseStack.popPose(); } } @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; } private static void renderHitbox(PoseStack poseStack, VertexConsumer buffer, Entity entity, float red, float green, float blue, float alpha) { AABB aABB = entity.getBoundingBox().move(-entity.getX(), -entity.getY(), -entity.getZ()); LevelRenderer.renderLineBox(poseStack, buffer, aABB, green, blue, alpha, 1.0F); if (entity instanceof EnderDragon) { double d = -Mth.lerp((double)red, entity.xOld, entity.getX()); double e = -Mth.lerp((double)red, entity.yOld, entity.getY()); double f = -Mth.lerp((double)red, entity.zOld, entity.getZ()); for (EnderDragonPart enderDragonPart : ((EnderDragon)entity).getSubEntities()) { poseStack.pushPose(); double g = d + Mth.lerp((double)red, enderDragonPart.xOld, enderDragonPart.getX()); double h = e + Mth.lerp((double)red, enderDragonPart.yOld, enderDragonPart.getY()); double i = f + Mth.lerp((double)red, enderDragonPart.zOld, enderDragonPart.getZ()); poseStack.translate(g, h, i); LevelRenderer.renderLineBox( poseStack, buffer, enderDragonPart.getBoundingBox().move(-enderDragonPart.getX(), -enderDragonPart.getY(), -enderDragonPart.getZ()), 0.25F, 1.0F, 0.0F, 1.0F ); poseStack.popPose(); } } if (entity instanceof LivingEntity) { float j = 0.01F; LevelRenderer.renderLineBox( poseStack, buffer, aABB.minX, entity.getEyeHeight() - 0.01F, aABB.minZ, aABB.maxX, entity.getEyeHeight() + 0.01F, aABB.maxZ, 1.0F, 0.0F, 0.0F, 1.0F ); } Entity entity2 = entity.getVehicle(); if (entity2 != null) { float k = Math.min(entity2.getBbWidth(), entity.getBbWidth()) / 2.0F; float l = 0.0625F; Vec3 vec3 = entity2.getPassengerRidingPosition(entity).subtract(entity.position()); LevelRenderer.renderLineBox(poseStack, buffer, vec3.x - k, vec3.y, vec3.z - k, vec3.x + k, vec3.y + 0.0625, vec3.z + k, 1.0F, 1.0F, 0.0F, 1.0F); } renderVector(poseStack, buffer, new Vector3f(0.0F, entity.getEyeHeight(), 0.0F), entity.getViewVector(red).scale(2.0), -16776961); } private static void renderVector(PoseStack poseStack, VertexConsumer buffer, Vector3f startPos, Vec3 vector, int color) { PoseStack.Pose pose = poseStack.last(); buffer.addVertex(pose, startPos).setColor(color).setNormal(pose, (float)vector.x, (float)vector.y, (float)vector.z); buffer.addVertex(pose, (float)(startPos.x() + vector.x), (float)(startPos.y() + vector.y), (float)(startPos.z() + vector.z)) .setColor(color) .setNormal(pose, (float)vector.x, (float)vector.y, (float)vector.z); } private void renderFlame(PoseStack poseStack, MultiBufferSource buffer, Entity entity, Quaternionf quaternion) { TextureAtlasSprite textureAtlasSprite = ModelBakery.FIRE_0.sprite(); TextureAtlasSprite textureAtlasSprite2 = ModelBakery.FIRE_1.sprite(); poseStack.pushPose(); float f = entity.getBbWidth() * 1.4F; poseStack.scale(f, f, f); float g = 0.5F; float h = 0.0F; float i = entity.getBbHeight() / f; float j = 0.0F; poseStack.mulPose(quaternion); poseStack.translate(0.0F, 0.0F, 0.3F - (int)i * 0.02F); float k = 0.0F; int l = 0; VertexConsumer vertexConsumer = buffer.getBuffer(Sheets.cutoutBlockSheet()); for (PoseStack.Pose pose = poseStack.last(); i > 0.0F; l++) { TextureAtlasSprite textureAtlasSprite3 = l % 2 == 0 ? textureAtlasSprite : textureAtlasSprite2; float m = textureAtlasSprite3.getU0(); float n = textureAtlasSprite3.getV0(); float o = textureAtlasSprite3.getU1(); float p = textureAtlasSprite3.getV1(); if (l / 2 % 2 == 0) { float q = o; o = m; m = q; } fireVertex(pose, vertexConsumer, -g - 0.0F, 0.0F - j, k, o, p); fireVertex(pose, vertexConsumer, g - 0.0F, 0.0F - j, k, m, p); fireVertex(pose, vertexConsumer, g - 0.0F, 1.4F - j, k, m, n); fireVertex(pose, vertexConsumer, -g - 0.0F, 1.4F - j, k, o, n); i -= 0.45F; j -= 0.45F; g *= 0.9F; k -= 0.03F; } poseStack.popPose(); } private static void fireVertex(PoseStack.Pose matrixEntry, VertexConsumer buffer, float x, float y, float z, float texU, float texV) { buffer.addVertex(matrixEntry, x, y, z).setColor(-1).setUv(texU, texV).setUv1(0, 10).setLight(240).setNormal(matrixEntry, 0.0F, 1.0F, 0.0F); } private static void renderShadow(PoseStack poseStack, MultiBufferSource buffer, Entity entity, float weight, float partialTicks, LevelReader level, float size) { double d = Mth.lerp((double)partialTicks, entity.xOld, entity.getX()); double e = Mth.lerp((double)partialTicks, entity.yOld, entity.getY()); double f = Mth.lerp((double)partialTicks, entity.zOld, entity.getZ()); float g = Math.min(weight / 0.5F, size); int i = Mth.floor(d - size); int j = Mth.floor(d + size); int k = Mth.floor(e - g); int l = Mth.floor(e); int m = Mth.floor(f - size); int n = Mth.floor(f + size); PoseStack.Pose pose = poseStack.last(); VertexConsumer vertexConsumer = buffer.getBuffer(SHADOW_RENDER_TYPE); BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); for (int o = m; o <= n; o++) { for (int p = i; p <= j; p++) { mutableBlockPos.set(p, 0, o); ChunkAccess chunkAccess = level.getChunk(mutableBlockPos); for (int q = k; q <= l; q++) { mutableBlockPos.setY(q); float h = weight - (float)(e - mutableBlockPos.getY()) * 0.5F; renderBlockShadow(pose, vertexConsumer, chunkAccess, level, mutableBlockPos, d, e, f, size, h); } } } } private static void renderBlockShadow( PoseStack.Pose pose, VertexConsumer vertexConsumer, ChunkAccess chunk, LevelReader level, BlockPos pos, double x, double y, double z, float size, float weight ) { BlockPos blockPos = pos.below(); BlockState blockState = chunk.getBlockState(blockPos); if (blockState.getRenderShape() != RenderShape.INVISIBLE && level.getMaxLocalRawBrightness(pos) > 3) { if (blockState.isCollisionShapeFullBlock(chunk, blockPos)) { VoxelShape voxelShape = blockState.getShape(chunk, blockPos); if (!voxelShape.isEmpty()) { float f = LightTexture.getBrightness(level.dimensionType(), level.getMaxLocalRawBrightness(pos)); float g = weight * 0.5F * f; if (g >= 0.0F) { if (g > 1.0F) { g = 1.0F; } int i = FastColor.ARGB32.color(Mth.floor(g * 255.0F), 255, 255, 255); AABB aABB = voxelShape.bounds(); double d = pos.getX() + aABB.minX; double e = pos.getX() + aABB.maxX; double h = pos.getY() + aABB.minY; double j = pos.getZ() + aABB.minZ; double k = pos.getZ() + aABB.maxZ; float l = (float)(d - x); float m = (float)(e - x); float n = (float)(h - y); float o = (float)(j - z); float p = (float)(k - z); float q = -l / 2.0F / size + 0.5F; float r = -m / 2.0F / size + 0.5F; float s = -o / 2.0F / size + 0.5F; float t = -p / 2.0F / size + 0.5F; shadowVertex(pose, vertexConsumer, i, l, n, o, q, s); shadowVertex(pose, vertexConsumer, i, l, n, p, q, t); shadowVertex(pose, vertexConsumer, i, m, n, p, r, t); shadowVertex(pose, vertexConsumer, i, m, n, o, r, s); } } } } } private static void shadowVertex(PoseStack.Pose pose, VertexConsumer consumer, int color, float offsetX, float offsetY, float offsetZ, float u, float v) { Vector3f vector3f = pose.pose().transformPosition(offsetX, offsetY, offsetZ, new Vector3f()); consumer.addVertex(vector3f.x(), vector3f.y(), vector3f.z(), color, u, v, OverlayTexture.NO_OVERLAY, 15728880, 0.0F, 1.0F, 0.0F); } /** * World sets this RenderManager's worldObj to the world provided */ public void setLevel(@Nullable Level level) { this.level = level; if (level == null) { this.camera = null; } } public double distanceToSqr(Entity entity) { return this.camera.getPosition().distanceToSqr(entity.position()); } public double distanceToSqr(double x, double y, double z) { return this.camera.getPosition().distanceToSqr(x, y, z); } public Quaternionf cameraOrientation() { return this.cameraOrientation; } public ItemInHandRenderer getItemInHandRenderer() { return this.itemInHandRenderer; } @Override public void onResourceManagerReload(ResourceManager resourceManager) { EntityRendererProvider.Context context = new EntityRendererProvider.Context( this, this.itemRenderer, this.blockRenderDispatcher, this.itemInHandRenderer, resourceManager, this.entityModels, this.font ); this.renderers = EntityRenderers.createEntityRenderers(context); this.playerRenderers = EntityRenderers.createPlayerRenderers(context); } }