package com.mojang.blaze3d.pipeline; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.AddressMode; import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuTexture; 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.renderer.RenderPipelines; import org.jetbrains.annotations.Nullable; @Environment(EnvType.CLIENT) public abstract class RenderTarget { private static int UNNAMED_RENDER_TARGETS = 0; public int width; public int height; public int viewWidth; public int viewHeight; protected final String label; public final boolean useDepth; @Nullable protected GpuTexture colorTexture; @Nullable protected GpuTexture depthTexture; public FilterMode filterMode; public RenderTarget(@Nullable String name, boolean useDepth) { this.label = name == null ? "FBO " + UNNAMED_RENDER_TARGETS++ : name; this.useDepth = useDepth; } public void resize(int width, int height) { RenderSystem.assertOnRenderThread(); this.destroyBuffers(); this.createBuffers(width, height); } public void destroyBuffers() { RenderSystem.assertOnRenderThread(); if (this.depthTexture != null) { this.depthTexture.close(); this.depthTexture = null; } if (this.colorTexture != null) { this.colorTexture.close(); this.colorTexture = null; } } public void copyDepthFrom(RenderTarget otherTarget) { RenderSystem.assertOnRenderThread(); if (this.depthTexture == null) { throw new IllegalStateException("Trying to copy depth texture to a RenderTarget without a depth texture"); } else if (otherTarget.depthTexture == null) { throw new IllegalStateException("Trying to copy depth texture from a RenderTarget without a depth texture"); } else { RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture(otherTarget.depthTexture, this.depthTexture, 0, 0, 0, 0, 0, this.width, this.height); } } public void createBuffers(int width, int height) { RenderSystem.assertOnRenderThread(); int i = RenderSystem.getDevice().getMaxTextureSize(); if (width > 0 && width <= i && height > 0 && height <= i) { this.viewWidth = width; this.viewHeight = height; this.width = width; this.height = height; if (this.useDepth) { this.depthTexture = RenderSystem.getDevice().createTexture(() -> this.label + " / Depth", TextureFormat.DEPTH32, width, height, 1); this.depthTexture.setTextureFilter(FilterMode.NEAREST, false); this.depthTexture.setAddressMode(AddressMode.CLAMP_TO_EDGE); } this.colorTexture = RenderSystem.getDevice().createTexture(() -> this.label + " / Color", TextureFormat.RGBA8, width, height, 1); this.colorTexture.setAddressMode(AddressMode.CLAMP_TO_EDGE); this.setFilterMode(FilterMode.NEAREST, true); } else { throw new IllegalArgumentException("Window " + width + "x" + height + " size out of bounds (max. size: " + i + ")"); } } public void setFilterMode(FilterMode filterMode) { this.setFilterMode(filterMode, false); } private void setFilterMode(FilterMode filterMode, boolean force) { if (this.colorTexture == null) { throw new IllegalStateException("Can't change filter mode, color texture doesn't exist yet"); } else { if (force || filterMode != this.filterMode) { this.filterMode = filterMode; this.colorTexture.setTextureFilter(filterMode, false); } } } public void blitToScreen() { if (this.colorTexture == null) { throw new IllegalStateException("Can't blit to screen, color texture doesn't exist yet"); } else { RenderSystem.getDevice().createCommandEncoder().presentTexture(this.colorTexture); } } public void blitAndBlendToTexture(GpuTexture texture) { RenderSystem.assertOnRenderThread(); RenderSystem.AutoStorageIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); GpuBuffer gpuBuffer = autoStorageIndexBuffer.getBuffer(6); GpuBuffer gpuBuffer2 = RenderSystem.getQuadVertexBuffer(); try (RenderPass renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(texture, OptionalInt.empty())) { renderPass.setPipeline(RenderPipelines.ENTITY_OUTLINE_BLIT); renderPass.setVertexBuffer(0, gpuBuffer2); renderPass.setIndexBuffer(gpuBuffer, autoStorageIndexBuffer.type()); renderPass.bindSampler("InSampler", this.colorTexture); renderPass.drawIndexed(0, 6); } } @Nullable public GpuTexture getColorTexture() { return this.colorTexture; } @Nullable public GpuTexture getDepthTexture() { return this.depthTexture; } }