From 6049840aa632e847cee214c0b755b508698dce63 Mon Sep 17 00:00:00 2001 From: tom lee Date: Tue, 14 Dec 2021 15:03:03 +0800 Subject: [PATCH] MAJOR Renderer Rewrite Rewriten almost all stuff in core.render. Changes made: 1. Added LightmapTexture object 2. Moved&Renamed LodShader & LodShaderProgram to be under render/objects/ 3. All class under render/objects are actual objects for OpenGL 4. Add LodRenderProgram which is a ShaderProgram + VertexAttribute. If we change the method on how we render stuff (Like modifying input/output), we should change this file as this is the repesentation of the Rendering Program that we are using. 5. Add VertexAttribute, and a GL43+ vesion of it. I implemented both versions because the GL43+ will get a noticable speedup due to some additions to OpenGL standard. However I still included older version for backward compatibilities. To access VertexAttributes, it is recommended to access it though the VertexAttribute abstract class's method. 6. Rewritten the main renderLod() method. Now it will cache as much stuff as possible. 7. To do '6.', now the renderer has a setup() and a cleanup() method, both that must be called inside Render Thread. The renderer should automatically detects when it needs to call the setup() and cleanup(). However, you can expicitly request the renderer to do cleanup() on next rendering by calling markForCleanup(). 8. ...I think that's all? Don't quite remember tbh. Hopefully I didn't miss any points. Note: This overhaul of the renderer means that THERE WILL BE BUGS. I can't test all versions on both Nvidia and AMD cards with all OPENGL versions so please report any bugs to me straight away. Thanks in advance! Note 2: I will add more docs in the source code next. --- .../com/seibel/lod/core/api/EventApi.java | 4 +- .../core/objects/rending/LodFogConfig.java | 43 ----- .../com/seibel/lod/core/render/GLProxy.java | 102 ++--------- .../seibel/lod/core/render/LodFogConfig.java | 75 ++++++++ .../lod/core/render/LodRenderProgram.java | 162 ++++++++++++++++++ .../core/render/objects/LightmapTexture.java | 54 ++++++ .../LodShader.java => objects/Shader.java} | 86 ++++------ .../ShaderProgram.java} | 125 +++++--------- .../core/render/objects/VertexAttribute.java | 104 +++++++++++ .../objects/VertexAttributePostGL43.java | 69 ++++++++ .../objects/VertexAttributePreGL43.java | 133 ++++++++++++++ 11 files changed, 684 insertions(+), 273 deletions(-) delete mode 100644 src/main/java/com/seibel/lod/core/objects/rending/LodFogConfig.java create mode 100644 src/main/java/com/seibel/lod/core/render/LodFogConfig.java create mode 100644 src/main/java/com/seibel/lod/core/render/LodRenderProgram.java create mode 100644 src/main/java/com/seibel/lod/core/render/objects/LightmapTexture.java rename src/main/java/com/seibel/lod/core/render/{shader/LodShader.java => objects/Shader.java} (63%) rename src/main/java/com/seibel/lod/core/render/{shader/LodShaderProgram.java => objects/ShaderProgram.java} (61%) create mode 100644 src/main/java/com/seibel/lod/core/render/objects/VertexAttribute.java create mode 100644 src/main/java/com/seibel/lod/core/render/objects/VertexAttributePostGL43.java create mode 100644 src/main/java/com/seibel/lod/core/render/objects/VertexAttributePreGL43.java 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