package net.coderbot.iris.compat.dh; import com.google.common.primitives.Ints; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.mixins.interfaces.EntityRendererAccessor; import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; import net.coderbot.iris.gl.blending.BlendModeOverride; import net.coderbot.iris.gl.blending.BufferBlendOverride; import net.coderbot.iris.gl.program.ProgramImages; import net.coderbot.iris.gl.program.ProgramSamplers; import net.coderbot.iris.gl.program.ProgramUniforms; import net.coderbot.iris.gl.shader.GlShader; import net.coderbot.iris.gl.shader.ShaderType; import net.coderbot.iris.gl.state.FogMode; import net.coderbot.iris.gl.texture.TextureType; import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline; import net.coderbot.iris.pipeline.PatchedShaderPrinter; import net.coderbot.iris.pipeline.transform.PatchShaderType; import net.coderbot.iris.pipeline.transform.TransformPatcher; import net.coderbot.iris.samplers.IrisSamplers; import net.coderbot.iris.shaderpack.ProgramSource; import net.coderbot.iris.uniforms.CommonUniforms; import net.coderbot.iris.uniforms.builtin.BuiltinReplacementUniforms; import net.coderbot.iris.uniforms.custom.CustomUniforms; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.DynamicTexture; import org.joml.Matrix4fc; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL32; import java.util.ArrayList; import java.util.List; import java.util.Map; public class IrisLodRenderProgram { // Uniforms public final int modelOffsetUniform; public final int worldYOffsetUniform; public final int mircoOffsetUniform; // Fog/Clip Uniforms public final int clipDistanceUniform; private final int id; private final ProgramUniforms uniforms; private final CustomUniforms customUniforms; private final ProgramSamplers samplers; private final ProgramImages images; private final BlendModeOverride blend; private final BufferBlendOverride[] bufferBlendOverrides; // This will bind AbstractVertexAttribute private IrisLodRenderProgram(String name, boolean isShadowPass, boolean translucent, BlendModeOverride override, BufferBlendOverride[] bufferBlendOverrides, String vertex, String tessControl, String tessEval, String geometry, String fragment, CustomUniforms customUniforms, DeferredWorldRenderingPipeline pipeline) { id = GLStateManager.glCreateProgram(); GLStateManager.glBindAttribLocation(this.id, 0, "vPosition"); GLStateManager.glBindAttribLocation(this.id, 1, "iris_color"); GLStateManager.glBindAttribLocation(this.id, 2, "irisExtra"); this.bufferBlendOverrides = bufferBlendOverrides; GlShader vert = new GlShader(ShaderType.VERTEX, name + ".vsh", vertex); GLStateManager.glAttachShader(id, vert.getHandle()); GlShader tessCont = null; if (tessControl != null) { tessCont = new GlShader(ShaderType.TESSELATION_CONTROL, name + ".tcs", tessControl); GLStateManager.glAttachShader(id, tessCont.getHandle()); } GlShader tessE = null; if (tessEval != null) { tessE = new GlShader(ShaderType.TESSELATION_EVAL, name + ".tes", tessEval); GLStateManager.glAttachShader(id, tessE.getHandle()); } GlShader geom = null; if (geometry != null) { geom = new GlShader(ShaderType.GEOMETRY, name + ".gsh", geometry); GLStateManager.glAttachShader(id, geom.getHandle()); } GlShader frag = new GlShader(ShaderType.FRAGMENT, name + ".fsh", fragment); GLStateManager.glAttachShader(id, frag.getHandle()); GLStateManager.glLinkProgram(this.id); int status = GLStateManager.glGetProgrami(this.id, GL20.GL_LINK_STATUS); if (status != 1) { String message = "Shader link error in Iris DH program! Details: " + GLStateManager.glGetProgramInfoLog(this.id, 9999); this.free(); throw new RuntimeException(message); } else { GLStateManager.glUseProgram(this.id); } vert.destroy(); frag.destroy(); if (tessCont != null) tessCont.destroy(); if (tessE != null) tessE.destroy(); if (geom != null) geom.destroy(); blend = override; ProgramUniforms.Builder uniformBuilder = ProgramUniforms.builder(name, id); ProgramSamplers.Builder samplerBuilder = ProgramSamplers.builder(id, IrisSamplers.WORLD_RESERVED_TEXTURE_UNITS); CommonUniforms.addDynamicUniforms(uniformBuilder, FogMode.PER_VERTEX); customUniforms.assignTo(uniformBuilder); BuiltinReplacementUniforms.addBuiltinReplacementUniforms(uniformBuilder); ProgramImages.Builder builder = ProgramImages.builder(id); pipeline.addGbufferOrShadowSamplers(samplerBuilder, builder, isShadowPass ? pipeline::getFlippedBeforeShadow : () -> translucent ? pipeline.getFlippedAfterTranslucent() : pipeline.getFlippedAfterPrepare(), isShadowPass, false, true, false); customUniforms.mapholderToPass(uniformBuilder, this); this.uniforms = uniformBuilder.buildUniforms(); this.customUniforms = customUniforms; samplers = samplerBuilder.build(); images = builder.build(); modelOffsetUniform = tryGetUniformLocation2("modelOffset"); worldYOffsetUniform = tryGetUniformLocation2("worldYOffset"); mircoOffsetUniform = tryGetUniformLocation2("mircoOffset"); // Fog/Clip Uniforms clipDistanceUniform = tryGetUniformLocation2("clipDistance"); } public static IrisLodRenderProgram createProgram(String name, boolean isShadowPass, boolean translucent, ProgramSource source, CustomUniforms uniforms, DeferredWorldRenderingPipeline pipeline) { Map transformed = TransformPatcher.patchDHTerrain( name, source.getVertexSource().orElseThrow(RuntimeException::new), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getGeometrySource().orElse(null), source.getFragmentSource().orElseThrow(RuntimeException::new), pipeline.getTextureMap()); String vertex = transformed.get(PatchShaderType.VERTEX); String tessControl = transformed.get(PatchShaderType.TESS_CONTROL); String tessEval = transformed.get(PatchShaderType.TESS_EVAL); String geometry = transformed.get(PatchShaderType.GEOMETRY); String fragment = transformed.get(PatchShaderType.FRAGMENT); PatchedShaderPrinter.debugPatchedShaders(source.getName(), vertex, geometry, tessControl, tessEval, fragment); List bufferOverrides = new ArrayList<>(); source.getDirectives().getBufferBlendOverrides().forEach(information -> { int index = Ints.indexOf(source.getDirectives().getDrawBuffers(), information.getIndex()); if (index > -1) { bufferOverrides.add(new BufferBlendOverride(index, information.getBlendMode())); } }); return new IrisLodRenderProgram(name, isShadowPass, translucent, source.getDirectives().getBlendModeOverride().orElse(null), bufferOverrides.toArray(BufferBlendOverride[]::new), vertex, tessControl, tessEval, geometry, fragment, uniforms, pipeline); } // Noise Uniforms public int tryGetUniformLocation2(CharSequence name) { return GLStateManager.glGetUniformLocation(this.id, name); } // Override ShaderProgram.bind() public void bind() { GLStateManager.glUseProgram(id); if (blend != null) blend.apply(); for (BufferBlendOverride override : bufferBlendOverrides) { override.apply(); } } public void unbind() { GLStateManager.glUseProgram(0); ProgramUniforms.clearActiveUniforms(); ProgramSamplers.clearActiveSamplers(); BlendModeOverride.restore(); } public void free() { GLStateManager.glDeleteProgram(id); } public void fillUniformData(Matrix4fc projection, Matrix4fc modelView, int worldYOffset, float partialTicks) { GLStateManager.glUseProgram(id); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + IrisSamplers.LIGHTMAP_TEXTURE_UNIT); DynamicTexture lightmapTexture = ((EntityRendererAccessor) Minecraft.getMinecraft().entityRenderer).getLightmapTexture(); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture.getGlTextureId()); LodRendererEvents.pushDhMatrices(projection, modelView); setUniform(mircoOffsetUniform, 0.01f); // 0.01 block offset // setUniform(skyLightUniform, skyLight); if (worldYOffsetUniform != -1) setUniform(worldYOffsetUniform, (float) worldYOffset); // Fog/Clip Uniforms float dhNearClipDistance = DhApi.Delayed.renderProxy.getNearClipPlaneDistanceInBlocks(partialTicks); setUniform(clipDistanceUniform, dhNearClipDistance); samplers.update(); uniforms.update(); customUniforms.push(this); images.update(); } private void setUniform(int index, float value) { GLStateManager.glUniform1f(index, value); } public void setModelPos(DhApiVec3f modelPos) { setUniform(modelOffsetUniform, modelPos); } private void setUniform(int index, DhApiVec3f pos) { GLStateManager.glUniform3f(index, pos.x, pos.y, pos.z); } }