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 java.util.function.Supplier; 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.LightTexture; import net.minecraft.client.renderer.MapRenderer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.ShapeRenderer; 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.entity.EntityRendererProvider.Context; 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.renderer.entity.state.PlayerRenderState; import net.minecraft.client.renderer.entity.state.ServerHitboxesRenderState; import net.minecraft.client.renderer.item.ItemModelResolver; 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.Model; import net.minecraft.client.resources.model.EquipmentAssetManager; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManagerReloadListener; import net.minecraft.util.ARGB; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; 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 ItemModelResolver itemModelResolver; private final MapRenderer mapRenderer; private final BlockRenderDispatcher blockRenderDispatcher; private final ItemInHandRenderer itemInHandRenderer; private final Font font; public final Options options; private final Supplier entityModels; private final EquipmentAssetManager equipmentAssets; 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, ItemModelResolver itemModelResolver, ItemRenderer itemRenderer, MapRenderer mapRenderer, BlockRenderDispatcher blockRenderDispatcher, Font font, Options options, Supplier entityModels, EquipmentAssetManager equipmentModels ) { this.textureManager = textureManager; this.itemModelResolver = itemModelResolver; this.mapRenderer = mapRenderer; this.itemInHandRenderer = new ItemInHandRenderer(minecraft, this, itemRenderer, itemModelResolver); this.blockRenderDispatcher = blockRenderDispatcher; this.font = font; this.options = options; this.entityModels = entityModels; this.equipmentAssets = equipmentModels; } public EntityRenderer getRenderer(T entity) { if (entity instanceof AbstractClientPlayer abstractClientPlayer) { Model model = abstractClientPlayer.getSkin().model(); EntityRenderer entityRenderer = (EntityRenderer)this.playerRenderers.get(model); return (EntityRenderer)(entityRenderer != null ? entityRenderer : (EntityRenderer)this.playerRenderers.get(Model.WIDE)); } else { return (EntityRenderer)this.renderers.get(entity.getType()); } } public EntityRenderer getRenderer(S renderState) { if (renderState instanceof PlayerRenderState playerRenderState) { Model model = playerRenderState.skin.model(); EntityRenderer entityRenderer = (EntityRenderer)this.playerRenderers.get(model); return (EntityRenderer)(entityRenderer != null ? entityRenderer : (EntityRenderer)this.playerRenderers.get(Model.WIDE)); } else { return (EntityRenderer)this.renderers.get(renderState.entityType); } } 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 xOffset, double yOffset, double zOffset, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight ) { EntityRenderer entityRenderer = this.getRenderer(entity); this.render(entity, xOffset, yOffset, zOffset, partialTick, poseStack, bufferSource, packedLight, entityRenderer); } private void render( E entity, double xOffset, double yOffset, double zOffset, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, EntityRenderer renderer ) { S entityRenderState; try { entityRenderState = renderer.createRenderState(entity, partialTick); } catch (Throwable var19) { CrashReport crashReport = CrashReport.forThrowable(var19, "Extracting render state for an entity in world"); CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being extracted"); entity.fillCrashReportCategory(crashReportCategory); CrashReportCategory crashReportCategory2 = this.fillRendererDetails(xOffset, yOffset, zOffset, renderer, crashReport); crashReportCategory2.setDetail("Delta", partialTick); throw new ReportedException(crashReport); } try { this.render(entityRenderState, xOffset, yOffset, zOffset, poseStack, bufferSource, packedLight, renderer); } catch (Throwable var18) { CrashReport crashReport = CrashReport.forThrowable(var18, "Rendering entity in world"); CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being rendered"); entity.fillCrashReportCategory(crashReportCategory); throw new ReportedException(crashReport); } } public void render( S renderState, double xOffset, double yOffset, double zOffset, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight ) { EntityRenderer entityRenderer = this.getRenderer(renderState); this.render(renderState, xOffset, yOffset, zOffset, poseStack, bufferSource, packedLight, entityRenderer); } private void render( S renderState, double xOffset, double yOffset, double zOffset, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, EntityRenderer renderer ) { try { Vec3 vec3 = renderer.getRenderOffset(renderState); double d = xOffset + vec3.x(); double e = yOffset + vec3.y(); double f = zOffset + vec3.z(); poseStack.pushPose(); poseStack.translate(d, e, f); renderer.render(renderState, poseStack, bufferSource, packedLight); if (renderState.displayFireAnimation) { this.renderFlame(poseStack, bufferSource, renderState, Mth.rotationAroundAxis(Mth.Y_AXIS, this.cameraOrientation, new Quaternionf())); } if (renderState instanceof PlayerRenderState) { poseStack.translate(-vec3.x(), -vec3.y(), -vec3.z()); } if (this.options.entityShadows().get() && this.shouldRenderShadow && !renderState.isInvisible) { float g = renderer.getShadowRadius(renderState); if (g > 0.0F) { double h = renderState.distanceToCameraSq; float i = (float)((1.0 - h / 256.0) * renderer.getShadowStrength(renderState)); if (i > 0.0F) { renderShadow(poseStack, bufferSource, renderState, i, this.level, Math.min(g, 32.0F)); } } } if (!(renderState instanceof PlayerRenderState)) { poseStack.translate(-vec3.x(), -vec3.y(), -vec3.z()); } if (renderState.hitboxesRenderState != null) { this.renderHitboxes(poseStack, renderState, renderState.hitboxesRenderState, bufferSource); } poseStack.popPose(); } catch (Throwable var23) { CrashReport crashReport = CrashReport.forThrowable(var23, "Rendering entity in world"); CrashReportCategory crashReportCategory = crashReport.addCategory("EntityRenderState being rendered"); renderState.fillCrashReportCategory(crashReportCategory); this.fillRendererDetails(xOffset, yOffset, zOffset, renderer, crashReport); throw new ReportedException(crashReport); } } private CrashReportCategory fillRendererDetails( double xOffset, double yOffset, double zOffset, EntityRenderer renderer, CrashReport crashReport ) { CrashReportCategory crashReportCategory = crashReport.addCategory("Renderer details"); crashReportCategory.setDetail("Assigned renderer", renderer); crashReportCategory.setDetail("Location", CrashReportCategory.formatLocation(this.level, xOffset, yOffset, zOffset)); return crashReportCategory; } private void renderHitboxes(PoseStack poseStack, EntityRenderState renderState, HitboxesRenderState hitboxesRenderState, MultiBufferSource bufferSource) { VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.lines()); renderHitboxesAndViewVector(poseStack, hitboxesRenderState, vertexConsumer, renderState.eyeHeight); ServerHitboxesRenderState serverHitboxesRenderState = renderState.serverHitboxesRenderState; if (serverHitboxesRenderState != null) { if (serverHitboxesRenderState.missing()) { HitboxRenderState hitboxRenderState = (HitboxRenderState)hitboxesRenderState.hitboxes().getFirst(); DebugRenderer.renderFloatingText(poseStack, bufferSource, "Missing", renderState.x, hitboxRenderState.y1() + 1.5, renderState.z, -65536); } else if (serverHitboxesRenderState.hitboxes() != null) { poseStack.pushPose(); poseStack.translate( serverHitboxesRenderState.serverEntityX() - renderState.x, serverHitboxesRenderState.serverEntityY() - renderState.y, serverHitboxesRenderState.serverEntityZ() - renderState.z ); renderHitboxesAndViewVector(poseStack, serverHitboxesRenderState.hitboxes(), vertexConsumer, serverHitboxesRenderState.eyeHeight()); Vec3 vec3 = new Vec3(serverHitboxesRenderState.deltaMovementX(), serverHitboxesRenderState.deltaMovementY(), serverHitboxesRenderState.deltaMovementZ()); ShapeRenderer.renderVector(poseStack, vertexConsumer, new Vector3f(), vec3, -256); poseStack.popPose(); } } } private static void renderHitboxesAndViewVector(PoseStack poseStack, HitboxesRenderState hitboxesRenderState, VertexConsumer consumer, float eyeHeight) { for (HitboxRenderState hitboxRenderState : hitboxesRenderState.hitboxes()) { renderHitbox(poseStack, consumer, hitboxRenderState); } Vec3 vec3 = new Vec3(hitboxesRenderState.viewX(), hitboxesRenderState.viewY(), hitboxesRenderState.viewZ()); ShapeRenderer.renderVector(poseStack, consumer, new Vector3f(0.0F, eyeHeight, 0.0F), vec3.scale(2.0), -16776961); } private static void renderHitbox(PoseStack posStack, VertexConsumer consumer, HitboxRenderState hitbox) { posStack.pushPose(); posStack.translate(hitbox.offsetX(), hitbox.offsetY(), hitbox.offsetZ()); ShapeRenderer.renderLineBox( posStack, consumer, hitbox.x0(), hitbox.y0(), hitbox.z0(), hitbox.x1(), hitbox.y1(), hitbox.z1(), hitbox.red(), hitbox.green(), hitbox.blue(), 1.0F ); posStack.popPose(); } private void renderFlame(PoseStack poseStack, MultiBufferSource bufferSource, EntityRenderState renderState, Quaternionf quaternion) { TextureAtlasSprite textureAtlasSprite = ModelBakery.FIRE_0.sprite(); TextureAtlasSprite textureAtlasSprite2 = ModelBakery.FIRE_1.sprite(); poseStack.pushPose(); float f = renderState.boundingBoxWidth * 1.4F; poseStack.scale(f, f, f); float g = 0.5F; float h = 0.0F; float i = renderState.boundingBoxHeight / 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 = bufferSource.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 bufferSource, EntityRenderState renderState, float strength, LevelReader level, float size ) { float f = Math.min(strength / 0.5F, size); int i = Mth.floor(renderState.x - size); int j = Mth.floor(renderState.x + size); int k = Mth.floor(renderState.y - f); int l = Mth.floor(renderState.y); int m = Mth.floor(renderState.z - size); int n = Mth.floor(renderState.z + size); PoseStack.Pose pose = poseStack.last(); VertexConsumer vertexConsumer = bufferSource.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 g = strength - (float)(renderState.y - mutableBlockPos.getY()) * 0.5F; renderBlockShadow(pose, vertexConsumer, chunkAccess, level, mutableBlockPos, renderState.x, renderState.y, renderState.z, size, g); } } } } private static void renderBlockShadow( PoseStack.Pose pose, VertexConsumer consumer, 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 = ARGB.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, consumer, i, l, n, o, q, s); shadowVertex(pose, consumer, i, l, n, p, q, t); shadowVertex(pose, consumer, i, m, n, p, r, t); shadowVertex(pose, consumer, 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) { Context context = new Context( this, this.itemModelResolver, this.mapRenderer, this.blockRenderDispatcher, resourceManager, (EntityModelSet)this.entityModels.get(), this.equipmentAssets, this.font ); this.renderers = EntityRenderers.createEntityRenderers(context); this.playerRenderers = EntityRenderers.createPlayerRenderers(context); } }