diff --git a/src/main/java/com/seibel/lod/core/api/EventApi.java b/src/main/java/com/seibel/lod/core/api/EventApi.java index d5c9ea638..bf4455843 100644 --- a/src/main/java/com/seibel/lod/core/api/EventApi.java +++ b/src/main/java/com/seibel/lod/core/api/EventApi.java @@ -121,6 +121,7 @@ public class EventApi { // the player just unloaded a world/dimension ThreadMapUtil.clearMaps(); + ClientApi.renderer.markForCleanup(); new Thread(() -> checkIfDisconnectedFromServer()).start(); } @@ -155,11 +156,12 @@ public class EventApi // prevent issues related to the buffer builder // breaking or retaining previous data when changing worlds. + ClientApi.renderer.markForCleanup(); ClientApi.renderer.destroyBuffers(); recalculateWidths = true; + // TODO: Check if after the refactoring, is this still needed ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory); - // make sure the nulled objects are freed. // (this prevents an out of memory error when // changing worlds) diff --git a/src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java b/src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java deleted file mode 100644 index 0fcdba0db..000000000 --- a/src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL v3 License. - * - * Copyright (C) 2020 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.core.objects.rending; - -import com.seibel.lod.core.enums.rendering.FogDistance; -import com.seibel.lod.core.enums.rendering.FogDrawMode; - -/** - * This object is just a replacement for an array - * to make things easier to understand in the LodRenderer. - * - * @author James Seibel - * @version 11-26-2021 - */ -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; -} diff --git a/src/main/java/com/seibel/lod/core/render/GLProxy.java b/src/main/java/com/seibel/lod/core/render/GLProxy.java index ee7aa0b0b..201782071 100644 --- a/src/main/java/com/seibel/lod/core/render/GLProxy.java +++ b/src/main/java/com/seibel/lod/core/render/GLProxy.java @@ -2,7 +2,7 @@ * This file is part of the Distant Horizon mod (formerly the LOD Mod), * licensed under the GNU GPL v3 License. * - * Copyright (C) 2020 James Seibel + * Copyright (C) 2021 James Seibel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,8 +27,6 @@ import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GLCapabilities; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -36,8 +34,6 @@ import com.seibel.lod.core.ModInfo; import com.seibel.lod.core.api.ClientApi; import com.seibel.lod.core.enums.config.GpuUploadMethod; import com.seibel.lod.core.enums.rendering.GLProxyContext; -import com.seibel.lod.core.render.shader.LodShader; -import com.seibel.lod.core.render.shader.LodShaderProgram; import com.seibel.lod.core.util.SingletonHandler; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; @@ -84,19 +80,11 @@ public class GLProxy /** the proxyWorker's GL capabilities */ public final GLCapabilities proxyWorkerGlCapabilities; - - - /** This program contains all shaders required when rendering LODs */ - public LodShaderProgram lodShaderProgram; - /** This is the VAO that is used when rendering */ - public final int vertexArrayObjectId; - /** This is the 2D texture that holds MC's lightmap */ - public final int lightMapTextureId; - - /** Requires OpenGL 4.5, and offers the best buffer uploading */ public final boolean bufferStorageSupported; + public final boolean openGL43VertexAttributeSupported; + /** Requires OpenGL 3.0 */ public final boolean mapBufferRangeSupported; @@ -109,6 +97,8 @@ public class GLProxy */ private GLProxy() { + // this must be created on minecraft's render context to work correctly + ClientApi.LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error."); // getting Minecraft's context has to be done on the render thread, @@ -147,10 +137,6 @@ public class GLProxy proxyWorkerGlCapabilities = GL.createCapabilities(); - - - - //==================================// // get any GPU related capabilities // //==================================// @@ -168,7 +154,8 @@ public class GLProxy MC.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 2.0 or greater.")); } - + // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after + openGL43VertexAttributeSupported = minecraftGlCapabilities.OpenGL43; // get specific capabilities bufferStorageSupported = lodBuilderGlCapabilities.glBufferStorage != 0; @@ -218,27 +205,6 @@ public class GLProxy CONFIG.client().advanced().buffers().setGpuUploadMethod(uploadMethod); ClientApi.LOGGER.info("GPU Vendor [" + vendor + "], Upload method set to [" + uploadMethod + "]."); } - - - - - //==============// - // shader setup // - //==============// - - setGlContext(GLProxyContext.MINECRAFT); - - createShaderProgram(); - - // Note: VAO objects can not be shared between contexts, - // this must be created on minecraft's render context to work correctly - vertexArrayObjectId = GL30.glGenVertexArrays(); - - lightMapTextureId = GL30.glGenTextures(); - - - - //==========// // clean up // //==========// @@ -246,53 +212,10 @@ public class GLProxy // Since this is created on the render thread, make sure the Minecraft context is used in the end setGlContext(GLProxyContext.MINECRAFT); - // GLProxy creation success ClientApi.LOGGER.info(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day."); } - /** - * Creates all required shaders - * @throws RuntimeException - * @throws FileNotFoundException - */ - public void createShaderProgram() - { - LodShader vertexShader = null; - LodShader fragmentShader = null; - - // get the shaders from the resource folder - // Use File.separator ONLY if the file is external (not in the resourse), otherwise use "/" - vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/standard.vert", false); - fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/flat_shaded.frag", false); - - // this can be used when testing shaders, - // since we can't hot swap the files in the resource folder - // vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "C:/Users/James Seibel/Desktop/shaders/standard.vert", true); - // fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/flat_shaded.frag", true); - - - // create the shaders - - lodShaderProgram = new LodShaderProgram(); - - // Attach the compiled shaders to the program, throws RuntimeException on link error - lodShaderProgram.attachShader(vertexShader); - lodShaderProgram.attachShader(fragmentShader); - - // activate the fragment shader output - GL30.glBindFragDataLocation(lodShaderProgram.id, 0, "fragColor"); - - // attach the shader program to the OpenGL context - lodShaderProgram.link(); - - // after the shaders have been attached to the program - // we don't need their OpenGL references anymore - GL20.glDeleteShader(vertexShader.id); - GL20.glDeleteShader(fragmentShader.id); - } - - /** * A wrapper function to make switching contexts easier.
* Does nothing if the calling thread is already using newContext. @@ -362,22 +285,17 @@ public class GLProxy + "no context [0]."); } + public static boolean hasInstance() { + return instance != null; + } public static GLProxy getInstance() { if (instance == null) instance = new GLProxy(); - return instance; } - - - - - - - /** * Asynchronously calls the given runnable on proxy's OpenGL context. * Useful for creating/destroying OpenGL objects in a thread diff --git a/src/main/java/com/seibel/lod/core/render/LodFogConfig.java b/src/main/java/com/seibel/lod/core/render/LodFogConfig.java new file mode 100644 index 000000000..7c2d2abe1 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/LodFogConfig.java @@ -0,0 +1,75 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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.handlers.IReflectionHandler; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; + +/** + * This object is just a replacement for an array + * to make things easier to understand in the LodRenderer. + * + * @author James Seibel + * @version 11-26-2021 + */ +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; + } + +} diff --git a/src/main/java/com/seibel/lod/core/render/LodRenderProgram.java b/src/main/java/com/seibel/lod/core/render/LodRenderProgram.java new file mode 100644 index 000000000..667a20d1b --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/LodRenderProgram.java @@ -0,0 +1,162 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2021 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.core.render; + +import java.awt.Color; + +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL42; +import org.lwjgl.opengl.GL43; + +import com.seibel.lod.core.enums.rendering.FogDistance; +import com.seibel.lod.core.enums.rendering.FogDrawMode; +import com.seibel.lod.core.objects.math.Mat4f; +import com.seibel.lod.core.objects.math.Vec3f; +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.util.LodUtil; + +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"; + + public final VertexAttribute vao; + + // Attributes + public final int posAttrib; + public final int colAttrib; + public final int blockSkyLightAttrib; + public final int blockLightAttrib; + // 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 lightMapUniform; + // Fog Uniforms + public final int fogEnabledUniform; + public final int nearFogEnabledUniform; + public final int farFogEnabledUniform; + public final int nearFogStartUniform; + public final int nearFogEndUniform; + public final int farFogStartUniform; + public final int farFogEndUniform; + + public LodRenderProgram() { + super(VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH, "fragColor"); + + super.bind(); + + posAttrib = getAttributeLocation("vPosition"); + colAttrib = getAttributeLocation("color"); + blockSkyLightAttrib = getAttributeLocation("blockSkyLight"); + blockLightAttrib = getAttributeLocation("blockLight"); + + mvmUniform = getUniformLocation("modelViewMatrix"); + projUniform = getUniformLocation("projectionMatrix"); + cameraUniform = getUniformLocation("cameraPos"); + fogColorUniform = getUniformLocation("fogColor"); + // skyLightUniform = getUniformLocation("worldSkyLight"); + lightMapUniform = getUniformLocation("lightMap"); + + // Fog uniforms + fogEnabledUniform = getUniformLocation("fogEnabled"); + nearFogEnabledUniform = getUniformLocation("nearFogEnabled"); + farFogEnabledUniform = getUniformLocation("farFogEnabled"); + // near + nearFogStartUniform = getUniformLocation("nearFogStart"); + nearFogEndUniform = getUniformLocation("nearFogEnd"); + // far + farFogStartUniform = getUniformLocation("farFogStart"); + farFogEndUniform = getUniformLocation("farFogEnd"); + + // TODO: Add better use of the LODFormat thing + int vertexByteCount = LodUtil.LOD_VERTEX_FORMAT.getByteSize(); + if (GLProxy.getInstance().openGL43VertexAttributeSupported) + vao = new VertexAttributePostGL43(); + else + vao = new VertexAttributePreGL43(); + vao.bind(); + vao.setVertexAttribute(0, posAttrib, VertexAttribute.VertexPointer.addVec3Pointer(false)); + vao.setVertexAttribute(0, colAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(4, true)); + vao.setVertexAttribute(0, blockSkyLightAttrib, VertexAttribute.VertexPointer.addUnsignedBytePointer(false)); + vao.setVertexAttribute(0, blockLightAttrib, VertexAttribute.VertexPointer.addUnsignedBytePointer(false)); + vao.completeAndCheck(vertexByteCount); + //vao.unbind(); + //super.unbind(); + } + + // Override ShaderProgram.bind() + public void bind() { + super.bind(); + vao.bind(); + } + // Override ShaderProgram.unbind() + public void unbind() { + super.unbind(); + vao.unbind(); + } + + // Override ShaderProgram.free() + public void free() { + vao.free(); + super.free(); + } + + public void bindVertexBuffer(int vbo) { + vao.bindBufferToAllBindingPoint(vbo); + } + + public void unbindVertexBuffer() { + vao.unbindBufferFromAllBindingPoint(); + } + + public void fillUniformData(Mat4f modelViewMatrix, Mat4f projectionMatrix, Vec3f cameraPos, Color fogColor, int skyLight, int lightmapBindPoint) { + super.bind(); + // uniforms + setUniform(mvmUniform, modelViewMatrix); + setUniform(projUniform, projectionMatrix); + setUniform(cameraUniform, cameraPos); + setUniform(fogColorUniform, fogColor); + // setUniform(skyLightUniform, skyLight); + setUniform(lightMapUniform, lightmapBindPoint); + } + + public void fillUniformDataForFog(LodFogConfig fogSettings) { + super.bind(); + 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); + } + } + +} diff --git a/src/main/java/com/seibel/lod/core/render/objects/LightmapTexture.java b/src/main/java/com/seibel/lod/core/render/objects/LightmapTexture.java new file mode 100644 index 000000000..367034067 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/objects/LightmapTexture.java @@ -0,0 +1,54 @@ +package com.seibel.lod.core.render.objects; + +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; + +public class LightmapTexture { + public final int id; + + public LightmapTexture() { + id = GL30.glGenTextures(); + bind(); + GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_BORDER); + GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_BORDER); + GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_NEAREST); + GL20.glTexParameteri(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_NEAREST); + } + + public void bind() { + GL20.glBindTexture(GL20.GL_TEXTURE_2D, id); + } + public void unbind() { + GL20.glBindTexture(GL20.GL_TEXTURE_2D, 0); + } + + public void free() { + GL20.glDeleteTextures(id); + } + + public void fillData(int lightMapWidth, int lightMapHeight, int[] pixels) { + if (pixels.length != lightMapWidth*lightMapHeight) + throw new RuntimeException("Lightmap Width*Height not equal to pixels provided!"); + + // comment me out to see when the lightmap is changing +// boolean same = true; +// int badIndex = 0; +// if (testArray != null && pixels != null) +// for (int i = 0; i < pixels.length; i++) +// { +// if(pixels[i] != testArray[i]) +// { +// same = false; +// badIndex = i; +// break; +// } +// } +// testArray = pixels; +// MC.sendChatMessage(same + " " + badIndex); + + // comment this line out to prevent uploading the new lightmap + GL20.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_RGBA, lightMapWidth, + lightMapHeight, 0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixels); + } + +} diff --git a/src/main/java/com/seibel/lod/core/render/shader/LodShader.java b/src/main/java/com/seibel/lod/core/render/objects/Shader.java similarity index 63% rename from src/main/java/com/seibel/lod/core/render/shader/LodShader.java rename to src/main/java/com/seibel/lod/core/render/objects/Shader.java index 91a1ca397..f071c107e 100644 --- a/src/main/java/com/seibel/lod/core/render/shader/LodShader.java +++ b/src/main/java/com/seibel/lod/core/render/objects/Shader.java @@ -2,7 +2,7 @@ * This file is part of the Distant Horizon mod (formerly the LOD Mod), * licensed under the GNU GPL v3 License. * - * Copyright (C) 2020 James Seibel + * Copyright (C) 2021 James Seibel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.lod.core.render.shader; +package com.seibel.lod.core.render.objects; import java.io.BufferedReader; import java.io.FileInputStream; @@ -28,6 +28,8 @@ import java.io.InputStreamReader; import org.lwjgl.opengl.GL20; +import com.seibel.lod.core.api.ClientApi; + /** * This object holds a OpenGL reference to a shader * and allows for reading in and compiling a shader file. @@ -35,31 +37,42 @@ import org.lwjgl.opengl.GL20; * @author James Seibel * @version 11-8-2021 */ -public class LodShader +public class Shader { /** OpenGL shader ID */ public final int id; - - - /** Creates a shader with specified type. */ - public LodShader(int type) - { - id = GL20.glCreateShader(type); - } - - - - /** - * Loads a shader from file. - * + /** Creates a shader with specified type. * @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. * @param path File path of the shader * @param absoluteFilePath If false the file path is relative to the resource jar folder. - * @throws Exception if the shader fails to compile + * @throws RuntimeException if the shader fails to compile */ - public static LodShader loadShader(int type, String path, boolean absoluteFilePath) + public Shader(int type, String path, boolean absoluteFilePath) { + ClientApi.LOGGER.info("Loading shader at "+path); + // Create an empty shader object + id = GL20.glCreateShader(type); + StringBuilder source = loadFile(path, absoluteFilePath); + GL20.glShaderSource(id, source); + + GL20.glCompileShader(id); + // check if the shader compiled + int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS); + if (status != GL20.GL_TRUE) { + String message = "Shader compiler error. Details: "+GL20.glGetShaderInfoLog(id); + free(); // important! + throw new RuntimeException(message); + } + ClientApi.LOGGER.info("Shader at "+path+" loaded sucessfully."); + } + + // REMEMBER to always free the resource! + public void free() { + GL20.glDeleteShader(id); + } + + private StringBuilder loadFile(String path, boolean absoluteFilePath) { StringBuilder stringBuilder = new StringBuilder(); try @@ -70,7 +83,7 @@ public class LodShader // Throws FileNotFoundException in = new FileInputStream(path); // Note: this should use OS path seperator } else { - in = LodShader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/' + in = Shader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/' if (in == null) { throw new FileNotFoundException("Shader file not found in resource: "+path); } @@ -86,39 +99,6 @@ public class LodShader { throw new RuntimeException("Unable to load shader from file [" + path + "]. Error: " + e.getMessage()); } - CharSequence shaderFileSource = stringBuilder.toString(); - - return createShader(type, shaderFileSource); + return stringBuilder; } - - /** - * Creates a shader with the specified type and source. - * - * @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. - * @param source Source of the shader - * @throws Exception if the shader fails to compile - */ - public static LodShader createShader(int type, CharSequence source) - { - LodShader shader = new LodShader(type); - GL20.glShaderSource(shader.id, source); - shader.compile(); - - return shader; - } - - /** - * Compiles the shader and checks its status afterwards. - * @throws Exception if the shader fails to compile - */ - public void compile() - { - GL20.glCompileShader(id); - - // check if the shader compiled - int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS); - if (status != GL20.GL_TRUE) - throw new RuntimeException("Shader compiler error. Details: "+GL20.glGetShaderInfoLog(id)); - } - } diff --git a/src/main/java/com/seibel/lod/core/render/shader/LodShaderProgram.java b/src/main/java/com/seibel/lod/core/render/objects/ShaderProgram.java similarity index 61% rename from src/main/java/com/seibel/lod/core/render/shader/LodShaderProgram.java rename to src/main/java/com/seibel/lod/core/render/objects/ShaderProgram.java index e8c59b8dd..5d82438da 100644 --- a/src/main/java/com/seibel/lod/core/render/shader/LodShaderProgram.java +++ b/src/main/java/com/seibel/lod/core/render/objects/ShaderProgram.java @@ -17,12 +17,13 @@ * along with this program. If not, see . */ -package com.seibel.lod.core.render.shader; +package com.seibel.lod.core.render.objects; import java.awt.Color; import java.nio.FloatBuffer; import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; import org.lwjgl.system.MemoryStack; import com.seibel.lod.core.objects.math.Mat4f; @@ -39,126 +40,84 @@ import com.seibel.lod.core.objects.math.Vec3f; * @author James Seibel * @version 11-26-2021 */ -public class LodShaderProgram +public class ShaderProgram { /** Stores the handle of the program. */ public final int id; /** Creates a shader program. */ - public LodShaderProgram() + // FIXME: A better way to set the fragData output name + public ShaderProgram(String vert, String frag, String fragDataOutputName) { + Shader vertShader = new Shader(GL20.GL_VERTEX_SHADER, vert, false); + Shader fragShader = new Shader(GL20.GL_FRAGMENT_SHADER, frag, false); + id = GL20.glCreateProgram(); + + GL20.glAttachShader(this.id, vertShader.id); + GL20.glAttachShader(this.id, fragShader.id); + //GL30.glBindFragDataLocation(id, 0, fragDataOutputName); + GL20.glLinkProgram(this.id); + + vertShader.free(); // important! + fragShader.free(); // important! + + int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS); + if (status != GL20.GL_TRUE) { + String message = "Shader Link Error. Details: "+GL20.glGetProgramInfoLog(this.id); + free(); // important! + throw new RuntimeException(message); + } } - - /** Calls GL20.glUseProgram(this.id) */ - public void use() + public void bind() { GL20.glUseProgram(id); } - - /** - * Calls GL20.glAttachShader(this.id, shader.id) - * - * @param shader Shader to get attached - */ - public void attachShader(LodShader shader) - { - GL20.glAttachShader(this.id, shader.id); + public void unbind() { + GL20.glUseProgram(0); } - - /** - * Links the shader program to the current OpenGL context. - * @throws Exception Exception if the program failed to link - */ - public void link() + // REMEMBER to always free the resource! + public void free() { - GL20.glLinkProgram(this.id); - checkLinkStatus(); + GL20.glDeleteProgram(id); } - /** - * Checks if the program was linked successfully. - * @throws Exception if the program failed to link - */ - public void checkLinkStatus() - { - int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS); - if (status != GL20.GL_TRUE) - throw new RuntimeException("Shader Link Error. Details: "+GL20.glGetProgramInfoLog(this.id)); - } - - - - - /** + /** WARNING: Slow native call! Cache it if possible! * Gets the location of an attribute variable with specified name. * Calls GL20.glGetAttribLocation(id, name) * * @param name Attribute name - * + * @throws RuntimeException if attribute not found * @return Location of the attribute */ public int getAttributeLocation(CharSequence name) { - return GL20.glGetAttribLocation(id, name); + int i = GL20.glGetAttribLocation(id, name); + if (i==-1) throw new RuntimeException("Attribute name not found: "+name); + return i; } - /** - * Calls GL20.glEnableVertexAttribArray(location) - * - * @param location Location of the vertex attribute - */ - public void enableVertexAttribute(int location) - { - GL20.glEnableVertexAttribArray(location); - } - - /** - * Calls GL20.glDisableVertexAttribArray(location) - * - * @param location Location of the vertex attribute - */ - public void disableVertexAttribute(int location) - { - GL20.glDisableVertexAttribArray(location); - } - - /** - * Sets the vertex attribute pointer. - * Calls GL20.glVertexAttribPointer(...) - * - * @param location Location of the vertex attribute - * @param size Number of values per vertex - * @param stride Offset between consecutive generic vertex attributes in - * bytes - * @param offset Offset of the first component of the first generic vertex - * attribute in bytes - */ - public void pointVertexAttribute(int location, int size, int stride, int offset) - { - GL20.glVertexAttribPointer(location, size, GL20.GL_FLOAT, false, stride, offset); - } - - /** + /** WARNING: Slow native call! Cache it if possible! * Gets the location of a uniform variable with specified name. * Calls GL20.glGetUniformLocation(id, name) * * @param name Uniform name - * - * @return -1 = error value, 0 = first value, 1 = second value, etc. + * @throws RuntimeException if uniform not found + * @return Location of the Uniform */ public int getUniformLocation(CharSequence name) { - return GL20.glGetUniformLocation(id, name); + int i = GL20.glGetUniformLocation(id, name); + if (i==-1) throw new RuntimeException("Uniform name not found: "+name); + return i; } - - public void setUniform(int location, boolean value) { + // This use -1 for false as that equals all one set GL20.glUniform1i(location, value ? 1 : 0); } @@ -198,6 +157,4 @@ public class LodShaderProgram GL20.glUniform4f(location, value.getRed() / 256.0f, value.getGreen() / 256.0f, value.getBlue() / 256.0f, value.getAlpha() / 256.0f); } - - } diff --git a/src/main/java/com/seibel/lod/core/render/objects/VertexAttribute.java b/src/main/java/com/seibel/lod/core/render/objects/VertexAttribute.java new file mode 100644 index 000000000..cdd6d6a23 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/objects/VertexAttribute.java @@ -0,0 +1,104 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2021 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.core.render.objects; + +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; + +import com.seibel.lod.core.api.ClientApi; + +public abstract class VertexAttribute { + + public static final class VertexPointer { + public final int elementCount; + public final int glType; + public final boolean normalized; + public final int byteSize; + public VertexPointer(int elementCount, int glType, boolean normalized, int byteSize) { + this.elementCount = elementCount; + this.glType = glType; + this.normalized = normalized; + this.byteSize = byteSize; + } + public static VertexPointer addFloatPointer(boolean normalized) { + return new VertexPointer(1, GL20.GL_FLOAT, normalized, 4); + } + public static VertexPointer addVec2Pointer(boolean normalized) { + return new VertexPointer(2, GL20.GL_FLOAT, normalized, 8); + } + public static VertexPointer addVec3Pointer(boolean normalized) { + return new VertexPointer(3, GL20.GL_FLOAT, normalized, 12); + } + public static VertexPointer addVec4Pointer(boolean normalized) { + return new VertexPointer(1, GL20.GL_FLOAT, normalized, 16); + } + public static VertexPointer addUnsignedBytePointer(boolean normalized) { + return new VertexPointer(1, GL20.GL_UNSIGNED_BYTE, normalized, 1); + } + public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized) { + return new VertexPointer(elementCount, GL20.GL_UNSIGNED_BYTE, normalized, elementCount); + } + public static VertexPointer addIntPointer(boolean normalized) { + return new VertexPointer(1, GL20.GL_INT, normalized, 4); + } + public static VertexPointer addIvec2Pointer(boolean normalized) { + return new VertexPointer(2, GL20.GL_INT, normalized, 8); + } + public static VertexPointer addIvec3Pointer(boolean normalized) { + return new VertexPointer(3, GL20.GL_INT, normalized, 12); + } + public static VertexPointer addIvec4Pointer(boolean normalized) { + return new VertexPointer(4, GL20.GL_INT, normalized, 16); + } + } + + + /** Stores the handle of the VertexAttribute. */ + public final int id; + + protected VertexAttribute() { + id = GL30.glGenVertexArrays(); + } + + public void bind() { + GL30.glBindVertexArray(id); + } + public void unbind() { + GL30.glBindVertexArray(0); + } + + // REMEMBER to always free the resource! + public void free() { + GL30.glDeleteVertexArrays(id); + } + + // Requires Vao binded + public abstract void bindBufferToAllBindingPoint(int buffer); + // Requires Vao binded + public abstract void bindBufferToBindingPoint(int buffer, int bindingPoint); + // Requires Vao binded + public abstract void unbindBufferFromAllBindingPoint(); + // Requires Vao binded + public abstract void unbindBufferFromBindingPoint(int bindingPoint); + // Requires Vao binded + public abstract void setVertexAttribute(int bindingPoint, int attributeIndex, VertexPointer attribute); + // Requires Vao binded + public abstract void completeAndCheck(int expectedStrideSize); +} diff --git a/src/main/java/com/seibel/lod/core/render/objects/VertexAttributePostGL43.java b/src/main/java/com/seibel/lod/core/render/objects/VertexAttributePostGL43.java new file mode 100644 index 000000000..9ee2b7c49 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/render/objects/VertexAttributePostGL43.java @@ -0,0 +1,69 @@ +package com.seibel.lod.core.render.objects; + +import java.util.ArrayList; + +import org.lwjgl.opengl.GL43; + +import com.seibel.lod.core.api.ClientApi; +import com.seibel.lod.core.render.objects.VertexAttribute.VertexPointer; + +// In OpenGL 4.3 and later, Vertex Attribute got a make-over. +// Now it provides support for buffer binding points natively. +// This means that setting up the VAO just use ONE native call when +// binding to a buffer. +// +// Since I no longer needs to implement binding points, I also no +// longer needs to keep track of Pointers. + +public final class VertexAttributePostGL43 extends VertexAttribute { + + int numberOfBindingPoints = 0; + int strideSize = 0; + + ArrayList pointersBuilder; + + public VertexAttributePostGL43() { + super(); + } + + @Override + public void bindBufferToAllBindingPoint(int buffer) { + for (int i=0; i> bindingPointsToIndexBuilder; + ArrayList pointersBuilder; + + public VertexAttributePreGL43() { + super(); + bindingPointsToIndexBuilder = new TreeMap>(); + pointersBuilder = new ArrayList(); + } + + @Override + public void bindBufferToAllBindingPoint(int buffer) { + GL20.glBindBuffer(GL20.GL_ARRAY_BUFFER, buffer); + + for (int i=0; i intArray = bindingPointsToIndexBuilder.get(bindingPoint); + if (intArray == null) { + intArray = new TreeSet(); + bindingPointsToIndexBuilder.put(bindingPoint, intArray); + } + intArray.add(attributeIndex); + + while (pointersBuilder.size() <= attributeIndex) { + // This is dumb, but ArrayList doesn't have a resize, And this code + // should only be ran when it's building the Vertex Attribute anyways. + pointersBuilder.add(null); + } + pointersBuilder.set(attributeIndex, attribute); + } + + @Override + public void completeAndCheck(int expectedStrideSize) { + int maxBindPointNumber = bindingPointsToIndexBuilder.lastKey(); + bindingPointsToIndex = new int[maxBindPointNumber+1][]; + + bindingPointsToIndexBuilder.forEach((Integer i, TreeSet set) -> { + bindingPointsToIndex[i] = new int[set.size()]; + Iterator iter = set.iterator(); + for (int j = 0; j