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.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.state.EntityRenderState; 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.EquipmentModelSet; 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.ARGB; 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 MapRenderer mapRenderer; private final BlockRenderDispatcher blockRenderDispatcher; private final ItemInHandRenderer itemInHandRenderer; private final Font font; public final Options options; private final EntityModelSet entityModels; private final EquipmentModelSet equipmentModels; 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, MapRenderer mapRenderer, BlockRenderDispatcher blockRenderDispatcher, Font font, Options options, EntityModelSet entityModelSet, EquipmentModelSet equipmentModelSet ) { this.textureManager = textureManager; this.itemRenderer = itemRenderer; this.mapRenderer = mapRenderer; this.itemInHandRenderer = new ItemInHandRenderer(minecraft, this, itemRenderer); this.blockRenderDispatcher = blockRenderDispatcher; this.font = font; this.options = options; this.entityModels = entityModelSet; this.equipmentModels = equipmentModelSet; } 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 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 d, double e, double f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i) { EntityRenderer entityRenderer = this.getRenderer(entity); this.render(entity, d, e, f, g, poseStack, multiBufferSource, i, entityRenderer); } private void render( E entity, double d, double e, double f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource, int i, EntityRenderer entityRenderer ) { try { S entityRenderState = entityRenderer.createRenderState(entity, g); Vec3 vec3 = entityRenderer.getRenderOffset(entityRenderState); double h = d + vec3.x(); double j = e + vec3.y(); double k = f + vec3.z(); poseStack.pushPose(); poseStack.translate(h, j, k); entityRenderer.render(entityRenderState, poseStack, multiBufferSource, i); if (entityRenderState.displayFireAnimation) { this.renderFlame(poseStack, multiBufferSource, entityRenderState, Mth.rotationAroundAxis(Mth.Y_AXIS, this.cameraOrientation, new Quaternionf())); } if (entity instanceof Player) { poseStack.translate(-vec3.x(), -vec3.y(), -vec3.z()); } if (this.options.entityShadows().get() && this.shouldRenderShadow && !entityRenderState.isInvisible) { float l = entityRenderer.getShadowRadius(entityRenderState); if (l > 0.0F) { double m = entityRenderState.distanceToCameraSq; float n = (float)((1.0 - m / 256.0) * entityRenderer.shadowStrength); if (n > 0.0F) { renderShadow(poseStack, multiBufferSource, entityRenderState, n, g, this.level, Math.min(l, 32.0F)); } } } if (!(entity instanceof Player)) { poseStack.translate(-vec3.x(), -vec3.y(), -vec3.z()); } if (this.renderHitBoxes && !entityRenderState.isInvisible && !Minecraft.getInstance().showOnlyReducedInfo()) { renderHitbox(poseStack, multiBufferSource.getBuffer(RenderType.lines()), entity, g, 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, d, e, f)); crashReportCategory2.setDetail("Delta", g); 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); ShapeRenderer.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()); ShapeRenderer.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); ShapeRenderer.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; ShapeRenderer.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()); ShapeRenderer.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); } ShapeRenderer.renderVector(poseStack, buffer, new Vector3f(0.0F, entity.getEyeHeight(), 0.0F), entity.getViewVector(red).scale(2.0), -16776961); } private void renderFlame(PoseStack poseStack, MultiBufferSource multiBufferSource, EntityRenderState entityRenderState, Quaternionf quaternionf) { TextureAtlasSprite textureAtlasSprite = ModelBakery.FIRE_0.sprite(); TextureAtlasSprite textureAtlasSprite2 = ModelBakery.FIRE_1.sprite(); poseStack.pushPose(); float f = entityRenderState.boundingBoxWidth * 1.4F; poseStack.scale(f, f, f); float g = 0.5F; float h = 0.0F; float i = entityRenderState.boundingBoxHeight / f; float j = 0.0F; poseStack.mulPose(quaternionf); poseStack.translate(0.0F, 0.0F, 0.3F - (int)i * 0.02F); float k = 0.0F; int l = 0; VertexConsumer vertexConsumer = multiBufferSource.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 multiBufferSource, EntityRenderState entityRenderState, float f, float g, LevelReader levelReader, float h ) { float i = Math.min(f / 0.5F, h); int j = Mth.floor(entityRenderState.x - h); int k = Mth.floor(entityRenderState.x + h); int l = Mth.floor(entityRenderState.y - i); int m = Mth.floor(entityRenderState.y); int n = Mth.floor(entityRenderState.z - h); int o = Mth.floor(entityRenderState.z + h); PoseStack.Pose pose = poseStack.last(); VertexConsumer vertexConsumer = multiBufferSource.getBuffer(SHADOW_RENDER_TYPE); BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); for (int p = n; p <= o; p++) { for (int q = j; q <= k; q++) { mutableBlockPos.set(q, 0, p); ChunkAccess chunkAccess = levelReader.getChunk(mutableBlockPos); for (int r = l; r <= m; r++) { mutableBlockPos.setY(r); float s = f - (float)(entityRenderState.y - mutableBlockPos.getY()) * 0.5F; renderBlockShadow(pose, vertexConsumer, chunkAccess, levelReader, mutableBlockPos, entityRenderState.x, entityRenderState.y, entityRenderState.z, h, s); } } } } 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 = 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, 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.mapRenderer, this.blockRenderDispatcher, resourceManager, this.entityModels, this.equipmentModels, this.font ); this.renderers = EntityRenderers.createEntityRenderers(context); this.playerRenderers = EntityRenderers.createPlayerRenderers(context); } }