From 4097cf76192b7dda96c73d7451ec6dbbb62ec4ef Mon Sep 17 00:00:00 2001 From: TomTheFurry <46843632+TomTheFurry@users.noreply.github.com> Date: Wed, 16 Mar 2022 18:50:41 +0800 Subject: [PATCH] ADVANCED FOGGGGGGGGGGGGGGGG~~~~~~~~~~~~~~~~ --- .../lod/core/enums/rendering/FogSetting.java | 25 +- .../enums/rendering/HeightFogMixMode.java | 14 + .../core/enums/rendering/HeightFogMode.java | 21 +- .../lod/core/objects/opengl/RenderRegion.java | 15 +- .../seibel/lod/core/render/LodFogConfig.java | 313 +++++++++++++++--- .../lod/core/render/LodRenderProgram.java | 132 ++++---- .../seibel/lod/core/render/LodRenderer.java | 37 ++- .../lod/core/render/objects/Shader.java | 24 +- .../core/render/objects/ShaderProgram.java | 39 ++- .../config/ILodConfigWrapperSingleton.java | 82 +++-- src/main/resources/assets/lod/lang/en_us.json | 106 ++++++ src/main/resources/shaders/flat_shaded.frag | 120 +++---- src/main/resources/shaders/standard.vert | 21 +- 13 files changed, 700 insertions(+), 249 deletions(-) create mode 100644 src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMixMode.java diff --git a/src/main/java/com/seibel/lod/core/enums/rendering/FogSetting.java b/src/main/java/com/seibel/lod/core/enums/rendering/FogSetting.java index af5281568..58573cee6 100644 --- a/src/main/java/com/seibel/lod/core/enums/rendering/FogSetting.java +++ b/src/main/java/com/seibel/lod/core/enums/rendering/FogSetting.java @@ -1,26 +1,43 @@ package com.seibel.lod.core.enums.rendering; +import java.util.Objects; + public class FogSetting { public final double start; public final double end; public final double min; public final double max; public final double density; - public final Type type; + public final FogType fogType; - public FogSetting(double start, double end, double min, double max, double density, Type type) { + public FogSetting(double start, double end, double min, double max, double density, FogType fogType) { this.start = start; this.end = end; this.min = min; this.max = max; this.density = density; - this.type = type; + this.fogType = fogType; } - public enum Type { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FogSetting that = (FogSetting) o; + return Double.compare(that.start, start) == 0 && Double.compare(that.end, end) == 0 && Double.compare(that.min, min) == 0 && Double.compare(that.max, max) == 0 && Double.compare(that.density, density) == 0 && fogType == that.fogType; + } + + @Override + public int hashCode() { + return Objects.hash(start, end, min, max, density, fogType); + } + + public enum FogType { LINEAR, EXPONENTIAL, EXPONENTIAL_SQUARED, // TEXTURE_BASED, // TODO: Impl this } + + } diff --git a/src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMixMode.java b/src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMixMode.java new file mode 100644 index 000000000..7ee8ceadc --- /dev/null +++ b/src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMixMode.java @@ -0,0 +1,14 @@ +package com.seibel.lod.core.enums.rendering; + +public enum HeightFogMixMode { + BASIC, + IGNORE_HEIGHT, + ADDITION, + MAX, + MULTIPLY, + INVERSE_MULTIPLY, + LIMITED_ADDITION, + MULTIPLY_ADDITION, + INVERSE_MULTIPLY_ADDITION, + AVERAGE, +} diff --git a/src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMode.java b/src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMode.java index 2f7621eb3..87542199f 100644 --- a/src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMode.java +++ b/src/main/java/com/seibel/lod/core/enums/rendering/HeightFogMode.java @@ -1,9 +1,20 @@ package com.seibel.lod.core.enums.rendering; public enum HeightFogMode { - BASIC, - IGNORE_HEIGHT, - ADDITION, - MAX, - SQUARED_ADDITION, + ABOVE_CAMERA(true, true, false), + BELOW_CAMERA(true, false, true), + ABOVE_AND_BELOW_CAMERA(true, true, true), + ABOVE_SET_HEIGHT(false, true, false), + BELOW_SET_HEIGHT(false, false, true), + ABOVE_AND_BELOW_SET_HEIGHT(false, true, true); + + public final boolean basedOnCamera; + public final boolean above; + public final boolean below; + + HeightFogMode(boolean basedOnCamera, boolean above, boolean below) { + this.basedOnCamera = basedOnCamera; + this.above = above; + this.below = below; + } } diff --git a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java index 85291081e..ad2816ecd 100644 --- a/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java +++ b/src/main/java/com/seibel/lod/core/objects/opengl/RenderRegion.java @@ -109,7 +109,7 @@ public class RenderRegion implements AutoCloseable public boolean render(LodDimension renderDim, Vec3d cameraPos, AbstractBlockPosWrapper cameraBlockPos, Vec3f cameraDir, - Mat4f baseModelViewMatrix, boolean enableDirectionalCulling, LodRenderProgram program) { + boolean enableDirectionalCulling, LodRenderProgram program) { if (!frontState.compareAndSet(FrontState.Unused, FrontState.Rendering)) return false; try { if (renderDim != lodDim) return false; @@ -120,7 +120,7 @@ public class RenderRegion implements AutoCloseable if (state == BackState.Complete) { if (renderBufferBack != null) { if (ENABLE_EVENT_LOGGING) ApiShared.LOGGER.info("RenderRegion swap @ {}", regionPos); - boolean shouldKeep = renderBufferFront==null ? false : renderBufferFront.onSwapToBack(); + boolean shouldKeep = renderBufferFront != null && renderBufferFront.onSwapToBack(); RenderBuffer temp = shouldKeep ? renderBufferFront : null; renderBufferFront = renderBufferBack; renderBufferBack = temp; @@ -131,12 +131,11 @@ public class RenderRegion implements AutoCloseable } } if (renderBufferFront == null) return false; - Mat4f localModelViewMatrix = baseModelViewMatrix.copy(); - localModelViewMatrix.multiplyTranslationMatrix( - (regionPos.x * LodUtil.REGION_WIDTH) - cameraPos.x, - LodBuilder.MIN_WORLD_HEIGHT - cameraPos.y, - (regionPos.z * LodUtil.REGION_WIDTH) - cameraPos.z); - program.fillUniformModelMatrix(localModelViewMatrix); + program.setModelPos(new Vec3f( + (float) ((regionPos.x * LodUtil.REGION_WIDTH) - cameraPos.x), + (float) (LodBuilder.MIN_WORLD_HEIGHT - cameraPos.y), + (float) ((regionPos.z * LodUtil.REGION_WIDTH) - cameraPos.z))); + return renderBufferFront.render(program); } finally { frontState.compareAndSet(FrontState.Rendering, FrontState.Unused); diff --git a/src/main/java/com/seibel/lod/core/render/LodFogConfig.java b/src/main/java/com/seibel/lod/core/render/LodFogConfig.java index 7c2d2abe1..b65ac0d28 100644 --- a/src/main/java/com/seibel/lod/core/render/LodFogConfig.java +++ b/src/main/java/com/seibel/lod/core/render/LodFogConfig.java @@ -19,11 +19,18 @@ package com.seibel.lod.core.render; -import com.seibel.lod.core.enums.rendering.FogDistance; -import com.seibel.lod.core.enums.rendering.FogDrawMode; +import com.seibel.lod.core.api.ApiShared; +import com.seibel.lod.core.enums.rendering.*; import com.seibel.lod.core.handlers.IReflectionHandler; +import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; +import com.seibel.lod.core.render.objects.Shader; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + /** * This object is just a replacement for an array * to make things easier to understand in the LodRenderer. @@ -33,43 +40,269 @@ import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; */ public class LodFogConfig { - public FogDrawMode fogDrawMode; - public FogDistance fogDistance; - - public float nearFogStart = 0; - public float nearFogEnd = 0; - - public float farFogStart = 0; - public float farFogEnd = 0; - - public LodFogConfig(ILodConfigWrapperSingleton config, IReflectionHandler reflectionHandler, int farPlaneBlockDistance, int vanillaBlockRenderedDistance) { - - fogDrawMode = config.client().graphics().fogQuality().getFogDrawMode(); - if (fogDrawMode == FogDrawMode.USE_OPTIFINE_SETTING) - fogDrawMode = reflectionHandler.getFogDrawMode(); - - // how different distances are drawn depends on the quality set - fogDistance = config.client().graphics().fogQuality().getFogDistance(); - - // far fog // - - if (config.client().graphics().fogQuality().getFogDistance() == FogDistance.NEAR_AND_FAR) - farFogStart = farPlaneBlockDistance * 0.9f; - else - // for more realistic fog when using FAR - farFogStart = Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f); - - farFogEnd = farPlaneBlockDistance; - - - // near fog // - - // the reason that I wrote fogEnd then fogStart backwards - // is because we are using fog backwards to how - // it is normally used, hiding near objects - // instead of far objects. - nearFogEnd = vanillaBlockRenderedDistance * 1.41f; - nearFogStart = vanillaBlockRenderedDistance * 1.6f; + private static final IReflectionHandler REFLECTION_HANDLER = SingletonHandler.get(IReflectionHandler.class); + private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + + public static final boolean DEBUG_DUMP_GENERATED_CODE = true; + + public final FogSetting farFogSetting; + public final FogSetting heightFogSetting; + public final HeightFogMixMode heightFogMixMode; + public final HeightFogMode heightFogMode; + public final float heightFogHeight; + + final boolean drawNearFog; + + public static LodFogConfig generateFogConfig() { + FogDrawMode doDraw = CONFIG.client().graphics().fogQuality().getFogDrawMode(); + if (doDraw == FogDrawMode.USE_OPTIFINE_SETTING) + doDraw = REFLECTION_HANDLER.getFogDrawMode(); + return new LodFogConfig(doDraw); + } + + private LodFogConfig(FogDrawMode fogDrawMode) { + if (fogDrawMode == FogDrawMode.FOG_DISABLED) { + drawNearFog = false; + farFogSetting = null; + heightFogMixMode = null; + heightFogMode = null; + heightFogSetting = null; + heightFogHeight = 0.f; + } else { + ILodConfigWrapperSingleton.IClient.IGraphics.IFogQuality setting = CONFIG.client().graphics().fogQuality(); + FogDistance fogDistance = setting.getFogDistance(); + drawNearFog = (fogDistance == FogDistance.NEAR || fogDistance == FogDistance.NEAR_AND_FAR); + if (fogDistance == FogDistance.FAR || fogDistance == FogDistance.NEAR_AND_FAR) { + farFogSetting = setting.advancedFog().computeFarFogSetting(); + heightFogMixMode = setting.advancedFog().heightFog().getHeightFogMixMode(); + if (heightFogMixMode != HeightFogMixMode.IGNORE_HEIGHT && heightFogMixMode != HeightFogMixMode.BASIC) { + heightFogSetting = setting.advancedFog().heightFog().computeHeightFogSetting(); + heightFogMode = setting.advancedFog().heightFog().getHeightFogMode(); + if (heightFogMode.basedOnCamera) { + heightFogHeight = 0.f; + } else { + heightFogHeight = (float) setting.advancedFog().heightFog().getHeightFogHeight(); + } + } else { + heightFogSetting = null; + heightFogMode = null; + heightFogHeight = 0.f; + } + } else { + farFogSetting = null; + heightFogSetting = null; + heightFogMode = null; + heightFogMixMode = null; + heightFogHeight = 0.f; + } + } + } + + public StringBuilder loadAndProcessFragShader(String path, boolean absoluteFilePath) { + StringBuilder str = makeRuntimeDefine(); + generateRuntimeShaderCode(Shader.loadFile(path, absoluteFilePath, str)); + if (DEBUG_DUMP_GENERATED_CODE) { + try (FileOutputStream file = new FileOutputStream("debugGenerated.frag", false)) { + file.write(str.toString().getBytes(StandardCharsets.UTF_8)); + ApiShared.LOGGER.info("Debug dumped generated code to debugGenerated.frag for {}", path); + } catch (IOException e) { + ApiShared.LOGGER.warn("Failed to debug dump generated code to file for {}", path); + } + } + return str; + } + + private StringBuilder makeRuntimeDefine() { + StringBuilder str = new StringBuilder(); + str.append("// =======RUNTIME GENERATED DEFINE SECTION========\n#version 150 core\n"); + + if (farFogSetting == null) { + str.append(""" +#define farFogStart 0.0 +#define farFogLength 0.0 +#define farFogMin 0.0 +#define farFogRange 0.0 +#define farFogDensity 0.0 +#define heightFogStart 0.0 +#define heightFogLength 0.0 +#define heightFogMin 0.0 +#define heightFogRange 0.0 +#define heightFogDensity 0.0 + """); + } else { + str.append("\n#define farFogStart "); + str.append(farFogSetting.start); + str.append("\n#define farFogLength "); + str.append(farFogSetting.end - farFogSetting.start); + str.append("\n#define farFogMin "); + str.append(farFogSetting.min); + str.append("\n#define farFogRange "); + str.append(farFogSetting.max - farFogSetting.min); + str.append("\n#define farFogDensity "); + str.append(farFogSetting.density); + str.append("\n"); + + if (heightFogSetting == null) { + str.append(""" +#define heightFogStart 0.0 +#define heightFogLength 0.0 +#define heightFogMin 0.0 +#define heightFogRange 0.0 +#define heightFogDensity 0.0 + """); + } else { + str.append("\n#define heightFogStart "); + str.append(heightFogSetting.start); + str.append("\n#define heightFogLength "); + str.append(heightFogSetting.end - heightFogSetting.start); + str.append("\n#define heightFogMin "); + str.append(heightFogSetting.min); + str.append("\n#define heightFogRange "); + str.append(heightFogSetting.max - heightFogSetting.min); + str.append("\n#define heightFogDensity "); + str.append(heightFogSetting.density); + str.append("\n"); + } + } + str.append("// =======RUNTIME END========\n"); + return str; + } + + private static String getFarFogMethod(FogSetting.FogType fogType) { + switch (fogType) { + case LINEAR: + return " return linearFog(dist, farFogStart, farFogLength, farFogMin, farFogRange);\n"; + case EXPONENTIAL: + return " return exponentialFog(dist, farFogStart, farFogLength, farFogMin, farFogRange, farFogDensity);\n"; + case EXPONENTIAL_SQUARED: + return " return exponentialSquaredFog(dist, farFogStart, farFogLength, farFogMin, farFogRange, farFogDensity);\n"; + } + throw new IllegalArgumentException(); + } + private static String getHeightDepthMethod(HeightFogMode mode, float heightFogHeight) { + String str = ""; + if (!mode.basedOnCamera) { + str = " vertical = realY - (" + heightFogHeight + ");\n"; + } + if (mode.below && mode.above) { + str += " return abs(vertical);\n"; + } else if (mode.below) { + str += " return -vertical;\n"; + } else if (mode.above) { + str += " return vertical;\n"; + } else { + str += " return 0;\n"; + } + return str; + } + + private static String getHeightFogMethod(FogSetting.FogType fogType) { + switch (fogType) { + case LINEAR: + return " return linearFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange);\n"; + case EXPONENTIAL: + return " return exponentialFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange, heightFogDensity);\n"; + case EXPONENTIAL_SQUARED: + return " return exponentialSquaredFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange, heightFogDensity);\n"; + } + throw new IllegalArgumentException(); + } + private static String getMixFogMethod(HeightFogMixMode mode) { + switch (mode) { + case BASIC: + case IGNORE_HEIGHT: + return " return max(near, far);\n"; + case ADDITION: + return " return max(near, far + height);\n"; + case MAX: + return " return max(near, max(far, height));\n"; + case INVERSE_MULTIPLY: + return " return max(near, 1.0 - (1.0-far)*(1.0-height));\n"; + case MULTIPLY: + return " return max(near, far*height);\n"; + case LIMITED_ADDITION: + return " return max(near, far + max(far, height));\n"; + case MULTIPLY_ADDITION: + return " return max(near, far + far*height);\n"; + case INVERSE_MULTIPLY_ADDITION: + return " return max(near, far + 1.0 - (1.0-far)*(1.0-height));\n"; + case AVERAGE: + return " return max(near, far*0.5 + height*0.5);\n"; + } + throw new IllegalArgumentException(); + } + + private void generateRuntimeShaderCode(StringBuilder str) { + str.append("// =======RUNTIME GENERATED CODE SECTION========\n"); + + // Generate method: float getNearFogThickness(float dist); + if (drawNearFog) { + str.append(""" +float getNearFogThickness(float dist) { + return linearFog(dist, nearFogStart, nearFogLength, 1.0, -1.0); +} + """); + } else { + str.append(""" +float getNearFogThickness(float dist) {return 0.0;} + """); + } + + if (farFogSetting == null) { + str.append(""" +float getFarFogThickness(float dist) { return 0.0; } +float getHeightFogThickness(float dist) { return 0.0; } +float calculateFarFogDepth(float horizontal, float dist) { return 0.0; } +float calculateHeightFogDepth(float vertical, float realY) { return 0.0; } +float mixFogThickness(float near, float far, float height) { return near; } + """); + } else { + // Generate method: float getFarFogThickness(float dist); + str.append("float getFarFogThickness(float dist) {\n"); + str.append(getFarFogMethod(farFogSetting.fogType)); + str.append("}\n"); + + // Generate method: float getHeightFogThickness(float dist); + if (heightFogSetting == null) { + str.append(""" +float getHeightFogThickness(float dist) { return 0.0; } +float calculateHeightFogDepth(float vertical, float realY) { return 0.0; } + """); + } else { + str.append("float getHeightFogThickness(float dist) {\n"); + str.append(getHeightFogMethod(heightFogSetting.fogType)); + str.append("}\n"); + str.append("float calculateHeightFogDepth(float vertical, float realY) {\n"); + str.append(getHeightDepthMethod(heightFogMode, heightFogHeight)); + str.append("}\n"); + } + + // Generate method: calculateFarFogDepth(float horizontal, float vertical, float dist); + str.append("float calculateFarFogDepth(float horizontal, float dist) {\n"); + if (heightFogMixMode == HeightFogMixMode.BASIC) { + str.append(" return dist;\n"); + } else { + str.append(" return horizontal;\n"); + } + str.append("}\n"); + + // Generate method: float mixFogThickness(float near, float far, float height); + str.append("float mixFogThickness(float near, float far, float height) {\n"); + str.append(getMixFogMethod(heightFogMixMode)); + str.append("}\n"); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LodFogConfig that = (LodFogConfig) o; + return Float.compare(that.heightFogHeight, heightFogHeight) == 0 && drawNearFog == that.drawNearFog && Objects.equals(farFogSetting, that.farFogSetting) && Objects.equals(heightFogSetting, that.heightFogSetting) && heightFogMixMode == that.heightFogMixMode && heightFogMode == that.heightFogMode; + } + + @Override + public int hashCode() { + return Objects.hash(farFogSetting, heightFogSetting, heightFogMixMode, heightFogMode, heightFogHeight, drawNearFog); } - } diff --git a/src/main/java/com/seibel/lod/core/render/LodRenderProgram.java b/src/main/java/com/seibel/lod/core/render/LodRenderProgram.java index 977cdb38a..63159e6c6 100644 --- a/src/main/java/com/seibel/lod/core/render/LodRenderProgram.java +++ b/src/main/java/com/seibel/lod/core/render/LodRenderProgram.java @@ -23,65 +23,66 @@ import java.awt.Color; import com.seibel.lod.core.enums.rendering.FogDistance; import com.seibel.lod.core.enums.rendering.FogDrawMode; +import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; import com.seibel.lod.core.objects.math.Mat4f; -import com.seibel.lod.core.render.objects.ShaderProgram; -import com.seibel.lod.core.render.objects.VertexAttribute; -import com.seibel.lod.core.render.objects.VertexAttributePostGL43; -import com.seibel.lod.core.render.objects.VertexAttributePreGL43; +import com.seibel.lod.core.objects.math.Vec3f; +import com.seibel.lod.core.render.objects.*; import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.wrapperInterfaces.IVersionConstants; +import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; public class LodRenderProgram extends ShaderProgram { public static final String VERTEX_SHADER_PATH = "shaders/standard.vert"; public static final String FRAGMENT_SHADER_PATH = "shaders/flat_shaded.frag"; + private static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class); public final VertexAttribute vao; // Attributes public final int posAttrib; public final int colAttrib; - //public final int lightAttrib; //Sky light then block light + // Uniforms - public final int mvmUniform; - public final int projUniform; - //public final int cameraUniform; - public final int fogColorUniform; - // public final int skyLightUniform; worldSkyLight is currently not used + public final int combinedMatUniform; + public final int modelOffsetUniform; + public final int worldYOffsetUniform; + public final int lightMapUniform; // Fog Uniforms - public final int fogEnabledUniform; - public final int nearFogEnabledUniform; - public final int farFogEnabledUniform; + public final int fogColorUniform; + public final int fogScaleUniform; + public final int fogVerticalScaleUniform; public final int nearFogStartUniform; - public final int nearFogEndUniform; - public final int farFogStartUniform; - public final int farFogEndUniform; + public final int nearFogLengthUniform;; + public final int fullFogModeUniform; + + public final LodFogConfig fogConfig; // This will bind VertexAttribute - public LodRenderProgram() { - super(VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH, "fragColor"); + public LodRenderProgram(LodFogConfig fogConfig) { + super(() -> Shader.loadFile(VERTEX_SHADER_PATH, false, new StringBuilder()).toString(), + () -> fogConfig.loadAndProcessFragShader(FRAGMENT_SHADER_PATH, false).toString(), + "fragColor"); + this.fogConfig = fogConfig; posAttrib = getAttributeLocation("vPosition"); - colAttrib = getAttributeLocation("color"); - //lightAttrib = getAttributeLocation("light"); - - mvmUniform = getUniformLocation("modelViewMatrix"); - projUniform = getUniformLocation("projectionMatrix"); - //cameraUniform = getUniformLocation("cameraPos"); - fogColorUniform = getUniformLocation("fogColor"); - // skyLightUniform = getUniformLocation("worldSkyLight"); + colAttrib = tryGetAttributeLocation("color"); // might be optimized out in some fog settings + + combinedMatUniform = getUniformLocation("combinedMatrix"); + modelOffsetUniform = getUniformLocation("modelOffset"); + worldYOffsetUniform = tryGetUniformLocation("worldYOffset"); + lightMapUniform = getUniformLocation("lightMap"); // Fog uniforms - fogEnabledUniform = getUniformLocation("fogEnabled"); - nearFogEnabledUniform = getUniformLocation("nearFogEnabled"); - farFogEnabledUniform = getUniformLocation("farFogEnabled"); + fullFogModeUniform = getUniformLocation("fullFogMode"); + fogColorUniform = getUniformLocation("fogColor"); + fogScaleUniform = tryGetUniformLocation("fogScale"); + fogVerticalScaleUniform = tryGetUniformLocation("fogVerticalScale"); // near - nearFogStartUniform = getUniformLocation("nearFogStart"); - nearFogEndUniform = getUniformLocation("nearFogEnd"); - // far - farFogStartUniform = getUniformLocation("farFogStart"); - farFogEndUniform = getUniformLocation("farFogEnd"); - + nearFogStartUniform = tryGetUniformLocation("nearFogStart"); + nearFogLengthUniform = tryGetUniformLocation("nearFogLength"); + // TODO: Add better use of the LODFormat thing int vertexByteCount = LodUtil.LOD_VERTEX_FORMAT.getByteSize(); if (GLProxy.getInstance().VertexAttributeBufferBindingSupported) @@ -92,7 +93,7 @@ public class LodRenderProgram extends ShaderProgram { // Now a pos+light. vao.setVertexAttribute(0, posAttrib, VertexAttribute.VertexPointer.addUnsignedShortsPointer(4, false)); // 2+2+2+2 //vao.setVertexAttribute(0, posAttrib, VertexAttribute.VertexPointer.addVec3Pointer(false)); // 4+4+4 - vao.setVertexAttribute(0, colAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(4, true)); // +4 + vao.setVertexAttribute(0, colAttrib == -1 ? 2 : colAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(4, true)); // +4 //vao.setVertexAttribute(0, lightAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(2, false)); // +4 due to how it aligns try { vao.completeAndCheck(vertexByteCount); @@ -101,6 +102,13 @@ public class LodRenderProgram extends ShaderProgram { throw e; } } + + // If not usable, return a new LodFogConfig to be constructed + public LodFogConfig isShaderUsable() { + LodFogConfig newConfig = LodFogConfig.generateFogConfig(); + if (fogConfig.equals(newConfig)) return null; + return newConfig; + } // Override ShaderProgram.bind() public void bind() { @@ -127,41 +135,33 @@ public class LodRenderProgram extends ShaderProgram { vao.unbindBuffersFromAllBindingPoint(); } - public void fillUniformData(Mat4f projectionMatrix, Color fogColor, int skyLight, int lightmapBindPoint) { + public void fillUniformData(Mat4f combinedMatrix, Color fogColor, + int lightmapBindPoint, int worldHeight, int worldYOffset, int lodDrawDistance, + int vanillaDrawDistance, boolean fullFogMode) { super.bind(); + vanillaDrawDistance += 32; // Give it a 2 chunk boundary for near fog. // uniforms - setUniform(projUniform, projectionMatrix); - setUniform(fogColorUniform, fogColor); + setUniform(combinedMatUniform, combinedMatrix); + // setUniform(skyLightUniform, skyLight); setUniform(lightMapUniform, lightmapBindPoint); + + if (worldYOffsetUniform != -1) setUniform(worldYOffsetUniform, (float)worldYOffset); + + // Fog + setUniform(fullFogModeUniform, fullFogMode ? 1 : 0); + setUniform(fogColorUniform, fogColor); + + float nearFogLen = vanillaDrawDistance * 0.2f / lodDrawDistance; + float nearFogStart = vanillaDrawDistance * (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float)Math.sqrt(2.) : 1.f) / lodDrawDistance; + if (nearFogStartUniform != -1) setUniform(nearFogStartUniform, nearFogStart); + if (nearFogLengthUniform != -1) setUniform(nearFogLengthUniform, nearFogLen); + if (fogScaleUniform != -1) setUniform(fogScaleUniform, 1.f/lodDrawDistance); + if (fogVerticalScaleUniform != -1) setUniform(fogVerticalScaleUniform, 1.f/worldHeight); } - - public void fillUniformModelMatrix(Mat4f modelViewMatrix) { - super.bind(); - setUniform(mvmUniform, modelViewMatrix); - } - - public void fillUniformDataForFog(LodFogConfig fogSettings, boolean allFogMode) { - super.bind(); - if (allFogMode) { - setUniform(fogEnabledUniform, true); - setUniform(nearFogEnabledUniform, false); - setUniform(farFogEnabledUniform, true); - setUniform(farFogStartUniform, 0.0f); - setUniform(farFogEndUniform, 0.0f); - } else if (fogSettings.fogDrawMode != FogDrawMode.FOG_DISABLED) { - setUniform(fogEnabledUniform, true); - setUniform(nearFogEnabledUniform, fogSettings.fogDistance != FogDistance.FAR); - setUniform(farFogEnabledUniform, fogSettings.fogDistance != FogDistance.NEAR); - // near - setUniform(nearFogStartUniform, fogSettings.nearFogStart); - setUniform(nearFogEndUniform, fogSettings.nearFogEnd); - // far - setUniform(farFogStartUniform, fogSettings.farFogStart); - setUniform(farFogEndUniform, fogSettings.farFogEnd); - } else { - setUniform(fogEnabledUniform, false); - } + + public void setModelPos(Vec3f modelPos) { + setUniform(modelOffsetUniform, modelPos); } } diff --git a/src/main/java/com/seibel/lod/core/render/LodRenderer.java b/src/main/java/com/seibel/lod/core/render/LodRenderer.java index 10540bacc..0c3efefe6 100644 --- a/src/main/java/com/seibel/lod/core/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/core/render/LodRenderer.java @@ -77,7 +77,6 @@ public class LodRenderer private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class); private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class); private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - private static final IReflectionHandler REFLECTION_HANDLER = SingletonHandler.get(IReflectionHandler.class); public static final int VANILLA_REFRESH_TIMEOUT = 60; /** @@ -251,7 +250,6 @@ public class LodRenderer profiler.push("LOD draw setup"); LagSpikeCatcher drawSetup = new LagSpikeCatcher(); - /*---------Set GL State--------*/ // Make sure to unbind current VBO so we don't mess up vanilla settings LagSpikeCatcher drawGLSetup = new LagSpikeCatcher(); @@ -294,6 +292,11 @@ public class LodRenderer drawObjectSetup.end("drawObjectSetup"); } else { LagSpikeCatcher drawShaderBind = new LagSpikeCatcher(); + LodFogConfig newConfig = shaderProgram.isShaderUsable(); + if (newConfig != null) { + shaderProgram.free(); + shaderProgram = new LodRenderProgram(newConfig); + } shaderProgram.bind(); drawShaderBind.end("drawShaderBind"); } @@ -312,18 +315,18 @@ public class LodRenderer farPlaneBlockDistance = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH; else farPlaneBlockDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * LodUtil.CHUNK_WIDTH; - LodFogConfig fogSettings = new LodFogConfig(CONFIG, REFLECTION_HANDLER, farPlaneBlockDistance, vanillaBlockRenderedDistance); drawCalculateParams.end("drawCalculateParams"); - Mat4f projectionMatrix = createProjectionMatrix(baseProjectionMatrix, vanillaBlockRenderedDistance, farPlaneBlockDistance); + + Mat4f combinedMatrix = createCombinedMatrix(baseProjectionMatrix, baseModelViewMatrix, vanillaBlockRenderedDistance, farPlaneBlockDistance); /*---------Fill uniform data--------*/ LagSpikeCatcher drawFillData = new LagSpikeCatcher(); // Fill the uniform data. Note: GL33.GL_TEXTURE0 == texture bindpoint 0 - shaderProgram.fillUniformData(projectionMatrix, + shaderProgram.fillUniformData(combinedMatrix, MC_RENDER.isFogStateSpecial() ? getSpecialFogColor(partialTicks) : getFogColor(partialTicks), - (int) (MC.getSkyDarken(partialTicks) * 15), 0); - // Previous guy said fog setting may be different from region to region, but the fogSettings never changed... soooooo... - shaderProgram.fillUniformDataForFog(fogSettings, MC_RENDER.isFogStateSpecial()); + 0, MC.getWrappedClientWorld().getHeight(), MC.getWrappedClientWorld().getMinHeight(), farPlaneBlockDistance, + vanillaBlockRenderedDistance, MC_RENDER.isFogStateSpecial()); + // Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one. LagSpikeCatcher drawFillLightmap = new LagSpikeCatcher(); lightmapTexture.fillData(MC_RENDER.getLightmapTextureWidth(), MC_RENDER.getLightmapTextureHeight(), MC_RENDER.getLightmapPixels()); @@ -358,7 +361,7 @@ public class LodRenderer RenderRegion region = regions.get(regionX, regionZ); if (region == null) continue; if (region.render(lodDim, cameraPos, cameraBlockPos, cameraDir, - baseModelViewMatrix, !cullingDisabled, shaderProgram)) drawCount++; + !cullingDisabled, shaderProgram)) drawCount++; } } if( (ox == oy) || ((ox < 0) && (ox == -oy)) || ((ox > 0) && (ox == 1-oy))){ @@ -429,7 +432,7 @@ public class LodRenderer } isSetupComplete = true; - shaderProgram = new LodRenderProgram(); + shaderProgram = new LodRenderProgram(LodFogConfig.generateFogConfig()); } /** Create all buffers that will be used. */ @@ -456,18 +459,22 @@ public class LodRenderer /** * create and return a new projection matrix based on MC's projection matrix - * @param currentProjectionMatrix this is Minecraft's current projection matrix + * @param projMat this is Minecraft's current projection matrix + * @param modelMat this is Minecraft's current model matrix * @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance */ - private static Mat4f createProjectionMatrix(Mat4f currentProjectionMatrix, float vanillaBlockRenderedDistance, int farPlaneBlockDistance) + private static Mat4f createCombinedMatrix(Mat4f projMat, Mat4f modelMat, float vanillaBlockRenderedDistance, int farPlaneBlockDistance) { //Create a copy of the current matrix, so the current matrix isn't modified. - Mat4f lodProj = currentProjectionMatrix.copy(); + Mat4f lodProj = projMat.copy(); //Set new far and near clip plane values. lodProj.setClipPlanes( - CONFIG.client().graphics().advancedGraphics().getUseExtendedNearClipPlane() ? vanillaBlockRenderedDistance / 5 : 1, - farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2); + CONFIG.client().graphics().advancedGraphics().getUseExtendedNearClipPlane() ? + (vanillaBlockRenderedDistance-16) : 16, + (float)((farPlaneBlockDistance+LodUtil.REGION_WIDTH) * Math.sqrt(2))); + + lodProj.multiply(modelMat); return lodProj; } diff --git a/src/main/java/com/seibel/lod/core/render/objects/Shader.java b/src/main/java/com/seibel/lod/core/render/objects/Shader.java index f2fe0469e..9deaa8786 100644 --- a/src/main/java/com/seibel/lod/core/render/objects/Shader.java +++ b/src/main/java/com/seibel/lod/core/render/objects/Shader.java @@ -54,7 +54,7 @@ public class Shader ApiShared.LOGGER.info("Loading shader at "+path); // Create an empty shader object id = GL32.glCreateShader(type); - StringBuilder source = loadFile(path, absoluteFilePath); + StringBuilder source = loadFile(path, absoluteFilePath, new StringBuilder()); GL32.glShaderSource(id, source); GL32.glCompileShader(id); @@ -68,14 +68,30 @@ public class Shader ApiShared.LOGGER.info("Shader at "+path+" loaded sucessfully."); } + public Shader(int type, String sourceString) + { + ApiShared.LOGGER.info("Loading shader with soruceString:\n{}", sourceString); + // Create an empty shader object + id = GL32.glCreateShader(type); + GL32.glShaderSource(id, sourceString); + + GL32.glCompileShader(id); + // check if the shader compiled + int status = GL32.glGetShaderi(id, GL32.GL_COMPILE_STATUS); + if (status != GL32.GL_TRUE) { + String message = "Shader compiler error. Details: "+GL32.glGetShaderInfoLog(id); + free(); // important! + throw new RuntimeException(message); + } + ApiShared.LOGGER.info("Shader loaded sucessfully."); + } + // REMEMBER to always free the resource! public void free() { GL32.glDeleteShader(id); } - private StringBuilder loadFile(String path, boolean absoluteFilePath) { - StringBuilder stringBuilder = new StringBuilder(); - + public static StringBuilder loadFile(String path, boolean absoluteFilePath, StringBuilder stringBuilder) { try { // open the file diff --git a/src/main/java/com/seibel/lod/core/render/objects/ShaderProgram.java b/src/main/java/com/seibel/lod/core/render/objects/ShaderProgram.java index 31db6214c..fe34d16c5 100644 --- a/src/main/java/com/seibel/lod/core/render/objects/ShaderProgram.java +++ b/src/main/java/com/seibel/lod/core/render/objects/ShaderProgram.java @@ -21,6 +21,7 @@ package com.seibel.lod.core.render.objects; import java.awt.Color; import java.nio.FloatBuffer; +import java.util.function.Supplier; import org.lwjgl.opengl.GL32; import org.lwjgl.system.MemoryStack; @@ -49,19 +50,26 @@ public class ShaderProgram * This will bind ShaderProgram */ public ShaderProgram(String vert, String frag, String fragDataOutputName) { - Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, vert, false); - Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, frag, false); - + this(() -> Shader.loadFile(vert, false, new StringBuilder()).toString(), + () -> Shader.loadFile(frag, false, new StringBuilder()).toString(), + fragDataOutputName); + } + + public ShaderProgram(Supplier vert, Supplier frag, String fragDataOutputName) + { + Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, vert.get()); + Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, frag.get()); + id = GL32.glCreateProgram(); - + GL32.glAttachShader(this.id, vertShader.id); GL32.glAttachShader(this.id, fragShader.id); - //GL32.glBindFragDataLocation(id, 0, fragDataOutputName); + //GL32.glBindFragDataLocation(id, 0, fragDataOutputName); GL32.glLinkProgram(this.id); - + vertShader.free(); // important! fragShader.free(); // important! - + int status = GL32.glGetProgrami(this.id, GL32.GL_LINK_STATUS); if (status != GL32.GL_TRUE) { String message = "Shader Link Error. Details: "+GL32.glGetProgramInfoLog(this.id); @@ -70,7 +78,7 @@ public class ShaderProgram } GL32.glUseProgram(id); // This HAVE to be a direct call to prevent calling the overloaded version } - + /** This will bind ShaderProgram */ public void bind() { @@ -101,7 +109,13 @@ public class ShaderProgram if (i==-1) throw new RuntimeException("Attribute name not found: "+name); return i; } - + // Same as above but without throwing errors. + // Return -1 if attribute doesn't exist or has been optimized out + public int tryGetAttributeLocation(CharSequence name) + { + return GL32.glGetAttribLocation(id, name); + } + /** WARNING: Slow native call! Cache it if possible! * Gets the location of a uniform variable with specified name. * Calls GL20.glGetUniformLocation(id, name) @@ -117,6 +131,13 @@ public class ShaderProgram return i; } + // Same as above but without throwing errors. + // Return -1 if uniform doesn't exist or has been optimized out + public int tryGetUniformLocation(CharSequence name) + { + return GL32.glGetUniformLocation(id, name); + } + /** Requires ShaderProgram binded. */ public void setUniform(int location, boolean value) { diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java index de5e845e6..d5ee914a4 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/config/ILodConfigWrapperSingleton.java @@ -229,7 +229,7 @@ public interface ILodConfigWrapperSingleton extends IBindable interface IAdvancedFog { String DESC = "Advanced settings for fog rendering. Has no effect if Far Fog is not drawn \n" - + "See https://www.desmos.com/calculator/drzzlfmur9 for how setting effect the curve."; + + "See https://www.desmos.com/calculator/?????? for how setting effect the curve."; MinDefaultMax FOG_RANGE = new MinDefaultMax<>(0.0,1.0, Math.sqrt(2.0)); @@ -271,16 +271,16 @@ public interface ILodConfigWrapperSingleton extends IBindable double getFarFogMax(); void setFarFogMax(double newFarFogMax); - FogSetting.Type FAR_FOG_TYPE_DEFAULT = FogSetting.Type.EXPONENTIAL_SQUARED; + FogSetting.FogType FAR_FOG_TYPE_DEFAULT = FogSetting.FogType.EXPONENTIAL_SQUARED; String FAR_FOG_TYPE_DESC = "" + " How the fog thickness should be calculated from distance? \n" + "\n" - + " "+ FogSetting.Type.LINEAR + ": Linear based on distance (will ignore 'density')\n" - + " "+ FogSetting.Type.EXPONENTIAL + ": 1/(e^(distance*density)) \n" - + " "+ FogSetting.Type.EXPONENTIAL_SQUARED + ": 1/(e^((distance*density)^2) \n"; + + " "+ FogSetting.FogType.LINEAR + ": Linear based on distance (will ignore 'density')\n" + + " "+ FogSetting.FogType.EXPONENTIAL + ": 1/(e^(distance*density)) \n" + + " "+ FogSetting.FogType.EXPONENTIAL_SQUARED + ": 1/(e^((distance*density)^2) \n"; //+ " "+ FogSetting.Type.TEXTURE_BASED + ": Use a provided 1D texture mapping (will ignore 'density', 'min', and 'max')\n"; - FogSetting.Type getFarFogType(); - void setFarFogType(FogSetting.Type newFarFogType); + FogSetting.FogType getFarFogType(); + void setFarFogType(FogSetting.FogType newFarFogType); MinDefaultMax FAR_FOG_DENSITY_MIN_DEFAULT_MAX = new MinDefaultMax<>(0.01,2.5, 50.0); String FAR_FOG_DENSITY_DESC = "" @@ -293,37 +293,61 @@ public interface ILodConfigWrapperSingleton extends IBindable String DESC = "Advanced settings for how far fog interacts with height. Has no effect if Far Fog is not drawn \n" + "See https://www.desmos.com/calculator/drzzlfmur9 for how setting effect the curve."; - HeightFogMode HEIGHT_FOG_MODE_DEFAULT = HeightFogMode.BASIC; - String HEIGHT_FOG_MODE_DESC = "" + HeightFogMixMode HEIGHT_FOG_MIX_MODE_DEFAULT = HeightFogMixMode.BASIC; + String HEIGHT_FOG_MIX_MODE_DESC = "" + " How the height should effect the fog thickness combined with the normal function? \n" + "\n" - + " " + HeightFogMode.BASIC + ": No special height fog effect. Fog is calculated based on camera distance \n" - + " " + HeightFogMode.IGNORE_HEIGHT + ": Ignore height completely. Fog is calculated based on horizontal distance \n" - + " " + HeightFogMode.ADDITION + ": The calculated Height Fog thickness is added to Horizontal Fog thickness \n" - + " " + HeightFogMode.MAX + ": Use the max of Height Fog thickness and Horizontal Fog thickness \n" - + " " + HeightFogMode.SQUARED_ADDITION + ": Height Fog thickness and Horizontal Fog thickness is added via squared scaling \n" + + " " + HeightFogMixMode.BASIC + ": No special height fog effect. Fog is calculated based on camera distance \n" + + " " + HeightFogMixMode.IGNORE_HEIGHT + ": Ignore height completely. Fog is calculated based on horizontal distance \n" + + " " + HeightFogMixMode.ADDITION + ": heightFog + farFog \n" + + " " + HeightFogMixMode.MAX + ": max(heightFog, farFog) \n" + + " " + HeightFogMixMode.MULTIPLY + ": heightFog * farFog \n" + + " " + HeightFogMixMode.INVERSE_MULTIPLY + ": 1 - (1-heightFog) * (1-farFog) \n" + + " " + HeightFogMixMode.LIMITED_ADDITION + ": farFog + max(farFog, heightFog) \n" + + " " + HeightFogMixMode.MULTIPLY_ADDITION + ": farFog + farFog * heightFog \n" + + " " + HeightFogMixMode.INVERSE_MULTIPLY_ADDITION + ": farFog + 1 - (1-heightFog) * (1-farFog) \n" + + " " + HeightFogMixMode.AVERAGE + ": farFog*0.5 + heightFog*0.5 \n" + "\n" + " Note that for 'BASIC' mode and 'IGNORE_HEIGHT' mode, fog settings for height fog has no effect.\n"; + HeightFogMixMode getHeightFogMixMode(); + void setHeightFogMixMode(HeightFogMixMode newHeightFogMixMode); + + HeightFogMode HEIGHT_FOG_MODE_DEFAULT = HeightFogMode.ABOVE_AND_BELOW_CAMERA; + String HEIGHT_FOG_MODE_DESC = "" + + " Where should the height fog be located? \n" + + "\n" + + " " + HeightFogMode.ABOVE_CAMERA + ": Height fog starts from camera to the sky \n" + + " " + HeightFogMode.BELOW_CAMERA + ": Height fog starts from camera to the void \n" + + " " + HeightFogMode.ABOVE_AND_BELOW_CAMERA + ": Height fog starts from camera to both the sky and the void \n" + + " " + HeightFogMode.ABOVE_SET_HEIGHT + ": Height fog starts from a set height to the sky \n" + + " " + HeightFogMode.BELOW_SET_HEIGHT + ": Height fog starts from a set height to the void \n" + + " " + HeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT + ": Height fog starts from a set height to both the sky and the void \n" + + "\n"; HeightFogMode getHeightFogMode(); - void setHeightFogType(HeightFogMode newHeightFogMode); + void setHeightFogMode(HeightFogMode newHeightFogMode); + + MinDefaultMax HEIGHT_FOG_HEIGHT_MIN_DEFAULT_MAX = new MinDefaultMax<>(-4096., 70., 4096.); + String HEIGHT_FOG_HEIGHT_DESC = "" + + " If the height fog is calculated around a set height, what is that height position? \n" + + "\n"; + double getHeightFogHeight(); + void setHeightFogHeight(double newHeightFogHeight); MinDefaultMax HEIGHT_FOG_START_MIN_DEFAULT_MAX = new MinDefaultMax<>(FOG_RANGE.minValue, 0.0, FOG_RANGE.maxValue); String HEIGHT_FOG_START_DESC = "" - + " Where should the far fog start? \n" + + " How far the start of height fog should offset? \n" + "\n" - + " '0.0': Fog start at player's position.\n" - + " '1.0': The fog-start's circle fit just in the lod render distance square.\n" - + " '1.414': The lod render distance square fit just in the fog-start's circle.\n"; + + " '0.0': Fog start with no offset.\n" + + " '1.0': Fog start with offset of the entire world's height. (Include depth)\n"; double getHeightFogStart(); void setHeightFogStart(double newHeightFogStart); MinDefaultMax HEIGHT_FOG_END_MIN_DEFAULT_MAX = new MinDefaultMax<>(FOG_RANGE.minValue, 1.0, FOG_RANGE.maxValue); String HEIGHT_FOG_END_DESC = "" - + " Where should the far fog end? \n" + + " How far the end of height fog should offset? \n" + "\n" - + " '0.0': Fog end at player's position.\n" - + " '1.0': The fog-end's circle fit just in the lod render distance square.\n" - + " '1.414': The lod render distance square fit just in the fog-end's circle.\n"; + + " '0.0': Fog end with no offset.\n" + + " '1.0': Fog end with offset of the entire world's height. (Include depth)\n"; double getHeightFogEnd(); void setHeightFogEnd(double newHeightFogEnd); @@ -345,16 +369,16 @@ public interface ILodConfigWrapperSingleton extends IBindable double getHeightFogMax(); void setHeightFogMax(double newHeightFogMax); - FogSetting.Type HEIGHT_FOG_TYPE_DEFAULT = FogSetting.Type.EXPONENTIAL_SQUARED; + FogSetting.FogType HEIGHT_FOG_TYPE_DEFAULT = FogSetting.FogType.EXPONENTIAL_SQUARED; String HEIGHT_FOG_TYPE_DESC = "" + " How the fog thickness should be calculated from height? \n" + "\n" - + " " + FogSetting.Type.LINEAR + ": Linear based on height (will ignore 'density')\n" - + " " + FogSetting.Type.EXPONENTIAL + ": 1/(e^(height*density)) \n" - + " " + FogSetting.Type.EXPONENTIAL_SQUARED + ": 1/(e^((height*density)^2) \n"; + + " " + FogSetting.FogType.LINEAR + ": Linear based on height (will ignore 'density')\n" + + " " + FogSetting.FogType.EXPONENTIAL + ": 1/(e^(height*density)) \n" + + " " + FogSetting.FogType.EXPONENTIAL_SQUARED + ": 1/(e^((height*density)^2) \n"; //+ " "+ FogSetting.Type.TEXTURE_BASED + ": Use a provided 1D texture mapping (will ignore 'density', 'min', and 'max')\n"; - FogSetting.Type getHeightFogType(); - void setHeightFogType(FogSetting.Type newFarFogType); + FogSetting.FogType getHeightFogType(); + void setHeightFogType(FogSetting.FogType newFarFogType); MinDefaultMax HEIGHT_FOG_DENSITY_MIN_DEFAULT_MAX = new MinDefaultMax<>(0.01, 2.5, 50.0); String HEIGHT_FOG_DENSITY_DESC = "" diff --git a/src/main/resources/assets/lod/lang/en_us.json b/src/main/resources/assets/lod/lang/en_us.json index 962170490..038991f9c 100644 --- a/src/main/resources/assets/lod/lang/en_us.json +++ b/src/main/resources/assets/lod/lang/en_us.json @@ -59,6 +59,74 @@ "Disable vanilla fog", "DistantHorizons.config.client.graphics.fogQuality.disableVanillaFog.@tooltip": "§6True:§r disables Minecraft's fog on vanilla chunks.\n§6False:§r Minecraft renders fog like normal.\n\nMay cause issues with other mods that edit fog.\nDisable if vanilla chunks are completely covered in fog.", + + "DistantHorizons.config.client.graphics.fogQuality.advancedFog": + "Advanced Fog Options", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogStart": +"Far Fog Start", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogEnd": + "Far Fog End", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogMin": + "Far Fog Min", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogMax": + "Far Fog Max", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogType": + "Far Fog Type", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogDensity": + "Far Fog Density", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog": + "Height Fog Options", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMixMode": + "Height Fog Mix Mode", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMode": + "Height Fog Mode", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogHeight": + "Height Fog Set Height", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogStart": + "Height Fog Start", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogEnd": + "Height Fog End", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMin": + "Height Fog Min", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMax": + "Height Fog Max", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogType": + "Height Fog Type", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogDensity": + "Height Fog Density", + + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogStart.@tooltip": + "Where should the far fog start? \n\n '0.0': Fog start at player's position.\n '1.0': The fog-start's circle fit just in the lod render distance square.\n'1.414': The lod render distance square fit just in the fog-start's circle.\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogEnd.@tooltip": + "Where should the far fog end? \n\n '0.0': Fog end at player's position.\n '1.0': The fog-end's circle fit just in the lod render distance square.\n'1.414': The lod render distance square fit just in the fog-end's circle.\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogMin.@tooltip": + "What is the minimum fog thickness? \n\n '0.0': No fog at all.\n '1.0': Fully fog color.\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogMax.@tooltip": + "What is the maximum fog thickness? \n\n '0.0': No fog at all.\n '1.0': Fully fog color.\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogType.@tooltip": + "How the fog thickness should be calculated from distance?\n\nLINEAR: Linear based on distance (will ignore 'density')\nEXPONENTIAL: 1/(e^(distance*density)) \nEXPONENTIAL_SQUARED: 1/(e^((distance*density)^2) \n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.farFogDensity.@tooltip": + "What is the fog density? ", + + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMixMode.@tooltip": + "How the height should effect the fog thickness combined with the normal function? \n\nBASIC: No special height fog effect. Fog is calculated based on camera distance \nIGNORE_HEIGHT: Ignore height completely. Fog is calculated based on horizontal distance \nADDITION: heightFog + farFog \nMAX: max(heightFog, farFog) \nMULTIPLY: heightFog * farFog \nINVERSE_MULTIPLY: 1 - (1-heightFog) * (1-farFog) \nLIMITED_ADDITION: farFog + max(farFog, heightFog) \nMULTIPLY_ADDITION: farFog + farFog * heightFog \nINVERSE_MULTIPLY_ADDITION: farFog + 1 - (1-heightFog) * (1-farFog) \nAVERAGE: farFog*0.5 + heightFog*0.5 \n\nNote that for 'BASIC' mode and 'IGNORE_HEIGHT' mode, fog settings for height fog has no effect.\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMode.@tooltip": + "Where should the height fog be located? \n\nABOVE_CAMERA: Height fog starts from camera to the sky \nBELOW_CAMERA: Height fog starts from camera to the void \nABOVE_AND_BELOW_CAMERA: Height fog starts from camera to both the sky and the void \nABOVE_SET_HEIGHT: Height fog starts from a set height to the sky \nBELOW_SET_HEIGHT: Height fog starts from a set height to the void \nABOVE_AND_BELOW_SET_HEIGHT: Height fog starts from a set height to both the sky and the void \n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogHeight.@tooltip": + "If the height fog is calculated around a set height, what is that height position? ", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogStart.@tooltip": + "How far the start of height fog should offset? \n\n '0.0': Fog start with no offset.\n '1.0': Fog start with offset of the entire world's height. (Include depth)\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogEnd.@tooltip": + "How far the end of height fog should offset? \n\n '0.0': Fog end with no offset.\n '1.0': Fog end with offset of the entire world's height. (Include depth)\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMin.@tooltip": + "What is the minimum fog thickness? \n\n '0.0': No fog at all.\n '1.0': Fully fog color.\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogMax.@tooltip": + "What is the maximum fog thickness? \n\n '0.0': No fog at all.\n '1.0': Fully fog color.\n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogType.@tooltip": + "How the fog thickness should be calculated from height? \n\nLINEAR: Linear based on height (will ignore 'density')\nEXPONENTIAL: 1/(e^(height*density)) \nEXPONENTIAL_SQUARED: 1/(e^((height*density)^2) \n", + "DistantHorizons.config.client.graphics.fogQuality.advancedFog.heightFog.heightFogDensity.@tooltip": + "What is the fog density? ", + "DistantHorizons.config.client.graphics.advancedGraphics": "Advanced quality option", "DistantHorizons.config.client.graphics.advancedGraphics.lodTemplate": @@ -205,6 +273,44 @@ "Use world fog", "DistantHorizons.config.enum.FogColorMode.USE_SKY_COLOR": "Use sky color", + "DistantHorizons.config.enum.FogType.LINEAR": + "Linear", + "DistantHorizons.config.enum.FogType.EXPONENTIAL": + "Exponential", + "DistantHorizons.config.enum.FogType.EXPONENTIAL_SQUARED": + "Exponential Squared", + "DistantHorizons.config.enum.HeightFogMode.ABOVE_CAMERA": + "Above Camera", + "DistantHorizons.config.enum.HeightFogMode.BELOW_CAMERA": + "Below Camera", + "DistantHorizons.config.enum.HeightFogMode.ABOVE_AND_BELOW_CAMERA": + "Above And Below Camera", + "DistantHorizons.config.enum.HeightFogMode.ABOVE_SET_HEIGHT": + "Above Set Height", + "DistantHorizons.config.enum.HeightFogMode.BELOW_SET_HEIGHT": + "Below Set Height", + "DistantHorizons.config.enum.HeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT": + "Above And Below Set Height", + "DistantHorizons.config.enum.HeightFogMixMode.BASIC": + "Basic", + "DistantHorizons.config.enum.HeightFogMixMode.IGNORE_HEIGHT": + "Ignore Height", + "DistantHorizons.config.enum.HeightFogMixMode.ADDITION": + "Addition", + "DistantHorizons.config.enum.HeightFogMixMode.MAX": + "Max", + "DistantHorizons.config.enum.HeightFogMixMode.MULTIPLY": + "Multiply", + "DistantHorizons.config.enum.HeightFogMixMode.INVERSE_MULTIPLY": + "Inverse Multiply", + "DistantHorizons.config.enum.HeightFogMixMode.LIMITED_ADDITION": + "Limited Addition", + "DistantHorizons.config.enum.HeightFogMixMode.MULTIPLY_ADDITION": + "Multiply Addition", + "DistantHorizons.config.enum.HeightFogMixMode.INVERSE_MULTIPLY_ADDITION": + "Inverse Multiply Addition", + "DistantHorizons.config.enum.HeightFogMixMode.AVERAGE": + "Average", "DistantHorizons.config.enum.LodTemplate.CUBIC": "Cubic", "DistantHorizons.config.enum.LodTemplate.TRIANGULAR": diff --git a/src/main/resources/shaders/flat_shaded.frag b/src/main/resources/shaders/flat_shaded.frag index c9517ed58..60411fbe1 100644 --- a/src/main/resources/shaders/flat_shaded.frag +++ b/src/main/resources/shaders/flat_shaded.frag @@ -1,25 +1,42 @@ -#version 150 core in vec4 vertexColor; in vec3 vertexWorldPos; +in float vertexYPos; out vec4 fragColor; -uniform bool fogEnabled; -uniform bool nearFogEnabled; -uniform bool farFogEnabled; - +uniform float fogScale; +uniform float fogVerticalScale; uniform float nearFogStart; -uniform float nearFogEnd; -uniform float farFogStart; -uniform float farFogEnd; +uniform float nearFogLength; +uniform int fullFogMode; + +/* ========MARCO DEFINED BY RUNTIME CODE GEN========= + +float farFogStart; +float farFogLength; +float farFogMin; +float farFogRange; +float farFogDensity; + +float heightFogStart; +float heightFogLength; +float heightFogMin; +float heightFogRange; +float heightFogDensity; +*/ + uniform vec4 fogColor; - // method definitions -float getFogAlpha(float start, float end, float dist); - - +// ==== The below 5 methods will be run-time generated. ==== +float getNearFogThickness(float dist); +float getFarFogThickness(float dist); +float getHeightFogThickness(float dist); +float calculateFarFogDepth(float horizontal, float dist); +float calculateHeightFogDepth(float vertical, float realY); +float mixFogThickness(float near, float far, float height); +// ========================================================= /** * Fragment Shader @@ -29,55 +46,46 @@ float getFogAlpha(float start, float end, float dist); */ void main() { - // TODO: add a white texture to support Optifine shaders - //vec4 textureColor = texture(texImage, textureCoord); - //fragColor = vertexColor * textureColor; - - vec4 returnColor; - if (fogEnabled) - { - // add fog - - // no fog by default - float fogAlpha = 0; - - float dist = length(vertexWorldPos); + if (fullFogMode != 0) { + returnColor = vec4(fogColor.rgb, 1.0); + } else { + // TODO: add a white texture to support Optifine shaders + //vec4 textureColor = texture(texImage, textureCoord); + //fragColor = vertexColor * textureColor; + + float horizontalDist = length(vertexWorldPos.xz) * fogScale; + float heightDist = calculateHeightFogDepth( + vertexWorldPos.y, vertexYPos) * fogVerticalScale; + float farDist = calculateFarFogDepth(horizontalDist, + length(vertexWorldPos.xyz) * fogScale); + + float nearFogThickness = getNearFogThickness(horizontalDist); + float farFogThickness = getFarFogThickness(farDist); + float heightFogThickness = getHeightFogThickness(heightDist); + float mixedFogThickness = clamp(mixFogThickness( + nearFogThickness, farFogThickness, heightFogThickness), 0.0, 1.0); + + returnColor = mix(vertexColor, vec4(fogColor.rgb, 1.0), mixedFogThickness); - // less than because nearFogStart is farther away than nearFogEnd - if (nearFogEnabled && dist < nearFogStart) - { - fogAlpha = getFogAlpha(nearFogStart, nearFogEnd, dist); - } - else if (farFogEnabled) - { - fogAlpha = getFogAlpha(farFogStart, farFogEnd, dist); - } - - returnColor = mix(vertexColor, vec4(fogColor.xyz, 1), fogAlpha); } - else - { - // simple flat color - returnColor = vertexColor; - } - - //fragColor = vec4(0.7,0.6,0.5,1.0); + //fragColor = vec4(0.7,0.6,0.5,1.0); fragColor = vec4(returnColor.rgb,1.0); } - - - -/** - * Returns the fog strength for the given fragment. - * This is the same implementation as legacy OpenGL's Linear fog option. - * 1 = completely opaque fog - * 0 = no fog - */ -float getFogAlpha(float start, float end, float dist) -{ - float fogAlpha = 1 - ((end - dist) / (end - start)); - return clamp(fogAlpha, 0, 1); +float linearFog(float x, float fogStart, float fogLength, float fogMin, float fogRange) { + x = clamp((x-fogStart)/fogLength, 0.0, 1.0); + return fogMin + fogRange * x; } +float exponentialFog(float x, float fogStart, float fogLength, + float fogMin, float fogRange, float fogDensity) { + x = max((x-fogStart)/fogLength, 0.0) * fogDensity; + return fogMin + fogRange - fogRange/exp(x); +} + +float exponentialSquaredFog(float x, float fogStart, float fogLength, + float fogMin, float fogRange, float fogDensity) { + x = max((x-fogStart)/fogLength, 0.0) * fogDensity; + return fogMin + fogRange - fogRange/exp(x*x); +} \ No newline at end of file diff --git a/src/main/resources/shaders/standard.vert b/src/main/resources/shaders/standard.vert index 6b35cd1ab..04ab6ff4d 100644 --- a/src/main/resources/shaders/standard.vert +++ b/src/main/resources/shaders/standard.vert @@ -5,9 +5,11 @@ in vec4 color; out vec4 vertexColor; out vec3 vertexWorldPos; +out float vertexYPos; -uniform mat4 modelViewMatrix; -uniform mat4 projectionMatrix; +uniform mat4 combinedMatrix; +uniform vec3 modelOffset; +uniform float worldYOffset; uniform int worldSkyLight; uniform sampler2D lightMap; @@ -24,18 +26,11 @@ uniform sampler2D lightMap; */ void main() { - vec4 worldSpacePos = modelViewMatrix * vec4(vPosition.xyz,1); + vertexWorldPos = vPosition.xyz + modelOffset; + vertexYPos = vPosition.y + worldYOffset; + float light = (vPosition.a+0.5) / 256.0; - vertexColor = color * texture(lightMap, vec2(light,0.5)); - - vertexWorldPos = worldSpacePos.xyz; - // vec4 pos = projectionMatrix * worldSpacePos; - gl_Position = projectionMatrix * worldSpacePos; - /*pos.a = 1.0; - if (pos.x>0) pos.x=-1; else pos.x=1; - if (pos.y>0) pos.y=-1; else pos.y=1; - pos.z = 0.5; - gl_Position = pos;*/ + gl_Position = combinedMatrix * vec4(vertexWorldPos, 1.0); }