minecraft-src/net/minecraft/client/renderer/GameRenderer.java
2025-09-18 12:27:44 +00:00

802 lines
31 KiB
Java

package net.minecraft.client.renderer;
import com.mojang.blaze3d.ProjectionType;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.resource.CrossFrameResourcePool;
import com.mojang.blaze3d.shaders.ShaderType;
import com.mojang.blaze3d.systems.GpuDevice;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.jtracy.TracyClient;
import com.mojang.logging.LogUtils;
import com.mojang.math.Axis;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.List;
import java.util.function.BiFunction;
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.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
import net.minecraft.client.Screenshot;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.render.GuiRenderer;
import net.minecraft.client.gui.render.pip.GuiBannerResultRenderer;
import net.minecraft.client.gui.render.pip.GuiBookModelRenderer;
import net.minecraft.client.gui.render.pip.GuiEntityRenderer;
import net.minecraft.client.gui.render.pip.GuiProfilerChartRenderer;
import net.minecraft.client.gui.render.pip.GuiSignRenderer;
import net.minecraft.client.gui.render.pip.GuiSkinRenderer;
import net.minecraft.client.gui.render.state.GuiRenderState;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.fog.FogRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.server.IntegratedServer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceProvider;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.util.profiling.Zone;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.monster.EnderMan;
import net.minecraft.world.entity.monster.Spider;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import net.minecraft.world.level.material.FogType;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.waypoints.TrackedWaypoint;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.joml.Vector4f;
import org.slf4j.Logger;
@Environment(EnvType.CLIENT)
public class GameRenderer implements TrackedWaypoint.Projector, AutoCloseable {
private static final ResourceLocation BLUR_POST_CHAIN_ID = ResourceLocation.withDefaultNamespace("blur");
public static final int MAX_BLUR_RADIUS = 10;
private static final Logger LOGGER = LogUtils.getLogger();
public static final float PROJECTION_Z_NEAR = 0.05F;
public static final float PROJECTION_3D_HUD_Z_FAR = 100.0F;
private static final float PORTAL_SPINNING_SPEED = 20.0F;
private static final float NAUSEA_SPINNING_SPEED = 7.0F;
private final Minecraft minecraft;
private final RandomSource random = RandomSource.create();
private float renderDistance;
public final ItemInHandRenderer itemInHandRenderer;
private final ScreenEffectRenderer screenEffectRenderer;
private final RenderBuffers renderBuffers;
private float spinningEffectTime;
private float spinningEffectSpeed;
private float fovModifier;
private float oldFovModifier;
private float darkenWorldAmount;
private float darkenWorldAmountO;
private boolean renderBlockOutline = true;
private long lastScreenshotAttempt;
private boolean hasWorldScreenshot;
private long lastActiveTime = Util.getMillis();
private final LightTexture lightTexture;
private final OverlayTexture overlayTexture = new OverlayTexture();
private boolean panoramicMode;
protected final CubeMap cubeMap = new CubeMap(ResourceLocation.withDefaultNamespace("textures/gui/title/background/panorama"));
protected final PanoramaRenderer panorama = new PanoramaRenderer(this.cubeMap);
private final CrossFrameResourcePool resourcePool = new CrossFrameResourcePool(3);
private final FogRenderer fogRenderer = new FogRenderer();
private final GuiRenderer guiRenderer;
private final GuiRenderState guiRenderState;
@Nullable
private ResourceLocation postEffectId;
private boolean effectActive;
private final Camera mainCamera = new Camera();
private final Lighting lighting = new Lighting();
private final GlobalSettingsUniform globalSettingsUniform = new GlobalSettingsUniform();
private final PerspectiveProjectionMatrixBuffer levelProjectionMatrixBuffer = new PerspectiveProjectionMatrixBuffer("level");
private final CachedPerspectiveProjectionMatrixBuffer hud3dProjectionMatrixBuffer = new CachedPerspectiveProjectionMatrixBuffer("3d hud", 0.05F, 100.0F);
public GameRenderer(Minecraft minecraft, ItemInHandRenderer itemInHandRenderer, RenderBuffers renderBuffers) {
this.minecraft = minecraft;
this.itemInHandRenderer = itemInHandRenderer;
this.lightTexture = new LightTexture(this, minecraft);
this.renderBuffers = renderBuffers;
this.guiRenderState = new GuiRenderState();
MultiBufferSource.BufferSource bufferSource = renderBuffers.bufferSource();
this.guiRenderer = new GuiRenderer(
this.guiRenderState,
bufferSource,
List.of(
new GuiEntityRenderer(bufferSource, minecraft.getEntityRenderDispatcher()),
new GuiSkinRenderer(bufferSource),
new GuiBookModelRenderer(bufferSource),
new GuiBannerResultRenderer(bufferSource),
new GuiSignRenderer(bufferSource),
new GuiProfilerChartRenderer(bufferSource)
)
);
this.screenEffectRenderer = new ScreenEffectRenderer(minecraft, bufferSource);
}
public void close() {
this.globalSettingsUniform.close();
this.lightTexture.close();
this.overlayTexture.close();
this.resourcePool.close();
this.guiRenderer.close();
this.levelProjectionMatrixBuffer.close();
this.hud3dProjectionMatrixBuffer.close();
this.lighting.close();
this.cubeMap.close();
this.fogRenderer.close();
}
public void setRenderBlockOutline(boolean renderBlockOutline) {
this.renderBlockOutline = renderBlockOutline;
}
public void setPanoramicMode(boolean panoramicMode) {
this.panoramicMode = panoramicMode;
}
public boolean isPanoramicMode() {
return this.panoramicMode;
}
public void clearPostEffect() {
this.postEffectId = null;
}
public void togglePostEffect() {
this.effectActive = !this.effectActive;
}
/**
* What shader to use when spectating this entity
*/
public void checkEntityPostEffect(@Nullable Entity entity) {
this.postEffectId = null;
if (entity instanceof Creeper) {
this.setPostEffect(ResourceLocation.withDefaultNamespace("creeper"));
} else if (entity instanceof Spider) {
this.setPostEffect(ResourceLocation.withDefaultNamespace("spider"));
} else if (entity instanceof EnderMan) {
this.setPostEffect(ResourceLocation.withDefaultNamespace("invert"));
}
}
private void setPostEffect(ResourceLocation postEffectId) {
this.postEffectId = postEffectId;
this.effectActive = true;
}
public void processBlurEffect() {
PostChain postChain = this.minecraft.getShaderManager().getPostChain(BLUR_POST_CHAIN_ID, LevelTargetBundle.MAIN_TARGETS);
if (postChain != null) {
postChain.process(this.minecraft.getMainRenderTarget(), this.resourcePool);
}
}
public void preloadUiShader(ResourceProvider resourceProvider) {
GpuDevice gpuDevice = RenderSystem.getDevice();
BiFunction<ResourceLocation, ShaderType, String> biFunction = (resourceLocation, shaderType) -> {
ResourceLocation resourceLocation2 = shaderType.idConverter().idToFile(resourceLocation);
try {
Reader reader = resourceProvider.getResourceOrThrow(resourceLocation2).openAsReader();
String var5;
try {
var5 = IOUtils.toString(reader);
} catch (Throwable var8) {
if (reader != null) {
try {
reader.close();
} catch (Throwable var7) {
var8.addSuppressed(var7);
}
}
throw var8;
}
if (reader != null) {
reader.close();
}
return var5;
} catch (IOException var9) {
LOGGER.error("Coudln't preload {} shader {}: {}", shaderType, resourceLocation, var9);
return null;
}
};
gpuDevice.precompilePipeline(RenderPipelines.GUI, biFunction);
gpuDevice.precompilePipeline(RenderPipelines.GUI_TEXTURED, biFunction);
if (TracyClient.isAvailable()) {
gpuDevice.precompilePipeline(RenderPipelines.TRACY_BLIT, biFunction);
}
}
/**
* Updates the entity renderer
*/
public void tick() {
this.tickFov();
this.lightTexture.tick();
LocalPlayer localPlayer = this.minecraft.player;
if (this.minecraft.getCameraEntity() == null) {
this.minecraft.setCameraEntity(localPlayer);
}
this.mainCamera.tick();
this.itemInHandRenderer.tick();
float f = localPlayer.portalEffectIntensity;
float g = localPlayer.getEffectBlendFactor(MobEffects.NAUSEA, 1.0F);
if (!(f > 0.0F) && !(g > 0.0F)) {
this.spinningEffectSpeed = 0.0F;
} else {
this.spinningEffectSpeed = (f * 20.0F + g * 7.0F) / (f + g);
this.spinningEffectTime = this.spinningEffectTime + this.spinningEffectSpeed;
}
if (this.minecraft.level.tickRateManager().runsNormally()) {
this.minecraft.levelRenderer.tickParticles(this.mainCamera);
this.darkenWorldAmountO = this.darkenWorldAmount;
if (this.minecraft.gui.getBossOverlay().shouldDarkenScreen()) {
this.darkenWorldAmount += 0.05F;
if (this.darkenWorldAmount > 1.0F) {
this.darkenWorldAmount = 1.0F;
}
} else if (this.darkenWorldAmount > 0.0F) {
this.darkenWorldAmount -= 0.0125F;
}
this.screenEffectRenderer.tick();
}
}
@Nullable
public ResourceLocation currentPostEffect() {
return this.postEffectId;
}
public void resize(int width, int height) {
this.resourcePool.clear();
this.minecraft.levelRenderer.resize(width, height);
}
/**
* Gets the block or object that is being moused over.
*/
public void pick(float partialTicks) {
Entity entity = this.minecraft.getCameraEntity();
if (entity != null) {
if (this.minecraft.level != null && this.minecraft.player != null) {
Profiler.get().push("pick");
double d = this.minecraft.player.blockInteractionRange();
double e = this.minecraft.player.entityInteractionRange();
HitResult hitResult = this.pick(entity, d, e, partialTicks);
this.minecraft.hitResult = hitResult;
this.minecraft.crosshairPickEntity = hitResult instanceof EntityHitResult entityHitResult ? entityHitResult.getEntity() : null;
Profiler.get().pop();
}
}
}
private HitResult pick(Entity entity, double blockInteractionRange, double entityInteractionRange, float partialTick) {
double d = Math.max(blockInteractionRange, entityInteractionRange);
double e = Mth.square(d);
Vec3 vec3 = entity.getEyePosition(partialTick);
HitResult hitResult = entity.pick(d, partialTick, false);
double f = hitResult.getLocation().distanceToSqr(vec3);
if (hitResult.getType() != HitResult.Type.MISS) {
e = f;
d = Math.sqrt(f);
}
Vec3 vec32 = entity.getViewVector(partialTick);
Vec3 vec33 = vec3.add(vec32.x * d, vec32.y * d, vec32.z * d);
float g = 1.0F;
AABB aABB = entity.getBoundingBox().expandTowards(vec32.scale(d)).inflate(1.0, 1.0, 1.0);
EntityHitResult entityHitResult = ProjectileUtil.getEntityHitResult(entity, vec3, vec33, aABB, EntitySelector.CAN_BE_PICKED, e);
return entityHitResult != null && entityHitResult.getLocation().distanceToSqr(vec3) < f
? filterHitResult(entityHitResult, vec3, entityInteractionRange)
: filterHitResult(hitResult, vec3, blockInteractionRange);
}
private static HitResult filterHitResult(HitResult hitResult, Vec3 pos, double blockInteractionRange) {
Vec3 vec3 = hitResult.getLocation();
if (!vec3.closerThan(pos, blockInteractionRange)) {
Vec3 vec32 = hitResult.getLocation();
Direction direction = Direction.getApproximateNearest(vec32.x - pos.x, vec32.y - pos.y, vec32.z - pos.z);
return BlockHitResult.miss(vec32, direction, BlockPos.containing(vec32));
} else {
return hitResult;
}
}
/**
* Update FOV modifier hand
*/
private void tickFov() {
float g;
if (this.minecraft.getCameraEntity() instanceof AbstractClientPlayer abstractClientPlayer) {
Options options = this.minecraft.options;
boolean bl = options.getCameraType().isFirstPerson();
float f = options.fovEffectScale().get().floatValue();
g = abstractClientPlayer.getFieldOfViewModifier(bl, f);
} else {
g = 1.0F;
}
this.oldFovModifier = this.fovModifier;
this.fovModifier = this.fovModifier + (g - this.fovModifier) * 0.5F;
this.fovModifier = Mth.clamp(this.fovModifier, 0.1F, 1.5F);
}
private float getFov(Camera camera, float partialTick, boolean useFovSetting) {
if (this.panoramicMode) {
return 90.0F;
} else {
float f = 70.0F;
if (useFovSetting) {
f = this.minecraft.options.fov().get().intValue();
f *= Mth.lerp(partialTick, this.oldFovModifier, this.fovModifier);
}
if (camera.getEntity() instanceof LivingEntity livingEntity && livingEntity.isDeadOrDying()) {
float g = Math.min(livingEntity.deathTime + partialTick, 20.0F);
f /= (1.0F - 500.0F / (g + 500.0F)) * 2.0F + 1.0F;
}
FogType fogType = camera.getFluidInCamera();
if (fogType == FogType.LAVA || fogType == FogType.WATER) {
float g = this.minecraft.options.fovEffectScale().get().floatValue();
f *= Mth.lerp(g, 1.0F, 0.85714287F);
}
return f;
}
}
private void bobHurt(PoseStack poseStack, float partialTicks) {
if (this.minecraft.getCameraEntity() instanceof LivingEntity livingEntity) {
float f = livingEntity.hurtTime - partialTicks;
if (livingEntity.isDeadOrDying()) {
float g = Math.min(livingEntity.deathTime + partialTicks, 20.0F);
poseStack.mulPose(Axis.ZP.rotationDegrees(40.0F - 8000.0F / (g + 200.0F)));
}
if (f < 0.0F) {
return;
}
f /= livingEntity.hurtDuration;
f = Mth.sin(f * f * f * f * (float) Math.PI);
float g = livingEntity.getHurtDir();
poseStack.mulPose(Axis.YP.rotationDegrees(-g));
float h = (float)(-f * 14.0 * this.minecraft.options.damageTiltStrength().get());
poseStack.mulPose(Axis.ZP.rotationDegrees(h));
poseStack.mulPose(Axis.YP.rotationDegrees(g));
}
}
private void bobView(PoseStack poseStack, float partialTicks) {
if (this.minecraft.getCameraEntity() instanceof AbstractClientPlayer abstractClientPlayer) {
float var7 = abstractClientPlayer.walkDist - abstractClientPlayer.walkDistO;
float g = -(abstractClientPlayer.walkDist + var7 * partialTicks);
float h = Mth.lerp(partialTicks, abstractClientPlayer.oBob, abstractClientPlayer.bob);
poseStack.translate(Mth.sin(g * (float) Math.PI) * h * 0.5F, -Math.abs(Mth.cos(g * (float) Math.PI) * h), 0.0F);
poseStack.mulPose(Axis.ZP.rotationDegrees(Mth.sin(g * (float) Math.PI) * h * 3.0F));
poseStack.mulPose(Axis.XP.rotationDegrees(Math.abs(Mth.cos(g * (float) Math.PI - 0.2F) * h) * 5.0F));
}
}
private void renderItemInHand(float partialTick, boolean sleeping, Matrix4f projectionMatrix) {
if (!this.panoramicMode) {
PoseStack poseStack = new PoseStack();
poseStack.pushPose();
poseStack.mulPose(projectionMatrix.invert(new Matrix4f()));
Matrix4fStack matrix4fStack = RenderSystem.getModelViewStack();
matrix4fStack.pushMatrix().mul(projectionMatrix);
this.bobHurt(poseStack, partialTick);
if (this.minecraft.options.bobView().get()) {
this.bobView(poseStack, partialTick);
}
if (this.minecraft.options.getCameraType().isFirstPerson()
&& !sleeping
&& !this.minecraft.options.hideGui
&& this.minecraft.gameMode.getPlayerMode() != GameType.SPECTATOR) {
this.lightTexture.turnOnLightLayer();
this.itemInHandRenderer
.renderHandsWithItems(
partialTick,
poseStack,
this.renderBuffers.bufferSource(),
this.minecraft.player,
this.minecraft.getEntityRenderDispatcher().getPackedLightCoords(this.minecraft.player, partialTick)
);
this.lightTexture.turnOffLightLayer();
}
matrix4fStack.popMatrix();
poseStack.popPose();
}
}
public Matrix4f getProjectionMatrix(float fov) {
Matrix4f matrix4f = new Matrix4f();
return matrix4f.perspective(
fov * (float) (Math.PI / 180.0), (float)this.minecraft.getWindow().getWidth() / this.minecraft.getWindow().getHeight(), 0.05F, this.getDepthFar()
);
}
public float getDepthFar() {
return Math.max(this.renderDistance * 4.0F, this.minecraft.options.cloudRange().get() * 16);
}
public static float getNightVisionScale(LivingEntity livingEntity, float nanoTime) {
MobEffectInstance mobEffectInstance = livingEntity.getEffect(MobEffects.NIGHT_VISION);
return !mobEffectInstance.endsWithin(200) ? 1.0F : 0.7F + Mth.sin((mobEffectInstance.getDuration() - nanoTime) * (float) Math.PI * 0.2F) * 0.3F;
}
public void render(DeltaTracker deltaTracker, boolean renderLevel) {
if (!this.minecraft.isWindowActive()
&& this.minecraft.options.pauseOnLostFocus
&& (!this.minecraft.options.touchscreen().get() || !this.minecraft.mouseHandler.isRightPressed())) {
if (Util.getMillis() - this.lastActiveTime > 500L) {
this.minecraft.pauseGame(false);
}
} else {
this.lastActiveTime = Util.getMillis();
}
if (!this.minecraft.noRender) {
this.globalSettingsUniform
.update(
this.minecraft.getWindow().getWidth(),
this.minecraft.getWindow().getHeight(),
this.minecraft.options.glintStrength().get(),
this.minecraft.level == null ? 0L : this.minecraft.level.getGameTime(),
deltaTracker,
this.minecraft.options.getMenuBackgroundBlurriness()
);
ProfilerFiller profilerFiller = Profiler.get();
boolean bl = this.minecraft.isGameLoadFinished();
int i = (int)this.minecraft.mouseHandler.getScaledXPos(this.minecraft.getWindow());
int j = (int)this.minecraft.mouseHandler.getScaledYPos(this.minecraft.getWindow());
if (bl && renderLevel && this.minecraft.level != null) {
profilerFiller.push("world");
this.renderLevel(deltaTracker);
this.tryTakeScreenshotIfNeeded();
this.minecraft.levelRenderer.doEntityOutline();
if (this.postEffectId != null && this.effectActive) {
RenderSystem.resetTextureMatrix();
PostChain postChain = this.minecraft.getShaderManager().getPostChain(this.postEffectId, LevelTargetBundle.MAIN_TARGETS);
if (postChain != null) {
postChain.process(this.minecraft.getMainRenderTarget(), this.resourcePool);
}
}
}
this.fogRenderer.endFrame();
RenderTarget renderTarget = this.minecraft.getMainRenderTarget();
RenderSystem.getDevice().createCommandEncoder().clearDepthTexture(renderTarget.getDepthTexture(), 1.0);
this.minecraft.gameRenderer.getLighting().setupFor(Lighting.Entry.ITEMS_3D);
this.guiRenderState.reset();
GuiGraphics guiGraphics = new GuiGraphics(this.minecraft, this.guiRenderState);
if (bl && renderLevel && this.minecraft.level != null) {
profilerFiller.popPush("gui");
this.minecraft.gui.render(guiGraphics, deltaTracker);
profilerFiller.pop();
}
if (this.minecraft.getOverlay() != null) {
try {
this.minecraft.getOverlay().render(guiGraphics, i, j, deltaTracker.getGameTimeDeltaTicks());
} catch (Throwable var15) {
CrashReport crashReport = CrashReport.forThrowable(var15, "Rendering overlay");
CrashReportCategory crashReportCategory = crashReport.addCategory("Overlay render details");
crashReportCategory.setDetail("Overlay name", (CrashReportDetail<String>)(() -> this.minecraft.getOverlay().getClass().getCanonicalName()));
throw new ReportedException(crashReport);
}
} else if (bl && this.minecraft.screen != null) {
try {
this.minecraft.screen.renderWithTooltip(guiGraphics, i, j, deltaTracker.getGameTimeDeltaTicks());
} catch (Throwable var14) {
CrashReport crashReport = CrashReport.forThrowable(var14, "Rendering screen");
CrashReportCategory crashReportCategory = crashReport.addCategory("Screen render details");
crashReportCategory.setDetail("Screen name", (CrashReportDetail<String>)(() -> this.minecraft.screen.getClass().getCanonicalName()));
this.minecraft.mouseHandler.fillMousePositionDetails(crashReportCategory, this.minecraft.getWindow());
throw new ReportedException(crashReport);
}
try {
if (this.minecraft.screen != null) {
this.minecraft.screen.handleDelayedNarration();
}
} catch (Throwable var13) {
CrashReport crashReport = CrashReport.forThrowable(var13, "Narrating screen");
CrashReportCategory crashReportCategory = crashReport.addCategory("Screen details");
crashReportCategory.setDetail("Screen name", (CrashReportDetail<String>)(() -> this.minecraft.screen.getClass().getCanonicalName()));
throw new ReportedException(crashReport);
}
}
if (bl && renderLevel && this.minecraft.level != null) {
this.minecraft.gui.renderSavingIndicator(guiGraphics, deltaTracker);
}
if (bl) {
try (Zone zone = profilerFiller.zone("toasts")) {
this.minecraft.getToastManager().render(guiGraphics);
}
}
this.guiRenderer.render(this.fogRenderer.getBuffer(FogRenderer.FogMode.NONE));
this.guiRenderer.incrementFrameNumber();
this.resourcePool.endFrame();
}
}
private void tryTakeScreenshotIfNeeded() {
if (!this.hasWorldScreenshot && this.minecraft.isLocalServer()) {
long l = Util.getMillis();
if (l - this.lastScreenshotAttempt >= 1000L) {
this.lastScreenshotAttempt = l;
IntegratedServer integratedServer = this.minecraft.getSingleplayerServer();
if (integratedServer != null && !integratedServer.isStopped()) {
integratedServer.getWorldScreenshotFile().ifPresent(path -> {
if (Files.isRegularFile(path, new LinkOption[0])) {
this.hasWorldScreenshot = true;
} else {
this.takeAutoScreenshot(path);
}
});
}
}
}
}
private void takeAutoScreenshot(Path path) {
if (this.minecraft.levelRenderer.countRenderedSections() > 10 && this.minecraft.levelRenderer.hasRenderedAllSections()) {
Screenshot.takeScreenshot(this.minecraft.getMainRenderTarget(), nativeImage -> Util.ioPool().execute(() -> {
int i = nativeImage.getWidth();
int j = nativeImage.getHeight();
int k = 0;
int l = 0;
if (i > j) {
k = (i - j) / 2;
i = j;
} else {
l = (j - i) / 2;
j = i;
}
try (NativeImage nativeImage2 = new NativeImage(64, 64, false)) {
nativeImage.resizeSubRectTo(k, l, i, j, nativeImage2);
nativeImage2.writeToFile(path);
} catch (IOException var16) {
LOGGER.warn("Couldn't save auto screenshot", (Throwable)var16);
} finally {
nativeImage.close();
}
}));
}
}
private boolean shouldRenderBlockOutline() {
if (!this.renderBlockOutline) {
return false;
} else {
Entity entity = this.minecraft.getCameraEntity();
boolean bl = entity instanceof Player && !this.minecraft.options.hideGui;
if (bl && !((Player)entity).getAbilities().mayBuild) {
ItemStack itemStack = ((LivingEntity)entity).getMainHandItem();
HitResult hitResult = this.minecraft.hitResult;
if (hitResult != null && hitResult.getType() == HitResult.Type.BLOCK) {
BlockPos blockPos = ((BlockHitResult)hitResult).getBlockPos();
BlockState blockState = this.minecraft.level.getBlockState(blockPos);
if (this.minecraft.gameMode.getPlayerMode() == GameType.SPECTATOR) {
bl = blockState.getMenuProvider(this.minecraft.level, blockPos) != null;
} else {
BlockInWorld blockInWorld = new BlockInWorld(this.minecraft.level, blockPos, false);
Registry<Block> registry = this.minecraft.level.registryAccess().lookupOrThrow(Registries.BLOCK);
bl = !itemStack.isEmpty() && (itemStack.canBreakBlockInAdventureMode(blockInWorld) || itemStack.canPlaceOnBlockInAdventureMode(blockInWorld));
}
}
}
return bl;
}
}
public void renderLevel(DeltaTracker deltaTracker) {
float f = deltaTracker.getGameTimeDeltaPartialTick(true);
LocalPlayer localPlayer = this.minecraft.player;
this.lightTexture.updateLightTexture(f);
if (this.minecraft.getCameraEntity() == null) {
this.minecraft.setCameraEntity(localPlayer);
}
this.pick(f);
ProfilerFiller profilerFiller = Profiler.get();
profilerFiller.push("center");
boolean bl = this.shouldRenderBlockOutline();
profilerFiller.popPush("camera");
Camera camera = this.mainCamera;
Entity entity = (Entity)(this.minecraft.getCameraEntity() == null ? localPlayer : this.minecraft.getCameraEntity());
float g = this.minecraft.level.tickRateManager().isEntityFrozen(entity) ? 1.0F : f;
camera.setup(this.minecraft.level, entity, !this.minecraft.options.getCameraType().isFirstPerson(), this.minecraft.options.getCameraType().isMirrored(), g);
this.renderDistance = this.minecraft.options.getEffectiveRenderDistance() * 16;
float h = this.getFov(camera, f, true);
Matrix4f matrix4f = this.getProjectionMatrix(h);
PoseStack poseStack = new PoseStack();
this.bobHurt(poseStack, camera.getPartialTickTime());
if (this.minecraft.options.bobView().get()) {
this.bobView(poseStack, camera.getPartialTickTime());
}
matrix4f.mul(poseStack.last().pose());
float i = this.minecraft.options.screenEffectScale().get().floatValue();
float j = Mth.lerp(f, localPlayer.oPortalEffectIntensity, localPlayer.portalEffectIntensity);
float k = localPlayer.getEffectBlendFactor(MobEffects.NAUSEA, f);
float l = Math.max(j, k) * (i * i);
if (l > 0.0F) {
float m = 5.0F / (l * l + 5.0F) - l * 0.04F;
m *= m;
Vector3f vector3f = new Vector3f(0.0F, Mth.SQRT_OF_TWO / 2.0F, Mth.SQRT_OF_TWO / 2.0F);
float n = (this.spinningEffectTime + f * this.spinningEffectSpeed) * (float) (Math.PI / 180.0);
matrix4f.rotate(n, vector3f);
matrix4f.scale(1.0F / m, 1.0F, 1.0F);
matrix4f.rotate(-n, vector3f);
}
float m = Math.max(h, this.minecraft.options.fov().get().intValue());
Matrix4f matrix4f2 = this.getProjectionMatrix(m);
RenderSystem.setProjectionMatrix(this.levelProjectionMatrixBuffer.getBuffer(matrix4f), ProjectionType.PERSPECTIVE);
Quaternionf quaternionf = camera.rotation().conjugate(new Quaternionf());
Matrix4f matrix4f3 = new Matrix4f().rotation(quaternionf);
this.minecraft.levelRenderer.prepareCullFrustum(camera.getPosition(), matrix4f3, matrix4f2);
profilerFiller.popPush("fog");
boolean bl2 = this.minecraft.level.effects().isFoggyAt(camera.getBlockPosition().getX(), camera.getBlockPosition().getZ())
|| this.minecraft.gui.getBossOverlay().shouldCreateWorldFog();
Vector4f vector4f = this.fogRenderer
.setupFog(camera, this.minecraft.options.getEffectiveRenderDistance(), bl2, deltaTracker, this.getDarkenWorldAmount(f), this.minecraft.level);
GpuBufferSlice gpuBufferSlice = this.fogRenderer.getBuffer(FogRenderer.FogMode.WORLD);
profilerFiller.popPush("level");
this.minecraft.levelRenderer.renderLevel(this.resourcePool, deltaTracker, bl, camera, matrix4f3, matrix4f, gpuBufferSlice, vector4f, !bl2);
profilerFiller.popPush("hand");
boolean bl3 = this.minecraft.getCameraEntity() instanceof LivingEntity && ((LivingEntity)this.minecraft.getCameraEntity()).isSleeping();
RenderSystem.setProjectionMatrix(
this.hud3dProjectionMatrixBuffer.getBuffer(this.minecraft.getWindow().getWidth(), this.minecraft.getWindow().getHeight(), this.getFov(camera, f, false)),
ProjectionType.PERSPECTIVE
);
RenderSystem.getDevice().createCommandEncoder().clearDepthTexture(this.minecraft.getMainRenderTarget().getDepthTexture(), 1.0);
this.renderItemInHand(f, bl3, matrix4f3);
profilerFiller.popPush("screen effects");
MultiBufferSource.BufferSource bufferSource = this.renderBuffers.bufferSource();
this.screenEffectRenderer.renderScreenEffect(bl3, f);
bufferSource.endBatch();
profilerFiller.pop();
RenderSystem.setShaderFog(this.fogRenderer.getBuffer(FogRenderer.FogMode.NONE));
if (this.minecraft.gui.shouldRenderDebugCrosshair()) {
this.minecraft.getDebugOverlay().render3dCrosshair(camera);
}
}
public void resetData() {
this.screenEffectRenderer.resetItemActivation();
this.minecraft.getMapTextureManager().resetData();
this.mainCamera.reset();
this.hasWorldScreenshot = false;
}
public void displayItemActivation(ItemStack stack) {
this.screenEffectRenderer.displayItemActivation(stack, this.random);
}
public Minecraft getMinecraft() {
return this.minecraft;
}
public float getDarkenWorldAmount(float partialTicks) {
return Mth.lerp(partialTicks, this.darkenWorldAmountO, this.darkenWorldAmount);
}
public float getRenderDistance() {
return this.renderDistance;
}
public Camera getMainCamera() {
return this.mainCamera;
}
public LightTexture lightTexture() {
return this.lightTexture;
}
public OverlayTexture overlayTexture() {
return this.overlayTexture;
}
@Override
public Vec3 projectPointToScreen(Vec3 point) {
Matrix4f matrix4f = this.getProjectionMatrix(this.getFov(this.mainCamera, 0.0F, true));
Quaternionf quaternionf = this.mainCamera.rotation().conjugate(new Quaternionf());
Matrix4f matrix4f2 = new Matrix4f().rotation(quaternionf);
Matrix4f matrix4f3 = matrix4f.mul(matrix4f2);
Vec3 vec3 = this.mainCamera.getPosition();
Vec3 vec32 = point.subtract(vec3);
Vector3f vector3f = matrix4f3.transformProject(vec32.toVector3f());
return new Vec3(vector3f);
}
@Override
public double projectHorizonToScreen() {
float f = this.mainCamera.getXRot();
if (f <= -90.0F) {
return Double.NEGATIVE_INFINITY;
} else if (f >= 90.0F) {
return Double.POSITIVE_INFINITY;
} else {
float g = this.getFov(this.mainCamera, 0.0F, true);
return Math.tan(f * (float) (Math.PI / 180.0)) / Math.tan(g / 2.0F * (float) (Math.PI / 180.0));
}
}
public GlobalSettingsUniform getGlobalSettingsUniform() {
return this.globalSettingsUniform;
}
public Lighting getLighting() {
return this.lighting;
}
public void setLevel(@Nullable ClientLevel level) {
if (level != null) {
this.lighting.updateLevel(level.effects().constantAmbientLight());
}
}
public PanoramaRenderer getPanorama() {
return this.panorama;
}
}