package net.minecraft.client.renderer; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuTexture; import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.blaze3d.textures.TextureFormat; import com.mojang.blaze3d.vertex.VertexFormat; import java.util.OptionalInt; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.util.Mth; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.dimension.DimensionType; import org.joml.Vector3f; @Environment(EnvType.CLIENT) public class LightTexture implements AutoCloseable { public static final int FULL_BRIGHT = 15728880; public static final int FULL_SKY = 15728640; public static final int FULL_BLOCK = 240; private static final int TEXTURE_SIZE = 16; private static final int LIGHTMAP_UBO_SIZE = new Std140SizeCalculator() .putFloat() .putFloat() .putFloat() .putInt() .putFloat() .putFloat() .putFloat() .putFloat() .putVec3() .get(); private final GpuTexture texture; private final GpuTextureView textureView; private boolean updateLightTexture; private float blockLightRedFlicker; private final GameRenderer renderer; private final Minecraft minecraft; private final MappableRingBuffer ubo; public LightTexture(GameRenderer renderer, Minecraft minecraft) { this.renderer = renderer; this.minecraft = minecraft; GpuDevice gpuDevice = RenderSystem.getDevice(); this.texture = gpuDevice.createTexture("Light Texture", 12, TextureFormat.RGBA8, 16, 16, 1, 1); this.texture.setTextureFilter(FilterMode.LINEAR, false); this.textureView = gpuDevice.createTextureView(this.texture); gpuDevice.createCommandEncoder().clearColorTexture(this.texture, -1); this.ubo = new MappableRingBuffer(() -> "Lightmap UBO", 130, LIGHTMAP_UBO_SIZE); } public GpuTextureView getTextureView() { return this.textureView; } public void close() { this.texture.close(); this.textureView.close(); this.ubo.close(); } public void tick() { this.blockLightRedFlicker = this.blockLightRedFlicker + (float)((Math.random() - Math.random()) * Math.random() * Math.random() * 0.1); this.blockLightRedFlicker *= 0.9F; this.updateLightTexture = true; } public void turnOffLightLayer() { RenderSystem.setShaderTexture(2, null); } public void turnOnLightLayer() { RenderSystem.setShaderTexture(2, this.textureView); } private float calculateDarknessScale(LivingEntity entity, float gamma, float partialTick) { float f = 0.45F * gamma; return Math.max(0.0F, Mth.cos((entity.tickCount - partialTick) * (float) Math.PI * 0.025F) * f); } public void updateLightTexture(float partialTicks) { if (this.updateLightTexture) { this.updateLightTexture = false; ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("lightTex"); ClientLevel clientLevel = this.minecraft.level; if (clientLevel != null) { float f = clientLevel.getSkyDarken(1.0F); float g; if (clientLevel.getSkyFlashTime() > 0) { g = 1.0F; } else { g = f * 0.95F + 0.05F; } float h = this.minecraft.options.darknessEffectScale().get().floatValue(); float i = this.minecraft.player.getEffectBlendFactor(MobEffects.DARKNESS, partialTicks) * h; float j = this.calculateDarknessScale(this.minecraft.player, i, partialTicks) * h; float k = this.minecraft.player.getWaterVision(); float l; if (this.minecraft.player.hasEffect(MobEffects.NIGHT_VISION)) { l = GameRenderer.getNightVisionScale(this.minecraft.player, partialTicks); } else if (k > 0.0F && this.minecraft.player.hasEffect(MobEffects.CONDUIT_POWER)) { l = k; } else { l = 0.0F; } Vector3f vector3f = new Vector3f(f, f, 1.0F).lerp(new Vector3f(1.0F, 1.0F, 1.0F), 0.35F); float m = this.blockLightRedFlicker + 1.5F; float n = clientLevel.dimensionType().ambientLight(); boolean bl = clientLevel.effects().forceBrightLightmap(); float o = this.minecraft.options.gamma().get().floatValue(); RenderSystem.AutoStorageIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); GpuBuffer gpuBuffer = autoStorageIndexBuffer.getBuffer(6); CommandEncoder commandEncoder = RenderSystem.getDevice().createCommandEncoder(); try (GpuBuffer.MappedView mappedView = commandEncoder.mapBuffer(this.ubo.currentBuffer(), false, true)) { Std140Builder.intoBuffer(mappedView.data()) .putFloat(n) .putFloat(g) .putFloat(m) .putInt(bl ? 1 : 0) .putFloat(l) .putFloat(j) .putFloat(this.renderer.getDarkenWorldAmount(partialTicks)) .putFloat(Math.max(0.0F, o - i)) .putVec3(vector3f); } try (RenderPass renderPass = commandEncoder.createRenderPass(() -> "Update light", this.textureView, OptionalInt.empty())) { renderPass.setPipeline(RenderPipelines.LIGHTMAP); RenderSystem.bindDefaultUniforms(renderPass); renderPass.setUniform("LightmapInfo", this.ubo.currentBuffer()); renderPass.setVertexBuffer(0, RenderSystem.getQuadVertexBuffer()); renderPass.setIndexBuffer(gpuBuffer, autoStorageIndexBuffer.type()); renderPass.drawIndexed(0, 0, 6, 1); } this.ubo.rotate(); profilerFiller.pop(); } } } public static float getBrightness(DimensionType dimensionType, int lightLevel) { return getBrightness(dimensionType.ambientLight(), lightLevel); } public static float getBrightness(float ambientLight, int lightLevel) { float f = lightLevel / 15.0F; float g = f / (4.0F - 3.0F * f); return Mth.lerp(ambientLight, g, 1.0F); } public static int pack(int blockLight, int skyLight) { return blockLight << 4 | skyLight << 20; } public static int block(int packedLight) { return packedLight >>> 4 & 15; } public static int sky(int packedLight) { return packedLight >>> 20 & 15; } public static int lightCoordsWithEmission(int packedLight, int emission) { if (emission == 0) { return packedLight; } else { int i = Math.max(sky(packedLight), emission); int j = Math.max(block(packedLight), emission); return pack(j, i); } } }