diff --git a/src/main/java/com/seibel/lod/enums/GlProxyContext.java b/src/main/java/com/seibel/lod/enums/GlProxyContext.java index 512b46c7b..f01c2c4b3 100644 --- a/src/main/java/com/seibel/lod/enums/GlProxyContext.java +++ b/src/main/java/com/seibel/lod/enums/GlProxyContext.java @@ -20,10 +20,10 @@ package com.seibel.lod.enums; /** - * Minecraft, Lod_Builder, None + * Minecraft, Lod_Builder, Lod_Render, None * * @author James Seibel - * @version 10-1-2021 + * @version 10-31-2021 */ public enum GlProxyContext { @@ -33,6 +33,9 @@ public enum GlProxyContext /** The context we send buffers to the GPU on */ LOD_BUILDER, + /** The context we draw LODs on */ + LOD_RENDER, + /** used to un-bind threads */ NONE, } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 1294af5fd..b68701b76 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -31,6 +31,7 @@ import com.seibel.lod.builders.worldGeneration.LodGenWorker; import com.seibel.lod.builders.worldGeneration.LodWorldGenerator; import com.seibel.lod.config.LodConfig; import com.seibel.lod.enums.DistanceGenerationMode; +import com.seibel.lod.enums.GlProxyContext; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.LodWorld; import com.seibel.lod.objects.RegionPos; @@ -55,18 +56,12 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; * This handles all events sent to the client, * and is the starting point for most of the mod. * @author James_Seibel - * @version 10-23-2021 + * @version 10-31-2021 */ public class ClientProxy { public static final Logger LOGGER = LogManager.getLogger("LOD"); - /** - * there is some setup that should only happen once, - * once this is true that setup has completed - */ - private boolean firstTimeSetupComplete = false; - private static final LodWorld lodWorld = new LodWorld(); private static final LodBuilder lodBuilder = new LodBuilder(); private static final LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); @@ -108,13 +103,11 @@ public class ClientProxy // clear any out of date objects mc.clearFrameObjectCache(); + // make sure the GlProxy is created before the LodBufferBuilder needs it + GlProxy glProxy = GlProxy.getInstance(); + try { - // only run the first time setup once - if (!firstTimeSetupComplete) - firstFrameSetup(); - - if (mc == null || mc.getPlayer() == null || lodWorld.getIsWorldNotLoaded()) return; @@ -138,6 +131,7 @@ public class ClientProxy profiler.pop(); // get out of "terrain" profiler.push("LOD"); + glProxy.setGlContext(GlProxyContext.LOD_RENDER); renderer.drawLODs(lodDim, mcModelViewMatrix, partialTicks, mc.getProfiler()); profiler.pop(); // end LOD @@ -154,6 +148,10 @@ public class ClientProxy LOGGER.error("client proxy: " + e.getMessage()); e.printStackTrace(); } + finally + { + glProxy.setGlContext(GlProxyContext.MINECRAFT); + } } /** used in a development environment to change settings on the fly */ @@ -357,16 +355,6 @@ public class ClientProxy DetailDistanceUtil.updateSettings(); } - - /** This event is called once during the first frame Minecraft renders in the world. */ - public void firstFrameSetup() - { - // make sure the GlProxy is created before the LodBufferBuilder needs it - GlProxy.getInstance(); - - firstTimeSetupComplete = true; - } - /** this method reset some static data every time we change world */ private void resetMod() { diff --git a/src/main/java/com/seibel/lod/proxy/GlProxy.java b/src/main/java/com/seibel/lod/proxy/GlProxy.java index 9aa4567eb..d1f49c3a4 100644 --- a/src/main/java/com/seibel/lod/proxy/GlProxy.java +++ b/src/main/java/com/seibel/lod/proxy/GlProxy.java @@ -21,6 +21,7 @@ package com.seibel.lod.proxy; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GLCapabilities; import com.mojang.blaze3d.systems.RenderSystem; @@ -40,7 +41,7 @@ import com.seibel.lod.wrappers.MinecraftWrapper; * https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one

* * @author James Seibel - * @version 10-23-2021 + * @version 10-31-2021 */ public class GlProxy { @@ -50,14 +51,19 @@ public class GlProxy /** Minecraft's GLFW window */ public final long minecraftGlContext; - /** Minecraft's GL context */ + /** Minecraft's GL capabilities */ public final GLCapabilities minecraftGlCapabilities; /** the LodBuilder's GLFW window */ public final long lodBuilderGlContext; - /** the LodBuilder's GL context */ + /** the LodBuilder's GL capabilities */ public final GLCapabilities lodBuilderGlCapabilities; + /** the LodRender's GLFW window */ + public final long lodRenderGlContext; + /** the LodRender's GL capabilities */ + public final GLCapabilities lodRenderGlCapabilities; + /** * This is just used for debugging, hopefully it can be removed once * the context switching is more stable. @@ -93,44 +99,27 @@ public class GlProxy minecraftGlContext = GLFW.glfwGetCurrentContext(); minecraftGlCapabilities = GL.getCapabilities(); - // create the LodBuilder's context - // Hopefully this shouldn't cause any issues with other mods that need custom contexts - // (although the number that do should be relatively few) + // context creation setup + GLFW.glfwDefaultWindowHints(); + // make the context window invisible GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); + // by default the context should get the highest available OpenGL version + // but this can be explicitly set for testing +// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4); +// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 5); - // create an invisible window to hold the context - lodBuilderGlCapabilities = GL.createCapabilities(); - lodBuilderGlContext = GLFW.glfwCreateWindow(640, 480, "LOD window", 0L, minecraftGlContext); + + // create the LodRender context + lodRenderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Render Window", 0L, 0L); // create a window to hold the context + GLFW.glfwMakeContextCurrent(lodRenderGlContext); + lodRenderGlCapabilities = GL.createCapabilities(); + + // create the LodBuilder context + lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, lodRenderGlContext); GLFW.glfwMakeContextCurrent(lodBuilderGlContext); + lodBuilderGlCapabilities = GL.createCapabilities(); - // Since this is called on the render thread, make sure the Minecraft context is used in the end - GLFW.glfwMakeContextCurrent(minecraftGlContext); - GL.setCapabilities(minecraftGlCapabilities); - - - - - //==============================// - // determine the OpenGL version // - //==============================// - - bufferStorageSupported = minecraftGlCapabilities.glBufferStorage != 0; - mapBufferRangeSupported = minecraftGlCapabilities.glMapBufferRange != 0; - - if (!minecraftGlCapabilities.OpenGL15) - { - String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater."; - mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater.")); - } - - if (!bufferStorageSupported) - { - String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5"; - - ClientProxy.LOGGER.error("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance."); - } - @@ -139,8 +128,31 @@ public class GlProxy // get any GPU related capabilities // //==================================// - // see if this GPU can run fancy fog - fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance; + ClientProxy.LOGGER.info("Lod Render OpenGL version [" + GL11.glGetString(GL11.GL_VERSION) + "]."); + + // crash the game if the GPU doesn't support OpenGL 1.5 + if (!minecraftGlCapabilities.OpenGL15) + { + // Note: as of MC 1.17 this shouldn't happen since MC + // requires OpenGL 3.3, but just in case. + String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater."; + mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater.")); + } + + + + // get specific capabilities + bufferStorageSupported = lodBuilderGlCapabilities.glBufferStorage != 0; + mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0; + fancyFogAvailable = minecraftGlCapabilities.GL_NV_fog_distance; + + + // display the capabilities + if (!bufferStorageSupported) + { + String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5"; + ClientProxy.LOGGER.error("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance."); + } if (!fancyFogAvailable) ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available."); @@ -148,6 +160,16 @@ public class GlProxy + + //==========// + // clean up // + //==========// + + // Since this is created on the render thread, make sure the Minecraft context is used in the end + GLFW.glfwMakeContextCurrent(minecraftGlContext); + GL.setCapabilities(minecraftGlCapabilities); + + // GlProxy creation success ClientProxy.LOGGER.error(GlProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day."); } @@ -163,12 +185,12 @@ public class GlProxy { GlProxyContext currentContext = getGlContext(); - // we don't have to change the context, we're already there. + // we don't have to change the context, we are already there. if (currentContext == newContext) return; - long contextPointer = 0L; + long contextPointer; GLCapabilities newGlCapabilities = null; // get the pointer(s) for this context @@ -178,6 +200,11 @@ public class GlProxy contextPointer = lodBuilderGlContext; newGlCapabilities = lodBuilderGlCapabilities; break; + + case LOD_RENDER: + contextPointer = lodRenderGlContext; + newGlCapabilities = lodRenderGlCapabilities; + break; case MINECRAFT: contextPointer = minecraftGlContext; @@ -187,6 +214,7 @@ public class GlProxy default: // default should never happen, it is just here to make the compiler happy case NONE: // 0L is equivalent to null + contextPointer = 0L; break; } @@ -215,14 +243,19 @@ public class GlProxy if (currentContext == lodBuilderGlContext) return GlProxyContext.LOD_BUILDER; + else if (currentContext == lodRenderGlContext) + return GlProxyContext.LOD_RENDER; else if (currentContext == minecraftGlContext) return GlProxyContext.MINECRAFT; else if (currentContext == 0L) return GlProxyContext.NONE; else - // hopefully this shouldn't happen, but - // at least now we will be notified if it does happen - throw new IllegalStateException(Thread.currentThread().getName() + " has a unknown OpenGl context: [" + currentContext + "]. Minecraft context [" + minecraftGlContext + "], LodBuilder context [" + lodBuilderGlContext + "], no context [0]."); + // hopefully this shouldn't happen + throw new IllegalStateException(Thread.currentThread().getName() + + " has a unknown OpenGl context: [" + currentContext + "]. " + + "Minecraft context [" + minecraftGlContext + "], " + + "LodBuilder context [" + lodBuilderGlContext + "], " + + "LodRender context [" + lodRenderGlContext + "], no context [0]."); } diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index 45c419533..beb294735 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -63,7 +63,7 @@ import net.minecraft.world.phys.Vec3; * This is where LODs are draw to the world. * * @author James Seibel - * @version 10-25-2021 + * @version 10-31-2021 */ public class LodRenderer { @@ -180,14 +180,6 @@ public class LodRenderer - //===============// - // initial setup // - //===============// - - profiler = newProfiler; - profiler.push("LOD setup"); - - // TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead) // starting here... determineIfLodsShouldRegenerate(lodDim, partialTicks); @@ -222,9 +214,18 @@ public class LodRenderer } - //===========================// - // GL settings for rendering // - //===========================// + + + //===============// + // initial setup // + //===============// + + profiler = newProfiler; + profiler.push("LOD setup"); + + GlProxy glProxy = GlProxy.getInstance(); + + // set the required open GL settings @@ -246,6 +247,7 @@ public class LodRenderer GL15.glDisable(GL15.GL_LIGHT0); GL15.glDisable(GL15.GL_LIGHT1); + // get the default projection matrix, so we can // reset it after drawing the LODs float[] mcProjMatrixRaw = new float[16]; @@ -255,6 +257,7 @@ public class LodRenderer // (or maybe vice versa I have no idea :P) mcProjectionMatrix.transpose(); + Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcModelViewMatrix, partialTicks); vanillaBlockRenderedDistance = mc.getRenderDistance() * LodUtil.CHUNK_WIDTH; // required for setupFog and setupProjectionMatrix @@ -263,19 +266,26 @@ public class LodRenderer else farPlaneBlockDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH; + setupProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks); + // commented out until we can add shaders to handle lighting //setupLighting(lodDim, partialTicks); - NearFarFogSettings fogSettings = determineFogSettings(); // determine the current fog settings, so they can be // reset after drawing the LODs float defaultFogStartDist = GL15.glGetFloat(GL15.GL_FOG_START); float defaultFogEndDist = GL15.glGetFloat(GL15.GL_FOG_END); int defaultFogMode = GL15.glGetInteger(GL15.GL_FOG_MODE); - int defaultFogDistance = GlProxy.getInstance().fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1; + int defaultFogDistance = glProxy.fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1; + + NearFarFogSettings fogSettings = determineFogSettings(); + + + + //===========// // rendering // @@ -289,7 +299,7 @@ public class LodRenderer Vector3f cameraDir = camera.getLookVector(); boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get(); - boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && GlProxy.getInstance().bufferStorageSupported; + boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported; // used to determine what type of fog to render int halfWidth = vbos.length / 2; @@ -328,6 +338,9 @@ public class LodRenderer } + + + //=========// // cleanup // //=========// @@ -371,17 +384,17 @@ public class LodRenderer LodUtil.LOD_VERTEX_FORMAT.setupBufferState(); // set up the model view matrix - GL15.glPushMatrix(); - GL15.glLoadIdentity(); +// GL15.glPushMatrix(); // matrix code is only available in OpenGL 3.2 and lower +// GL15.glLoadIdentity(); FloatBuffer matrixBuffer = FloatBuffer.allocate(16); modelViewMatrix.store(matrixBuffer); - GL15.glMultMatrixf(matrixBuffer); +// GL15.glMultMatrixf(matrixBuffer); GL15.glDrawArrays(GL15.GL_QUADS, 0, vertexCount); // post draw cleanup - GL15.glPopMatrix(); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); +// GL15.glPopMatrix(); +// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); LodUtil.LOD_VERTEX_FORMAT.clearBufferState(); }