package net.minecraft.client.renderer; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.framegraph.FrameGraphBuilder; import com.mojang.blaze3d.framegraph.FramePass; import com.mojang.blaze3d.framegraph.FrameGraphBuilder.Inspector; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.pipeline.TextureTarget; import com.mojang.blaze3d.platform.Lighting; import com.mojang.blaze3d.resource.GraphicsResourceAllocator; import com.mojang.blaze3d.resource.RenderTargetDescriptor; import com.mojang.blaze3d.resource.ResourceHandle; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.GpuTexture; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.SheetedDecalTextureGenerator; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexMultiConsumer; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectFunction; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectListIterator; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.Set; import java.util.SortedSet; 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.CrashReportDetail; import net.minecraft.ReportedException; import net.minecraft.Util; import net.minecraft.client.Camera; import net.minecraft.client.CloudStatus; import net.minecraft.client.DeltaTracker; import net.minecraft.client.GraphicsStatus; import net.minecraft.client.Minecraft; import net.minecraft.client.PrioritizeChunkUpdates; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.Particle; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.DimensionSpecialEffects.SkyType; import net.minecraft.client.renderer.FogRenderer.FogMode; import net.minecraft.client.renderer.MultiBufferSource.BufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; import net.minecraft.client.renderer.chunk.RenderRegionCache; import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.entity.EntityRenderDispatcher; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.NbtOps; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.BlockDestructionProgress; import net.minecraft.server.level.ParticleStatus; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.ResourceManagerReloadListener; import net.minecraft.util.ARGB; import net.minecraft.util.Brightness; import net.minecraft.util.Mth; import net.minecraft.util.VisibleForDebug; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.profiling.Zone; import net.minecraft.world.TickRateManager; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FogType; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.HitResult.Type; import net.minecraft.world.phys.shapes.CollisionContext; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; import org.joml.Matrix4fStack; import org.joml.Vector4f; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public class LevelRenderer implements ResourceManagerReloadListener, AutoCloseable { private static final Logger LOGGER = LogUtils.getLogger(); private static final ResourceLocation TRANSPARENCY_POST_CHAIN_ID = ResourceLocation.withDefaultNamespace("transparency"); private static final ResourceLocation ENTITY_OUTLINE_POST_CHAIN_ID = ResourceLocation.withDefaultNamespace("entity_outline"); public static final int SECTION_SIZE = 16; public static final int HALF_SECTION_SIZE = 8; public static final int NEARBY_SECTION_DISTANCE_IN_BLOCKS = 32; private static final int MINIMUM_TRANSPARENT_SORT_COUNT = 15; private final Minecraft minecraft; private final EntityRenderDispatcher entityRenderDispatcher; private final BlockEntityRenderDispatcher blockEntityRenderDispatcher; private final RenderBuffers renderBuffers; private final SkyRenderer skyRenderer = new SkyRenderer(); private final CloudRenderer cloudRenderer = new CloudRenderer(); private final WorldBorderRenderer worldBorderRenderer = new WorldBorderRenderer(); private final WeatherEffectRenderer weatherEffectRenderer = new WeatherEffectRenderer(); @Nullable private ClientLevel level; private final SectionOcclusionGraph sectionOcclusionGraph = new SectionOcclusionGraph(); private final ObjectArrayList visibleSections = new ObjectArrayList<>(10000); private final ObjectArrayList nearbyVisibleSections = new ObjectArrayList<>(50); /** * Global block entities; these are always rendered, even if off-screen. * Any block entity is added to this if {@link net.minecraft.client.renderer.blockentity.BlockEntityRenderer#shouldRenderOffScreen(net.minecraft.world.level.block.entity.BlockEntity)} returns {@code true}. */ private final Set globalBlockEntities = Sets.newHashSet(); @Nullable private ViewArea viewArea; private int ticks; private final Int2ObjectMap destroyingBlocks = new Int2ObjectOpenHashMap<>(); private final Long2ObjectMap> destructionProgress = new Long2ObjectOpenHashMap<>(); @Nullable private RenderTarget entityOutlineTarget; private final LevelTargetBundle targets = new LevelTargetBundle(); private int lastCameraSectionX = Integer.MIN_VALUE; private int lastCameraSectionY = Integer.MIN_VALUE; private int lastCameraSectionZ = Integer.MIN_VALUE; private double prevCamX = Double.MIN_VALUE; private double prevCamY = Double.MIN_VALUE; private double prevCamZ = Double.MIN_VALUE; private double prevCamRotX = Double.MIN_VALUE; private double prevCamRotY = Double.MIN_VALUE; @Nullable private SectionRenderDispatcher sectionRenderDispatcher; private int lastViewDistance = -1; private final List visibleEntities = new ArrayList(); private int visibleEntityCount; private Frustum cullingFrustum; private boolean captureFrustum; @Nullable private Frustum capturedFrustum; @Nullable private BlockPos lastTranslucentSortBlockPos; private int translucencyResortIterationIndex; public LevelRenderer( Minecraft minecraft, EntityRenderDispatcher entityRenderDispatcher, BlockEntityRenderDispatcher blockEntityRenderDispatcher, RenderBuffers renderBuffers ) { this.minecraft = minecraft; this.entityRenderDispatcher = entityRenderDispatcher; this.blockEntityRenderDispatcher = blockEntityRenderDispatcher; this.renderBuffers = renderBuffers; } public void tickParticles(Camera camera) { this.weatherEffectRenderer.tickRainParticles(this.minecraft.level, camera, this.ticks, this.minecraft.options.particles().get()); } public void close() { if (this.entityOutlineTarget != null) { this.entityOutlineTarget.destroyBuffers(); } this.skyRenderer.close(); this.cloudRenderer.close(); } @Override public void onResourceManagerReload(ResourceManager resourceManager) { this.initOutline(); } public void initOutline() { if (this.entityOutlineTarget != null) { this.entityOutlineTarget.destroyBuffers(); } this.entityOutlineTarget = new TextureTarget("Entity Outline", this.minecraft.getWindow().getWidth(), this.minecraft.getWindow().getHeight(), true); } @Nullable private PostChain getTransparencyChain() { if (!Minecraft.useShaderTransparency()) { return null; } else { PostChain postChain = this.minecraft.getShaderManager().getPostChain(TRANSPARENCY_POST_CHAIN_ID, LevelTargetBundle.SORTING_TARGETS); if (postChain == null) { this.minecraft.options.graphicsMode().set(GraphicsStatus.FANCY); this.minecraft.options.save(); } return postChain; } } public void doEntityOutline() { if (this.shouldShowEntityOutlines()) { this.entityOutlineTarget.blitAndBlendToTexture(this.minecraft.getMainRenderTarget().getColorTexture()); } } protected boolean shouldShowEntityOutlines() { return !this.minecraft.gameRenderer.isPanoramicMode() && this.entityOutlineTarget != null && this.minecraft.player != null; } /** * @param level the level to set, or {@code null} to clear */ public void setLevel(@Nullable ClientLevel level) { this.lastCameraSectionX = Integer.MIN_VALUE; this.lastCameraSectionY = Integer.MIN_VALUE; this.lastCameraSectionZ = Integer.MIN_VALUE; this.entityRenderDispatcher.setLevel(level); this.level = level; if (level != null) { this.allChanged(); } else { if (this.viewArea != null) { this.viewArea.releaseAllBuffers(); this.viewArea = null; } if (this.sectionRenderDispatcher != null) { this.sectionRenderDispatcher.dispose(); } this.sectionRenderDispatcher = null; this.globalBlockEntities.clear(); this.sectionOcclusionGraph.waitAndReset(null); this.clearVisibleSections(); } } private void clearVisibleSections() { this.visibleSections.clear(); this.nearbyVisibleSections.clear(); } /** * Loads all renderers and sets up the basic options usage. */ public void allChanged() { if (this.level != null) { this.level.clearTintCaches(); if (this.sectionRenderDispatcher == null) { this.sectionRenderDispatcher = new SectionRenderDispatcher( this.level, this, Util.backgroundExecutor(), this.renderBuffers, this.minecraft.getBlockRenderer(), this.minecraft.getBlockEntityRenderDispatcher() ); } else { this.sectionRenderDispatcher.setLevel(this.level); } this.cloudRenderer.markForRebuild(); ItemBlockRenderTypes.setFancy(Minecraft.useFancyGraphics()); this.lastViewDistance = this.minecraft.options.getEffectiveRenderDistance(); if (this.viewArea != null) { this.viewArea.releaseAllBuffers(); } this.sectionRenderDispatcher.blockUntilClear(); synchronized (this.globalBlockEntities) { this.globalBlockEntities.clear(); } this.viewArea = new ViewArea(this.sectionRenderDispatcher, this.level, this.minecraft.options.getEffectiveRenderDistance(), this); this.sectionOcclusionGraph.waitAndReset(this.viewArea); this.clearVisibleSections(); Camera camera = this.minecraft.gameRenderer.getMainCamera(); this.viewArea.repositionCamera(SectionPos.of(camera.getPosition())); } } public void resize(int width, int height) { this.needsUpdate(); if (this.entityOutlineTarget != null) { this.entityOutlineTarget.resize(width, height); } } public String getSectionStatistics() { int i = this.viewArea.sections.length; int j = this.countRenderedSections(); return String.format( Locale.ROOT, "C: %d/%d %sD: %d, %s", j, i, this.minecraft.smartCull ? "(s) " : "", this.lastViewDistance, this.sectionRenderDispatcher == null ? "null" : this.sectionRenderDispatcher.getStats() ); } public SectionRenderDispatcher getSectionRenderDispatcher() { return this.sectionRenderDispatcher; } public double getTotalSections() { return this.viewArea.sections.length; } public double getLastViewDistance() { return this.lastViewDistance; } public int countRenderedSections() { int i = 0; for (SectionRenderDispatcher.RenderSection renderSection : this.visibleSections) { if (renderSection.getCompiled().hasRenderableLayers()) { i++; } } return i; } /** * @return entity rendering statistics to display on the {@linkplain net.minecraft.client.gui.components.DebugScreenOverlay debug overlay} */ public String getEntityStatistics() { return "E: " + this.visibleEntityCount + "/" + this.level.getEntityCount() + ", SD: " + this.level.getServerSimulationDistance(); } private void setupRender(Camera camera, Frustum frustum, boolean hasCapturedFrustum, boolean isSpectator) { Vec3 vec3 = camera.getPosition(); if (this.minecraft.options.getEffectiveRenderDistance() != this.lastViewDistance) { this.allChanged(); } ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("camera"); int i = SectionPos.posToSectionCoord(vec3.x()); int j = SectionPos.posToSectionCoord(vec3.y()); int k = SectionPos.posToSectionCoord(vec3.z()); if (this.lastCameraSectionX != i || this.lastCameraSectionY != j || this.lastCameraSectionZ != k) { this.lastCameraSectionX = i; this.lastCameraSectionY = j; this.lastCameraSectionZ = k; this.viewArea.repositionCamera(SectionPos.of(vec3)); this.worldBorderRenderer.invalidate(); } this.sectionRenderDispatcher.setCamera(vec3); profilerFiller.popPush("cull"); double d = Math.floor(vec3.x / 8.0); double e = Math.floor(vec3.y / 8.0); double f = Math.floor(vec3.z / 8.0); if (d != this.prevCamX || e != this.prevCamY || f != this.prevCamZ) { this.sectionOcclusionGraph.invalidate(); } this.prevCamX = d; this.prevCamY = e; this.prevCamZ = f; profilerFiller.popPush("update"); if (!hasCapturedFrustum) { boolean bl = this.minecraft.smartCull; if (isSpectator && this.level.getBlockState(camera.getBlockPosition()).isSolidRender()) { bl = false; } profilerFiller.push("section_occlusion_graph"); this.sectionOcclusionGraph.update(bl, camera, frustum, this.visibleSections, this.level.getChunkSource().getLoadedEmptySections()); profilerFiller.pop(); double g = Math.floor(camera.getXRot() / 2.0F); double h = Math.floor(camera.getYRot() / 2.0F); if (this.sectionOcclusionGraph.consumeFrustumUpdate() || g != this.prevCamRotX || h != this.prevCamRotY) { this.applyFrustum(offsetFrustum(frustum)); this.prevCamRotX = g; this.prevCamRotY = h; } } profilerFiller.pop(); } public static Frustum offsetFrustum(Frustum frustum) { return new Frustum(frustum).offsetToFullyIncludeCameraCube(8); } private void applyFrustum(Frustum frustum) { if (!Minecraft.getInstance().isSameThread()) { throw new IllegalStateException("applyFrustum called from wrong thread: " + Thread.currentThread().getName()); } else { Profiler.get().push("apply_frustum"); this.clearVisibleSections(); this.sectionOcclusionGraph.addSectionsInFrustum(frustum, this.visibleSections, this.nearbyVisibleSections); Profiler.get().pop(); } } public void addRecentlyCompiledSection(SectionRenderDispatcher.RenderSection renderSection) { this.sectionOcclusionGraph.schedulePropagationFrom(renderSection); } public void prepareCullFrustum(Vec3 cameraPosition, Matrix4f frustumMatrix, Matrix4f projectionMatrix) { this.cullingFrustum = new Frustum(frustumMatrix, projectionMatrix); this.cullingFrustum.prepare(cameraPosition.x(), cameraPosition.y(), cameraPosition.z()); } public void renderLevel( GraphicsResourceAllocator graphicsResourceAllocator, DeltaTracker deltaTracker, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f frustumMatrix, Matrix4f projectionMatrix ) { float f = deltaTracker.getGameTimeDeltaPartialTick(false); RenderSystem.setShaderGameTime(this.level.getGameTime(), f); this.blockEntityRenderDispatcher.prepare(this.level, camera, this.minecraft.hitResult); this.entityRenderDispatcher.prepare(this.level, camera, this.minecraft.crosshairPickEntity); final ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.popPush("light_update_queue"); this.level.pollLightUpdates(); profilerFiller.popPush("light_updates"); this.level.getChunkSource().getLightEngine().runLightUpdates(); Vec3 vec3 = camera.getPosition(); double d = vec3.x(); double e = vec3.y(); double g = vec3.z(); profilerFiller.popPush("culling"); boolean bl = this.capturedFrustum != null; Frustum frustum = bl ? this.capturedFrustum : this.cullingFrustum; Profiler.get().popPush("captureFrustum"); if (this.captureFrustum) { this.capturedFrustum = bl ? new Frustum(frustumMatrix, projectionMatrix) : frustum; this.capturedFrustum.prepare(d, e, g); this.captureFrustum = false; } profilerFiller.popPush("fog"); float h = gameRenderer.getRenderDistance(); boolean bl2 = this.minecraft.level.effects().isFoggyAt(Mth.floor(d), Mth.floor(e)) || this.minecraft.gui.getBossOverlay().shouldCreateWorldFog(); Vector4f vector4f = FogRenderer.computeFogColor( camera, f, this.minecraft.level, this.minecraft.options.getEffectiveRenderDistance(), gameRenderer.getDarkenWorldAmount(f) ); FogParameters fogParameters = FogRenderer.setupFog(camera, FogMode.FOG_TERRAIN, vector4f, h, bl2, f); FogParameters fogParameters2 = FogRenderer.setupFog(camera, FogMode.FOG_SKY, vector4f, h, bl2, f); profilerFiller.popPush("cullEntities"); boolean bl3 = this.collectVisibleEntities(camera, frustum, this.visibleEntities); this.visibleEntityCount = this.visibleEntities.size(); profilerFiller.popPush("terrain_setup"); this.setupRender(camera, frustum, bl, this.minecraft.player.isSpectator()); profilerFiller.popPush("compile_sections"); this.compileSections(camera); Matrix4fStack matrix4fStack = RenderSystem.getModelViewStack(); matrix4fStack.pushMatrix(); matrix4fStack.mul(frustumMatrix); FrameGraphBuilder frameGraphBuilder = new FrameGraphBuilder(); this.targets.main = frameGraphBuilder.importExternal("main", this.minecraft.getMainRenderTarget()); int i = this.minecraft.getMainRenderTarget().width; int j = this.minecraft.getMainRenderTarget().height; RenderTargetDescriptor renderTargetDescriptor = new RenderTargetDescriptor(i, j, true, 0); PostChain postChain = this.getTransparencyChain(); if (postChain != null) { this.targets.translucent = frameGraphBuilder.createInternal("translucent", renderTargetDescriptor); this.targets.itemEntity = frameGraphBuilder.createInternal("item_entity", renderTargetDescriptor); this.targets.particles = frameGraphBuilder.createInternal("particles", renderTargetDescriptor); this.targets.weather = frameGraphBuilder.createInternal("weather", renderTargetDescriptor); this.targets.clouds = frameGraphBuilder.createInternal("clouds", renderTargetDescriptor); } if (this.entityOutlineTarget != null) { this.targets.entityOutline = frameGraphBuilder.importExternal("entity_outline", this.entityOutlineTarget); } FramePass framePass = frameGraphBuilder.addPass("clear"); this.targets.main = framePass.readsAndWrites(this.targets.main); framePass.executes( () -> { RenderTarget renderTarget = this.minecraft.getMainRenderTarget(); RenderSystem.getDevice() .createCommandEncoder() .clearColorAndDepthTextures( renderTarget.getColorTexture(), ARGB.colorFromFloat(0.0F, vector4f.x, vector4f.y, vector4f.z), renderTarget.getDepthTexture(), 1.0 ); } ); if (!bl2) { this.addSkyPass(frameGraphBuilder, camera, f, fogParameters2); } this.addMainPass(frameGraphBuilder, frustum, camera, frustumMatrix, projectionMatrix, fogParameters, renderBlockOutline, bl3, deltaTracker, profilerFiller); PostChain postChain2 = this.minecraft.getShaderManager().getPostChain(ENTITY_OUTLINE_POST_CHAIN_ID, LevelTargetBundle.OUTLINE_TARGETS); if (bl3 && postChain2 != null) { postChain2.addToFrame(frameGraphBuilder, i, j, this.targets, null); } this.addParticlesPass(frameGraphBuilder, camera, f, fogParameters); CloudStatus cloudStatus = this.minecraft.options.getCloudsType(); if (cloudStatus != CloudStatus.OFF) { float k = this.level.effects().getCloudHeight(); if (!Float.isNaN(k)) { float l = this.ticks + f; int m = this.level.getCloudColor(f); this.addCloudsPass(frameGraphBuilder, cloudStatus, camera.getPosition(), l, m, k + 0.33F); } } this.addWeatherPass(frameGraphBuilder, camera.getPosition(), f, fogParameters); if (postChain != null) { postChain.addToFrame(frameGraphBuilder, i, j, this.targets, null); } this.addLateDebugPass(frameGraphBuilder, vec3, fogParameters); profilerFiller.popPush("framegraph"); frameGraphBuilder.execute(graphicsResourceAllocator, new Inspector() { @Override public void beforeExecutePass(String name) { profilerFiller.push(name); } @Override public void afterExecutePass(String name) { profilerFiller.pop(); } }); this.visibleEntities.clear(); this.targets.clear(); matrix4fStack.popMatrix(); RenderSystem.setShaderFog(FogParameters.NO_FOG); } private void addMainPass( FrameGraphBuilder frameGraphBuilder, Frustum frustum, Camera camera, Matrix4f frustumMatrix, Matrix4f projectionMatrix, FogParameters fogParameters, boolean renderBlockOutline, boolean renderEntityOutline, DeltaTracker deltaTracker, ProfilerFiller profiler ) { FramePass framePass = frameGraphBuilder.addPass("main"); this.targets.main = framePass.readsAndWrites(this.targets.main); if (this.targets.translucent != null) { this.targets.translucent = framePass.readsAndWrites(this.targets.translucent); } if (this.targets.itemEntity != null) { this.targets.itemEntity = framePass.readsAndWrites(this.targets.itemEntity); } if (this.targets.weather != null) { this.targets.weather = framePass.readsAndWrites(this.targets.weather); } if (renderEntityOutline && this.targets.entityOutline != null) { this.targets.entityOutline = framePass.readsAndWrites(this.targets.entityOutline); } ResourceHandle resourceHandle = this.targets.main; ResourceHandle resourceHandle2 = this.targets.translucent; ResourceHandle resourceHandle3 = this.targets.itemEntity; ResourceHandle resourceHandle4 = this.targets.entityOutline; framePass.executes(() -> { RenderSystem.setShaderFog(fogParameters); float f = deltaTracker.getGameTimeDeltaPartialTick(false); Vec3 vec3 = camera.getPosition(); double d = vec3.x(); double e = vec3.y(); double g = vec3.z(); profiler.push("terrain"); this.renderSectionLayer(RenderType.solid(), d, e, g, frustumMatrix, projectionMatrix); this.renderSectionLayer(RenderType.cutoutMipped(), d, e, g, frustumMatrix, projectionMatrix); this.renderSectionLayer(RenderType.cutout(), d, e, g, frustumMatrix, projectionMatrix); if (this.level.effects().constantAmbientLight()) { Lighting.setupNetherLevel(); } else { Lighting.setupLevel(); } if (resourceHandle3 != null) { resourceHandle3.get().copyDepthFrom(this.minecraft.getMainRenderTarget()); } if (this.shouldShowEntityOutlines() && resourceHandle4 != null) { RenderTarget renderTarget = resourceHandle4.get(); RenderSystem.getDevice().createCommandEncoder().clearColorAndDepthTextures(renderTarget.getColorTexture(), 0, renderTarget.getDepthTexture(), 1.0); } PoseStack poseStack = new PoseStack(); BufferSource bufferSource = this.renderBuffers.bufferSource(); BufferSource bufferSource2 = this.renderBuffers.crumblingBufferSource(); profiler.popPush("entities"); this.renderEntities(poseStack, bufferSource, camera, deltaTracker, this.visibleEntities); bufferSource.endLastBatch(); this.checkPoseStack(poseStack); profiler.popPush("blockentities"); this.renderBlockEntities(poseStack, bufferSource, bufferSource2, camera, f); bufferSource.endLastBatch(); this.checkPoseStack(poseStack); bufferSource.endBatch(RenderType.solid()); bufferSource.endBatch(RenderType.endPortal()); bufferSource.endBatch(RenderType.endGateway()); bufferSource.endBatch(Sheets.solidBlockSheet()); bufferSource.endBatch(Sheets.cutoutBlockSheet()); bufferSource.endBatch(Sheets.bedSheet()); bufferSource.endBatch(Sheets.shulkerBoxSheet()); bufferSource.endBatch(Sheets.signSheet()); bufferSource.endBatch(Sheets.hangingSignSheet()); bufferSource.endBatch(Sheets.chestSheet()); this.renderBuffers.outlineBufferSource().endOutlineBatch(); if (renderBlockOutline) { this.renderBlockOutline(camera, bufferSource, poseStack, false); } profiler.popPush("debug"); this.minecraft.debugRenderer.render(poseStack, frustum, bufferSource, d, e, g); bufferSource.endLastBatch(); this.checkPoseStack(poseStack); bufferSource.endBatch(Sheets.translucentItemSheet()); bufferSource.endBatch(Sheets.bannerSheet()); bufferSource.endBatch(Sheets.shieldSheet()); bufferSource.endBatch(RenderType.armorEntityGlint()); bufferSource.endBatch(RenderType.glint()); bufferSource.endBatch(RenderType.glintTranslucent()); bufferSource.endBatch(RenderType.entityGlint()); profiler.popPush("destroyProgress"); this.renderBlockDestroyAnimation(poseStack, camera, bufferSource2); bufferSource2.endBatch(); this.checkPoseStack(poseStack); bufferSource.endBatch(RenderType.waterMask()); bufferSource.endBatch(); if (resourceHandle2 != null) { resourceHandle2.get().copyDepthFrom(resourceHandle.get()); } profiler.popPush("translucent"); this.renderSectionLayer(RenderType.translucent(), d, e, g, frustumMatrix, projectionMatrix); profiler.popPush("string"); this.renderSectionLayer(RenderType.tripwire(), d, e, g, frustumMatrix, projectionMatrix); if (renderBlockOutline) { this.renderBlockOutline(camera, bufferSource, poseStack, true); } bufferSource.endBatch(); profiler.pop(); }); } private void addParticlesPass(FrameGraphBuilder frameGraphBuilder, Camera camera, float partialTick, FogParameters fog) { FramePass framePass = frameGraphBuilder.addPass("particles"); if (this.targets.particles != null) { this.targets.particles = framePass.readsAndWrites(this.targets.particles); framePass.reads(this.targets.main); } else { this.targets.main = framePass.readsAndWrites(this.targets.main); } ResourceHandle resourceHandle = this.targets.main; ResourceHandle resourceHandle2 = this.targets.particles; framePass.executes(() -> { RenderSystem.setShaderFog(fog); if (resourceHandle2 != null) { resourceHandle2.get().copyDepthFrom(resourceHandle.get()); } this.minecraft.particleEngine.render(camera, partialTick, this.renderBuffers.bufferSource()); }); } private void addCloudsPass(FrameGraphBuilder frameGraphBuilder, CloudStatus cloudStatus, Vec3 cameraPosition, float ticks, int cloudColor, float cloudHeight) { FramePass framePass = frameGraphBuilder.addPass("clouds"); if (this.targets.clouds != null) { this.targets.clouds = framePass.readsAndWrites(this.targets.clouds); } else { this.targets.main = framePass.readsAndWrites(this.targets.main); } framePass.executes(() -> this.cloudRenderer.render(cloudColor, cloudStatus, cloudHeight, cameraPosition, ticks)); } private void addWeatherPass(FrameGraphBuilder frameGraphBuilder, Vec3 cameraPosition, float partialTick, FogParameters fog) { int i = this.minecraft.options.getEffectiveRenderDistance() * 16; float f = this.minecraft.gameRenderer.getDepthFar(); FramePass framePass = frameGraphBuilder.addPass("weather"); if (this.targets.weather != null) { this.targets.weather = framePass.readsAndWrites(this.targets.weather); } else { this.targets.main = framePass.readsAndWrites(this.targets.main); } framePass.executes(() -> { RenderSystem.setShaderFog(fog); BufferSource bufferSource = this.renderBuffers.bufferSource(); this.weatherEffectRenderer.render(this.minecraft.level, bufferSource, this.ticks, partialTick, cameraPosition); this.worldBorderRenderer.render(this.level.getWorldBorder(), cameraPosition, i, f); bufferSource.endBatch(); }); } private void addLateDebugPass(FrameGraphBuilder frameGraphBuilder, Vec3 cameraPosition, FogParameters fog) { FramePass framePass = frameGraphBuilder.addPass("late_debug"); this.targets.main = framePass.readsAndWrites(this.targets.main); if (this.targets.itemEntity != null) { this.targets.itemEntity = framePass.readsAndWrites(this.targets.itemEntity); } ResourceHandle resourceHandle = this.targets.main; framePass.executes(() -> { RenderSystem.setShaderFog(fog); PoseStack poseStack = new PoseStack(); BufferSource bufferSource = this.renderBuffers.bufferSource(); this.minecraft.debugRenderer.renderAfterTranslucents(poseStack, bufferSource, cameraPosition.x, cameraPosition.y, cameraPosition.z); bufferSource.endLastBatch(); this.checkPoseStack(poseStack); }); } private boolean collectVisibleEntities(Camera camera, Frustum frustum, List output) { Vec3 vec3 = camera.getPosition(); double d = vec3.x(); double e = vec3.y(); double f = vec3.z(); boolean bl = false; boolean bl2 = this.shouldShowEntityOutlines(); Entity.setViewScale(Mth.clamp(this.minecraft.options.getEffectiveRenderDistance() / 8.0, 1.0, 2.5) * this.minecraft.options.entityDistanceScaling().get()); for (Entity entity : this.level.entitiesForRendering()) { if (this.entityRenderDispatcher.shouldRender(entity, frustum, d, e, f) || entity.hasIndirectPassenger(this.minecraft.player)) { BlockPos blockPos = entity.blockPosition(); if ((this.level.isOutsideBuildHeight(blockPos.getY()) || this.isSectionCompiled(blockPos)) && (entity != camera.getEntity() || camera.isDetached() || camera.getEntity() instanceof LivingEntity && ((LivingEntity)camera.getEntity()).isSleeping()) && (!(entity instanceof LocalPlayer) || camera.getEntity() == entity)) { output.add(entity); if (bl2 && this.minecraft.shouldEntityAppearGlowing(entity)) { bl = true; } } } } return bl; } private void renderEntities(PoseStack poseStack, BufferSource bufferSource, Camera camera, DeltaTracker deltaTracker, List entities) { Vec3 vec3 = camera.getPosition(); double d = vec3.x(); double e = vec3.y(); double f = vec3.z(); TickRateManager tickRateManager = this.minecraft.level.tickRateManager(); boolean bl = this.shouldShowEntityOutlines(); for (Entity entity : entities) { if (entity.tickCount == 0) { entity.xOld = entity.getX(); entity.yOld = entity.getY(); entity.zOld = entity.getZ(); } MultiBufferSource multiBufferSource; if (bl && this.minecraft.shouldEntityAppearGlowing(entity)) { OutlineBufferSource outlineBufferSource = this.renderBuffers.outlineBufferSource(); multiBufferSource = outlineBufferSource; int i = entity.getTeamColor(); outlineBufferSource.setColor(ARGB.red(i), ARGB.green(i), ARGB.blue(i), 255); } else { multiBufferSource = bufferSource; } float g = deltaTracker.getGameTimeDeltaPartialTick(!tickRateManager.isEntityFrozen(entity)); this.renderEntity(entity, d, e, f, g, poseStack, multiBufferSource); } } private void renderBlockEntities(PoseStack poseStack, BufferSource bufferSource, BufferSource crumblingBufferSource, Camera camera, float partialTick) { Vec3 vec3 = camera.getPosition(); double d = vec3.x(); double e = vec3.y(); double f = vec3.z(); for (SectionRenderDispatcher.RenderSection renderSection : this.visibleSections) { List list = renderSection.getCompiled().getRenderableBlockEntities(); if (!list.isEmpty()) { for (BlockEntity blockEntity : list) { BlockPos blockPos = blockEntity.getBlockPos(); MultiBufferSource multiBufferSource = bufferSource; poseStack.pushPose(); poseStack.translate(blockPos.getX() - d, blockPos.getY() - e, blockPos.getZ() - f); SortedSet sortedSet = this.destructionProgress.get(blockPos.asLong()); if (sortedSet != null && !sortedSet.isEmpty()) { int i = ((BlockDestructionProgress)sortedSet.last()).getProgress(); if (i >= 0) { PoseStack.Pose pose = poseStack.last(); VertexConsumer vertexConsumer = new SheetedDecalTextureGenerator( crumblingBufferSource.getBuffer((RenderType)ModelBakery.DESTROY_TYPES.get(i)), pose, 1.0F ); multiBufferSource = renderType -> { VertexConsumer vertexConsumer2 = bufferSource.getBuffer(renderType); return renderType.affectsCrumbling() ? VertexMultiConsumer.create(vertexConsumer, vertexConsumer2) : vertexConsumer2; }; } } this.blockEntityRenderDispatcher.render(blockEntity, partialTick, poseStack, multiBufferSource); poseStack.popPose(); } } } synchronized (this.globalBlockEntities) { for (BlockEntity blockEntity2 : this.globalBlockEntities) { BlockPos blockPos2 = blockEntity2.getBlockPos(); poseStack.pushPose(); poseStack.translate(blockPos2.getX() - d, blockPos2.getY() - e, blockPos2.getZ() - f); this.blockEntityRenderDispatcher.render(blockEntity2, partialTick, poseStack, bufferSource); poseStack.popPose(); } } } private void renderBlockDestroyAnimation(PoseStack poseStack, Camera camera, BufferSource bufferSource) { Vec3 vec3 = camera.getPosition(); double d = vec3.x(); double e = vec3.y(); double f = vec3.z(); for (Entry> entry : this.destructionProgress.long2ObjectEntrySet()) { BlockPos blockPos = BlockPos.of(entry.getLongKey()); if (!(blockPos.distToCenterSqr(d, e, f) > 1024.0)) { SortedSet sortedSet = (SortedSet)entry.getValue(); if (sortedSet != null && !sortedSet.isEmpty()) { int i = ((BlockDestructionProgress)sortedSet.last()).getProgress(); poseStack.pushPose(); poseStack.translate(blockPos.getX() - d, blockPos.getY() - e, blockPos.getZ() - f); PoseStack.Pose pose = poseStack.last(); VertexConsumer vertexConsumer = new SheetedDecalTextureGenerator(bufferSource.getBuffer((RenderType)ModelBakery.DESTROY_TYPES.get(i)), pose, 1.0F); this.minecraft.getBlockRenderer().renderBreakingTexture(this.level.getBlockState(blockPos), blockPos, this.level, poseStack, vertexConsumer); poseStack.popPose(); } } } } private void renderBlockOutline(Camera camera, BufferSource bufferSource, PoseStack poseStack, boolean sort) { if (this.minecraft.hitResult instanceof BlockHitResult blockHitResult) { if (blockHitResult.getType() != Type.MISS) { BlockPos blockPos = blockHitResult.getBlockPos(); BlockState blockState = this.level.getBlockState(blockPos); if (!blockState.isAir() && this.level.getWorldBorder().isWithinBounds(blockPos)) { boolean bl = ItemBlockRenderTypes.getChunkRenderType(blockState).sortOnUpload(); if (bl != sort) { return; } Vec3 vec3 = camera.getPosition(); Boolean boolean_ = this.minecraft.options.highContrastBlockOutline().get(); if (boolean_) { VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.secondaryBlockOutline()); this.renderHitOutline(poseStack, vertexConsumer, camera.getEntity(), vec3.x, vec3.y, vec3.z, blockPos, blockState, -16777216); } VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.lines()); int i = boolean_ ? -11010079 : ARGB.color(102, -16777216); this.renderHitOutline(poseStack, vertexConsumer, camera.getEntity(), vec3.x, vec3.y, vec3.z, blockPos, blockState, i); bufferSource.endLastBatch(); } } } } /** * Asserts that the specified {@code poseStack} is {@linkplain com.mojang.blaze3d.vertex.PoseStack#clear() clear}. * @throws java.lang.IllegalStateException if the specified {@code poseStack} is not clear */ private void checkPoseStack(PoseStack poseStack) { if (!poseStack.isEmpty()) { throw new IllegalStateException("Pose stack not empty"); } } private void renderEntity(Entity entity, double camX, double camY, double camZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource) { double d = Mth.lerp((double)partialTick, entity.xOld, entity.getX()); double e = Mth.lerp((double)partialTick, entity.yOld, entity.getY()); double f = Mth.lerp((double)partialTick, entity.zOld, entity.getZ()); this.entityRenderDispatcher .render(entity, d - camX, e - camY, f - camZ, partialTick, poseStack, bufferSource, this.entityRenderDispatcher.getPackedLightCoords(entity, partialTick)); } private void scheduleTranslucentSectionResort(Vec3 cameraPosition) { if (!this.visibleSections.isEmpty()) { BlockPos blockPos = BlockPos.containing(cameraPosition); boolean bl = !blockPos.equals(this.lastTranslucentSortBlockPos); Profiler.get().push("translucent_sort"); SectionRenderDispatcher.TranslucencyPointOfView translucencyPointOfView = new SectionRenderDispatcher.TranslucencyPointOfView(); for (SectionRenderDispatcher.RenderSection renderSection : this.nearbyVisibleSections) { this.scheduleResort(renderSection, translucencyPointOfView, cameraPosition, bl, true); } this.translucencyResortIterationIndex = this.translucencyResortIterationIndex % this.visibleSections.size(); int i = Math.max(this.visibleSections.size() / 8, 15); while (i-- > 0) { int j = this.translucencyResortIterationIndex++ % this.visibleSections.size(); this.scheduleResort(this.visibleSections.get(j), translucencyPointOfView, cameraPosition, bl, false); } this.lastTranslucentSortBlockPos = blockPos; Profiler.get().pop(); } } /** * @param force If {@code true}, will schedule a resort even if the point of view hasn't changed */ private void scheduleResort( SectionRenderDispatcher.RenderSection section, SectionRenderDispatcher.TranslucencyPointOfView pointOfView, Vec3 cameraPosition, boolean force, boolean ignoreAxisAlignment ) { pointOfView.set(cameraPosition, section.getSectionNode()); boolean bl = !pointOfView.equals(section.pointOfView.get()); boolean bl2 = force && (pointOfView.isAxisAligned() || ignoreAxisAlignment); if ((bl2 || bl) && !section.transparencyResortingScheduled() && section.hasTranslucentGeometry()) { section.resortTransparency(this.sectionRenderDispatcher); } } private void renderSectionLayer(RenderType renderType, double x, double y, double z, Matrix4f frustrumMatrix, Matrix4f projectionMatrix) { RenderSystem.assertOnRenderThread(); Zone zone = Profiler.get().zone((Supplier)(() -> "render_" + renderType.name)); zone.addText(renderType::toString); boolean bl = renderType != RenderType.translucent(); ObjectListIterator objectListIterator = this.visibleSections.listIterator(bl ? 0 : this.visibleSections.size()); renderType.setupRenderState(); RenderPipeline renderPipeline = renderType.getRenderPipeline(); ArrayList arrayList = new ArrayList(); RenderSystem.AutoStorageIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(renderType.mode()); int i = 0; while (bl ? objectListIterator.hasNext() : objectListIterator.hasPrevious()) { SectionRenderDispatcher.RenderSection renderSection = bl ? (SectionRenderDispatcher.RenderSection)objectListIterator.next() : objectListIterator.previous(); SectionRenderDispatcher.SectionBuffers sectionBuffers = renderSection.getBuffers(renderType); if (!renderSection.getCompiled().isEmpty(renderType) && sectionBuffers != null) { GpuBuffer gpuBuffer; VertexFormat.IndexType indexType; if (sectionBuffers.getIndexBuffer() == null) { if (sectionBuffers.getIndexCount() > i) { i = sectionBuffers.getIndexCount(); } gpuBuffer = null; indexType = null; } else { gpuBuffer = sectionBuffers.getIndexBuffer(); indexType = sectionBuffers.getIndexType(); } BlockPos blockPos = renderSection.getRenderOrigin(); arrayList.add( new RenderPass.Draw( 0, sectionBuffers.getVertexBuffer(), gpuBuffer, indexType, 0, sectionBuffers.getIndexCount(), uniformUploader -> uniformUploader.upload("ModelOffset", (float)(blockPos.getX() - x), (float)(blockPos.getY() - y), (float)(blockPos.getZ() - z)) ) ); } } GpuBuffer gpuBuffer2 = i == 0 ? null : autoStorageIndexBuffer.getBuffer(i); VertexFormat.IndexType indexType2 = i == 0 ? null : autoStorageIndexBuffer.type(); try (RenderPass renderPass = RenderSystem.getDevice() .createCommandEncoder() .createRenderPass( renderType.getRenderTarget().getColorTexture(), OptionalInt.empty(), renderType.getRenderTarget().getDepthTexture(), OptionalDouble.empty() )) { renderPass.setPipeline(renderPipeline); for (int j = 0; j < 12; j++) { GpuTexture gpuTexture = RenderSystem.getShaderTexture(j); if (gpuTexture != null) { renderPass.bindSampler("Sampler" + j, gpuTexture); } } renderPass.drawMultipleIndexed(arrayList, gpuBuffer2, indexType2); } zone.close(); renderType.clearRenderState(); } public void captureFrustum() { this.captureFrustum = true; } public void killFrustum() { this.capturedFrustum = null; } public void tick() { if (this.level.tickRateManager().runsNormally()) { this.ticks++; } if (this.ticks % 20 == 0) { Iterator iterator = this.destroyingBlocks.values().iterator(); while (iterator.hasNext()) { BlockDestructionProgress blockDestructionProgress = (BlockDestructionProgress)iterator.next(); int i = blockDestructionProgress.getUpdatedRenderTick(); if (this.ticks - i > 400) { iterator.remove(); this.removeProgress(blockDestructionProgress); } } } } private void removeProgress(BlockDestructionProgress progress) { long l = progress.getPos().asLong(); Set set = (Set)this.destructionProgress.get(l); set.remove(progress); if (set.isEmpty()) { this.destructionProgress.remove(l); } } private void addSkyPass(FrameGraphBuilder frameGraphBuilder, Camera camera, float partialTick, FogParameters fog) { FogType fogType = camera.getFluidInCamera(); if (fogType != FogType.POWDER_SNOW && fogType != FogType.LAVA && !this.doesMobEffectBlockSky(camera)) { DimensionSpecialEffects dimensionSpecialEffects = this.level.effects(); SkyType skyType = dimensionSpecialEffects.skyType(); if (skyType != SkyType.NONE) { FramePass framePass = frameGraphBuilder.addPass("sky"); this.targets.main = framePass.readsAndWrites(this.targets.main); framePass.executes(() -> { RenderSystem.setShaderFog(fog); if (skyType == SkyType.END) { this.skyRenderer.renderEndSky(); } else { PoseStack poseStack = new PoseStack(); float g = this.level.getSunAngle(partialTick); float h = this.level.getTimeOfDay(partialTick); float i = 1.0F - this.level.getRainLevel(partialTick); float j = this.level.getStarBrightness(partialTick) * i; int k = dimensionSpecialEffects.getSunriseOrSunsetColor(h); int l = this.level.getMoonPhase(); int m = this.level.getSkyColor(this.minecraft.gameRenderer.getMainCamera().getPosition(), partialTick); float n = ARGB.redFloat(m); float o = ARGB.greenFloat(m); float p = ARGB.blueFloat(m); this.skyRenderer.renderSkyDisc(n, o, p); BufferSource bufferSource = this.renderBuffers.bufferSource(); if (dimensionSpecialEffects.isSunriseOrSunset(h)) { this.skyRenderer.renderSunriseAndSunset(poseStack, bufferSource, g, k); } this.skyRenderer.renderSunMoonAndStars(poseStack, bufferSource, h, l, i, j, fog); bufferSource.endBatch(); if (this.shouldRenderDarkDisc(partialTick)) { this.skyRenderer.renderDarkDisc(); } } }); } } } private boolean shouldRenderDarkDisc(float partialTick) { return this.minecraft.player.getEyePosition(partialTick).y - this.level.getLevelData().getHorizonHeight(this.level) < 0.0; } private boolean doesMobEffectBlockSky(Camera camera) { return !(camera.getEntity() instanceof LivingEntity livingEntity) ? false : livingEntity.hasEffect(MobEffects.BLINDNESS) || livingEntity.hasEffect(MobEffects.DARKNESS); } private void compileSections(Camera camera) { ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("populate_sections_to_compile"); RenderRegionCache renderRegionCache = new RenderRegionCache(); BlockPos blockPos = camera.getBlockPosition(); List list = Lists.newArrayList(); for (SectionRenderDispatcher.RenderSection renderSection : this.visibleSections) { if (renderSection.isDirty() && renderSection.hasAllNeighbors()) { boolean bl = false; if (this.minecraft.options.prioritizeChunkUpdates().get() == PrioritizeChunkUpdates.NEARBY) { BlockPos blockPos2 = SectionPos.of(renderSection.getSectionNode()).center(); bl = blockPos2.distSqr(blockPos) < 768.0 || renderSection.isDirtyFromPlayer(); } else if (this.minecraft.options.prioritizeChunkUpdates().get() == PrioritizeChunkUpdates.PLAYER_AFFECTED) { bl = renderSection.isDirtyFromPlayer(); } if (bl) { profilerFiller.push("build_near_sync"); this.sectionRenderDispatcher.rebuildSectionSync(renderSection, renderRegionCache); renderSection.setNotDirty(); profilerFiller.pop(); } else { list.add(renderSection); } } } profilerFiller.popPush("upload"); this.sectionRenderDispatcher.uploadAllPendingUploads(); profilerFiller.popPush("schedule_async_compile"); for (SectionRenderDispatcher.RenderSection renderSectionx : list) { renderSectionx.rebuildSectionAsync(this.sectionRenderDispatcher, renderRegionCache); renderSectionx.setNotDirty(); } profilerFiller.pop(); this.scheduleTranslucentSectionResort(camera.getPosition()); } private void renderHitOutline( PoseStack poseStack, VertexConsumer buffer, Entity entity, double camX, double camY, double camZ, BlockPos pos, BlockState state, int color ) { ShapeRenderer.renderShape( poseStack, buffer, state.getShape(this.level, pos, CollisionContext.of(entity)), pos.getX() - camX, pos.getY() - camY, pos.getZ() - camZ, color ); } public void blockChanged(BlockGetter level, BlockPos pos, BlockState oldState, BlockState newState, int flags) { this.setBlockDirty(pos, (flags & 8) != 0); } private void setBlockDirty(BlockPos pos, boolean reRenderOnMainThread) { for (int i = pos.getZ() - 1; i <= pos.getZ() + 1; i++) { for (int j = pos.getX() - 1; j <= pos.getX() + 1; j++) { for (int k = pos.getY() - 1; k <= pos.getY() + 1; k++) { this.setSectionDirty(SectionPos.blockToSectionCoord(j), SectionPos.blockToSectionCoord(k), SectionPos.blockToSectionCoord(i), reRenderOnMainThread); } } } } /** * Re-renders all blocks in the specified range. */ public void setBlocksDirty(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { for (int i = minZ - 1; i <= maxZ + 1; i++) { for (int j = minX - 1; j <= maxX + 1; j++) { for (int k = minY - 1; k <= maxY + 1; k++) { this.setSectionDirty(SectionPos.blockToSectionCoord(j), SectionPos.blockToSectionCoord(k), SectionPos.blockToSectionCoord(i)); } } } } public void setBlockDirty(BlockPos pos, BlockState oldState, BlockState newState) { if (this.minecraft.getModelManager().requiresRender(oldState, newState)) { this.setBlocksDirty(pos.getX(), pos.getY(), pos.getZ(), pos.getX(), pos.getY(), pos.getZ()); } } public void setSectionDirtyWithNeighbors(int sectionX, int sectionY, int sectionZ) { this.setSectionRangeDirty(sectionX - 1, sectionY - 1, sectionZ - 1, sectionX + 1, sectionY + 1, sectionZ + 1); } public void setSectionRangeDirty(int minY, int minX, int minZ, int maxY, int maxX, int maxZ) { for (int i = minZ; i <= maxZ; i++) { for (int j = minY; j <= maxY; j++) { for (int k = minX; k <= maxX; k++) { this.setSectionDirty(j, k, i); } } } } public void setSectionDirty(int sectionX, int sectionY, int sectionZ) { this.setSectionDirty(sectionX, sectionY, sectionZ, false); } private void setSectionDirty(int sectionX, int sectionY, int sectionZ, boolean reRenderOnMainThread) { this.viewArea.setDirty(sectionX, sectionY, sectionZ, reRenderOnMainThread); } public void onSectionBecomingNonEmpty(long sectionPos) { SectionRenderDispatcher.RenderSection renderSection = this.viewArea.getRenderSection(sectionPos); if (renderSection != null) { this.sectionOcclusionGraph.schedulePropagationFrom(renderSection); } } /** * @param force if {@code true}, the particle will be created regardless of its distance from the camera and the {@linkplain #calculateParticleLevel(boolean) calculated particle level} */ public void addParticle(ParticleOptions options, boolean force, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { this.addParticle(options, force, false, x, y, z, xSpeed, ySpeed, zSpeed); } /** * @param force if {@code true}, the particle will be created regardless of its distance from the camera and the {@linkplain #calculateParticleLevel(boolean) calculated particle level} * @param decreased if {@code true}, and the {@linkplain net.minecraft.client.Options#particles particles option} is set to minimal, attempts to spawn the particle at a decreased level */ public void addParticle(ParticleOptions options, boolean force, boolean decreased, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { try { this.addParticleInternal(options, force, decreased, x, y, z, xSpeed, ySpeed, zSpeed); } catch (Throwable var19) { CrashReport crashReport = CrashReport.forThrowable(var19, "Exception while adding particle"); CrashReportCategory crashReportCategory = crashReport.addCategory("Particle being added"); crashReportCategory.setDetail("ID", BuiltInRegistries.PARTICLE_TYPE.getKey(options.getType())); crashReportCategory.setDetail( "Parameters", (CrashReportDetail)(() -> ParticleTypes.CODEC .encodeStart(this.level.registryAccess().createSerializationContext(NbtOps.INSTANCE), options) .toString()) ); crashReportCategory.setDetail("Position", (CrashReportDetail)(() -> CrashReportCategory.formatLocation(this.level, x, y, z))); throw new ReportedException(crashReport); } } public void addParticle(T options, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { this.addParticle(options, options.getType().getOverrideLimiter(), x, y, z, xSpeed, ySpeed, zSpeed); } /** * @param force if {@code true}, the particle will be created regardless of its distance from the camera and the {@linkplain #calculateParticleLevel(boolean) calculated particle level} */ @Nullable Particle addParticleInternal(ParticleOptions options, boolean force, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) { return this.addParticleInternal(options, force, false, x, y, z, xSpeed, ySpeed, zSpeed); } /** * @param force if {@code true}, the particle will be created regardless of its distance from the camera and the {@linkplain #calculateParticleLevel(boolean) calculated particle level} * @param decreased if {@code true}, and the {@linkplain net.minecraft.client.Options#particles particles option} is set to minimal, attempts to spawn the particle at a decreased level */ @Nullable private Particle addParticleInternal( ParticleOptions options, boolean force, boolean decreased, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed ) { Camera camera = this.minecraft.gameRenderer.getMainCamera(); ParticleStatus particleStatus = this.calculateParticleLevel(decreased); if (force) { return this.minecraft.particleEngine.createParticle(options, x, y, z, xSpeed, ySpeed, zSpeed); } else if (camera.getPosition().distanceToSqr(x, y, z) > 1024.0) { return null; } else { return particleStatus == ParticleStatus.MINIMAL ? null : this.minecraft.particleEngine.createParticle(options, x, y, z, xSpeed, ySpeed, zSpeed); } } private ParticleStatus calculateParticleLevel(boolean decreased) { ParticleStatus particleStatus = this.minecraft.options.particles().get(); if (decreased && particleStatus == ParticleStatus.MINIMAL && this.level.random.nextInt(10) == 0) { particleStatus = ParticleStatus.DECREASED; } if (particleStatus == ParticleStatus.DECREASED && this.level.random.nextInt(3) == 0) { particleStatus = ParticleStatus.MINIMAL; } return particleStatus; } public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) { if (progress >= 0 && progress < 10) { BlockDestructionProgress blockDestructionProgress = this.destroyingBlocks.get(breakerId); if (blockDestructionProgress != null) { this.removeProgress(blockDestructionProgress); } if (blockDestructionProgress == null || blockDestructionProgress.getPos().getX() != pos.getX() || blockDestructionProgress.getPos().getY() != pos.getY() || blockDestructionProgress.getPos().getZ() != pos.getZ()) { blockDestructionProgress = new BlockDestructionProgress(breakerId, pos); this.destroyingBlocks.put(breakerId, blockDestructionProgress); } blockDestructionProgress.setProgress(progress); blockDestructionProgress.updateTick(this.ticks); this.destructionProgress .computeIfAbsent( blockDestructionProgress.getPos().asLong(), (Long2ObjectFunction>)(l -> Sets.newTreeSet()) ) .add(blockDestructionProgress); } else { BlockDestructionProgress blockDestructionProgressx = this.destroyingBlocks.remove(breakerId); if (blockDestructionProgressx != null) { this.removeProgress(blockDestructionProgressx); } } } public boolean hasRenderedAllSections() { return this.sectionRenderDispatcher.isQueueEmpty(); } public void onChunkReadyToRender(ChunkPos chunkPos) { this.sectionOcclusionGraph.onChunkReadyToRender(chunkPos); } public void needsUpdate() { this.sectionOcclusionGraph.invalidate(); this.cloudRenderer.markForRebuild(); } public void updateGlobalBlockEntities(Collection blockEntitiesToRemove, Collection blockEntitiesToAdd) { synchronized (this.globalBlockEntities) { this.globalBlockEntities.removeAll(blockEntitiesToRemove); this.globalBlockEntities.addAll(blockEntitiesToAdd); } } public static int getLightColor(BlockAndTintGetter level, BlockPos pos) { return getLightColor(LevelRenderer.BrightnessGetter.DEFAULT, level, level.getBlockState(pos), pos); } public static int getLightColor(LevelRenderer.BrightnessGetter brightnessGetter, BlockAndTintGetter level, BlockState state, BlockPos pos) { if (state.emissiveRendering(level, pos)) { return 15728880; } else { int i = brightnessGetter.packedBrightness(level, pos); int j = LightTexture.block(i); int k = state.getLightEmission(); if (j < k) { int l = LightTexture.sky(i); return LightTexture.pack(k, l); } else { return i; } } } public boolean isSectionCompiled(BlockPos pos) { SectionRenderDispatcher.RenderSection renderSection = this.viewArea.getRenderSectionAt(pos); return renderSection != null && renderSection.compiled.get() != SectionRenderDispatcher.CompiledSection.UNCOMPILED; } @Nullable public RenderTarget entityOutlineTarget() { return this.targets.entityOutline != null ? this.targets.entityOutline.get() : null; } @Nullable public RenderTarget getTranslucentTarget() { return this.targets.translucent != null ? this.targets.translucent.get() : null; } @Nullable public RenderTarget getItemEntityTarget() { return this.targets.itemEntity != null ? this.targets.itemEntity.get() : null; } @Nullable public RenderTarget getParticlesTarget() { return this.targets.particles != null ? this.targets.particles.get() : null; } @Nullable public RenderTarget getWeatherTarget() { return this.targets.weather != null ? this.targets.weather.get() : null; } @Nullable public RenderTarget getCloudsTarget() { return this.targets.clouds != null ? this.targets.clouds.get() : null; } @VisibleForDebug public ObjectArrayList getVisibleSections() { return this.visibleSections; } @VisibleForDebug public SectionOcclusionGraph getSectionOcclusionGraph() { return this.sectionOcclusionGraph; } @Nullable public Frustum getCapturedFrustum() { return this.capturedFrustum; } public CloudRenderer getCloudRenderer() { return this.cloudRenderer; } @FunctionalInterface @Environment(EnvType.CLIENT) public interface BrightnessGetter { LevelRenderer.BrightnessGetter DEFAULT = (blockAndTintGetter, blockPos) -> { int i = blockAndTintGetter.getBrightness(LightLayer.SKY, blockPos); int j = blockAndTintGetter.getBrightness(LightLayer.BLOCK, blockPos); return Brightness.pack(j, i); }; int packedBrightness(BlockAndTintGetter blockAndTintGetter, BlockPos blockPos); } }