Update rendering to OpenGL 3.2+ (buffers seem to be corrupted or missing)

The rendering is almost completely functional, only fog needs to be re-implemented.
This commit is contained in:
James Seibel
2021-11-08 19:45:40 -06:00
parent 5eed9dae40
commit 03ff908de1
15 changed files with 650 additions and 164 deletions
Binary file not shown.
+11
View File
@@ -0,0 +1,11 @@
These Files are used when developing the mod.
Eclipse Auto Formatting
IntelliJ Auto Formatting
- These files are the auto-formatting settings that should be used when developing for the mod. We want to make sure the style of code is consistant regardless of who is writting the code.
renderDocMcDistantHorizonsSettings
- This file contains the configuration to run a remote debug instance so you can edit the mod in your IDE while also viewing the OpenGL code in RenderDoc.
minecraft launch options
- This file contains the Java command line arguments used to launch the mod from a development environment (specifically Eclipse, James didn't test it with IntelliJ), and is included to more easily edit the options for RenderDoc
Binary file not shown.
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -11,7 +11,7 @@ buildscript {
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true
classpath group: 'org.spongepowered', name: 'mixingradle', version: '0.7-SNAPSHOT'
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
classpath "gradle.plugin.com.github.johnrengelman:shadow:7.1.0"
}
}
@@ -22,9 +22,9 @@ apply plugin: 'eclipse'
apply plugin: 'maven-publish'
apply plugin: 'com.github.johnrengelman.shadow'
version = '1.5.3a'
version = '1.5.4a'
group = 'com.seibel.lod'
archivesBaseName = 'Distant-Horizons_1.16.5'
archivesBaseName = 'Distant-Horizons_1.17.1'
// Mojang ships Java 16 to end users in 1.17+ instead of Java 8 in 1.16 or lower, so your mod should target Java 16.
java.toolchain.languageVersion = JavaLanguageVersion.of(16)
@@ -68,7 +68,7 @@ import net.minecraft.world.level.LightLayer;
/**
* This object is used to create NearFarBuffer objects.
* @author James Seibel
* @version 10-23-2021
* @version 11-8-2021
*/
public class LodBufferBuilder
{
@@ -535,13 +535,16 @@ public class LodBufferBuilder
*/
public void setupBuffers(LodDimension lodDimension)
{
GlProxy glProxy = GlProxy.getInstance();
bufferLock.lock();
int numbRegionsWide = lodDimension.getWidth();
long regionMemoryRequired;
int numberOfBuffers;
GlProxy glProxy = GlProxy.getInstance();
GlProxyContext oldContext = glProxy.getGlContext();
glProxy.setGlContext(GlProxyContext.LOD_BUILDER);
previousRegionWidth = numbRegionsWide;
numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide];
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide][];
@@ -632,6 +635,7 @@ public class LodBufferBuilder
}
}
glProxy.setGlContext(oldContext);
bufferLock.unlock();
}
@@ -794,7 +798,11 @@ public class LodBufferBuilder
for (int i = 0; i < buildableBuffers[x][z].length; i++)
{
ByteBuffer uploadBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond();
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, true, uploadMethod);
int storageBufferId = 0;
if (buildableStorageBufferIds != null)
storageBufferId = buildableStorageBufferIds[x][z][i];
vboUpload(buildableVbos[x][z][i], storageBufferId, uploadBuffer, true, uploadMethod);
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
}
}
@@ -818,8 +826,7 @@ public class LodBufferBuilder
}
}
/** Uploads the uploadBuffer so the GPU can use it.
* @param uploadMethod */
/** Uploads the uploadBuffer so the GPU can use it. */
private void vboUpload(VertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer,
boolean allowBufferExpansion, GpuUploadMethod uploadMethod)
{
@@ -827,15 +834,14 @@ public class LodBufferBuilder
if (vbo.vertextBufferId != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER)
{
// this is how many points will be rendered
// TODO double check that 4 * 6 is correct for the number of points
vbo.indexCount = (uploadBuffer.capacity() / (4 * 6));
vbo.indexCount = (uploadBuffer.capacity() / (6 * 6)); // TODO make this change with the LodTemplate
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.vertextBufferId);
try
{
// if possible use the faster buffer storage route
if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE && storageBufferId != 0)
{
// get a pointer to the buffer in system memory
ByteBuffer vboBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
@@ -916,7 +922,6 @@ public class LodBufferBuilder
// hybrid subData/bufferData //
// less stutter, low GPU usage
//long size = GL31.glGetBufferParameteri64(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); // hopefully just a int should be long enough
long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER)
{
@@ -74,35 +74,58 @@ public class Box
public static final Map<Direction, int[][]> DIRECTION_VERTEX_MAP = new HashMap<Direction, int[][]>()
{{
put(Direction.UP, new int[][] {
{ 0, 1, 0 },
{ 0, 1, 1 },
{ 1, 1, 1 },
{ 1, 1, 0 } });
{ 0, 1, 0 }, // 0
{ 0, 1, 1 }, // 1
{ 1, 1, 1 }, // 2
{ 0, 1, 0 }, // 0
{ 1, 1, 1 }, // 2
{ 1, 1, 0 } // 3
});
put(Direction.DOWN, new int[][] {
{ 1, 0, 0 },
{ 1, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 } });
{ 1, 0, 0 }, // 0
{ 1, 0, 1 }, // 1
{ 0, 0, 1 }, // 2
{ 1, 0, 0 }, // 0
{ 0, 0, 1 }, // 2
{ 0, 0, 0 } // 3
});
put(Direction.EAST, new int[][] {
{ 1, 1, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 0, 0 } });
{ 1, 1, 0 }, // 0
{ 1, 1, 1 }, // 1
{ 1, 0, 1 }, // 2
{ 1, 1, 0 }, // 0
{ 1, 0, 1 }, // 2
{ 1, 0, 0 } }); // 3
put(Direction.WEST, new int[][] {
{ 0, 0, 0 },
{ 0, 0, 1 },
{ 0, 1, 1 },
{ 0, 1, 0 } });
{ 0, 0, 0 }, // 0
{ 0, 0, 1 }, // 1
{ 0, 1, 1 }, // 2
{ 0, 0, 0 }, // 0
{ 0, 1, 1 }, // 2
{ 0, 1, 0 } // 3
});
put(Direction.SOUTH, new int[][] {
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 1, 1 },
{ 0, 0, 1 } });
{ 1, 0, 1 }, // 0
{ 1, 1, 1 }, // 1
{ 0, 1, 1 }, // 2
{ 1, 0, 1 }, // 0
{ 0, 1, 1 }, // 2
{ 0, 0, 1 } // 3
});
put(Direction.NORTH, new int[][] {
{ 0, 0, 0 },
{ 0, 1, 0 },
{ 1, 1, 0 },
{ 1, 0, 0 } });
{ 0, 0, 0 }, // 0
{ 0, 1, 0 }, // 1
{ 1, 1, 0 }, // 2
{ 0, 0, 0 }, // 0
{ 1, 1, 0 }, // 2
{ 1, 0, 0 } // 3
});
}};
@@ -33,7 +33,7 @@ import net.minecraft.core.Direction;
/**
* Builds LODs as rectangular prisms.
* @author James Seibel
* @version 10-10-2021
* @version 11-8-2021
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
@@ -118,10 +118,11 @@ public class CubicLodTemplate extends AbstractLodTemplate
{
if(box.isCulled(direction))
continue;
int verticalFaceIndex = 0;
while (box.shouldRenderFace(direction, verticalFaceIndex))
{
for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++)
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
{
color = box.getColor(direction);
skyLight = box.getSkyLight(direction, verticalFaceIndex);
@@ -56,7 +56,7 @@ 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-31-2021
* @version 11-8-2021
*/
public class ClientProxy
{
@@ -131,8 +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());
renderer.drawLODs(lodDim, mcModelViewMatrix, projectionMatrix, partialTicks, mc.getProfiler());
profiler.pop(); // end LOD
profiler.push("terrain"); // go back into "terrain"
@@ -161,18 +160,19 @@ public class ClientProxy
// remind the developer(s) that the config override is active
if (!configOverrideReminderPrinted)
{
// mc.getPlayer().sendMessage(new TextComponent("LOD experimental build 1.5.1"), mc.getPlayer().getUUID());
// TODO add a send message method to the MC wrapper
// mc.getPlayer().sendMessage(new TextComponent("LOD experimental build " + ModInfo.VERSION), mc.getPlayer().getUUID());
// mc.getPlayer().sendMessage(new TextComponent("Here be dragons!"), mc.getPlayer().getUUID());
mc.getPlayer().sendMessage(new TextComponent("Debug settings enabled!"), mc.getPlayer().getUUID());
configOverrideReminderPrinted = true;
}
// LodConfig.CLIENT.graphics.drawResolution.set(HorizontalResolution.BLOCK);
// LodConfig.CLIENT.worldGenerator.generationResolution.set(HorizontalResolution.BLOCK);
// requires a world restart?
// LodConfig.CLIENT.worldGenerator.lodQualityMode.set(VerticalQuality.VOXEL);
// LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.set(FogDistance.FAR);
// LodConfig.CLIENT.graphics.fogQualityOption.fogDrawOverride.set(FogDrawOverride.FANCY);
// LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.set(true);
@@ -181,7 +181,7 @@ public class ClientProxy
// LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(VanillaOverdraw.DYNAMIC);
// LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.BUFFER_STORAGE);
// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
// LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(128);
// LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
@@ -251,23 +251,25 @@ public class ClientProxy
{
// the player just left the server
// TODO should "resetMod()" be called here? -James
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
//LodNodeGenWorker.restartExecutorService();
//LodNodeGenWorker.restartExecutorService(); // TODO why was this commented out? -James
//ThreadMapUtil.clearMaps();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
lodWorld.deselectWorld();
// hopefully this should reduce issues related to the buffer builder
// prevent issues related to the buffer builder
// breaking when changing worlds.
renderer.destroyBuffers();
recalculateWidths = true;
renderer = new LodRenderer(lodBufferBuilder);
// make sure the nilled objects are freed.
// make sure the nulled objects are freed.
// (this prevents an out of memory error when
// changing worlds)
System.gc();
@@ -358,6 +360,7 @@ public class ClientProxy
/** this method reset some static data every time we change world */
private void resetMod()
{
// TODO when should this be used?
ThreadMapUtil.clearMaps();
LodGenWorker.restartExecutorService();
+84 -40
View File
@@ -22,11 +22,15 @@ package com.seibel.lod.proxy;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.ModInfo;
import com.seibel.lod.enums.GlProxyContext;
import com.seibel.lod.render.shader.LodShader;
import com.seibel.lod.render.shader.LodShaderProgram;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
@@ -41,7 +45,7 @@ import com.seibel.lod.wrappers.MinecraftWrapper;
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br><br>
*
* @author James Seibel
* @version 10-31-2021
* @version 11-8-2021
*/
public class GlProxy
{
@@ -49,6 +53,7 @@ public class GlProxy
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** Minecraft's GLFW window */
public final long minecraftGlContext;
/** Minecraft's GL capabilities */
@@ -59,16 +64,12 @@ public class GlProxy
/** 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.
*/
public Thread lodBuilderOwnerThread = null;
/** 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;
/** Does this computer's GPU support fancy fog? */
public final boolean fancyFogAvailable;
@@ -80,6 +81,8 @@ public class GlProxy
public final boolean mapBufferRangeSupported;
private GlProxy()
{
ClientProxy.LOGGER.error("Creating " + GlProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
@@ -110,13 +113,8 @@ public class GlProxy
// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 5);
// 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);
lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext);
GLFW.glfwMakeContextCurrent(lodBuilderGlContext);
lodBuilderGlCapabilities = GL.createCapabilities();
@@ -124,25 +122,28 @@ public class GlProxy
//==================================//
// get any GPU related capabilities //
//==================================//
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)
// crash the game if the GPU doesn't support OpenGL 2.0
if (!minecraftGlCapabilities.OpenGL20)
{
// 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."));
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 2.0 or greater.";
mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 2.0 or greater."));
}
// get specific capabilities
bufferStorageSupported = lodBuilderGlCapabilities.glBufferStorage != 0;
// TODO re-add buffer storage support
bufferStorageSupported = false; //lodBuilderGlCapabilities.glBufferStorage != 0;
mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0;
fancyFogAvailable = minecraftGlCapabilities.GL_NV_fog_distance;
@@ -161,20 +162,78 @@ public class GlProxy
//==============//
// shader setup //
//==============//
//setGlContext(GlProxyContext.LOD_RENDER);
setGlContext(GlProxyContext.MINECRAFT);
createShaderProgram();
// Note: VAO objects can not be shared between contexts,
// this must be created on the LOD render context to work correctly
vertexArrayObjectId = GL30.glGenVertexArrays();
//==========//
// 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);
setGlContext(GlProxyContext.MINECRAFT);
// GlProxy creation success
ClientProxy.LOGGER.error(GlProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
}
/** Creates all required shaders */
public void createShaderProgram()
{
LodShader vertexShader = null;
LodShader fragmentShader = null;
try
{
// get the shaders from the resource folder
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/unshaded.vert", false);
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/unshaded.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/unshaded.vert", true);
// fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/unshaded.frag", true);
// create the shaders
lodShaderProgram = new LodShaderProgram();
// Attach the compiled shaders to the program
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);
}
catch (Exception e)
{
ClientProxy.LOGGER.error("Unable to compile shaders. Error: " + e.getMessage());
}
}
/**
@@ -200,11 +259,6 @@ public class GlProxy
contextPointer = lodBuilderGlContext;
newGlCapabilities = lodBuilderGlCapabilities;
break;
case LOD_RENDER:
contextPointer = lodRenderGlContext;
newGlCapabilities = lodRenderGlCapabilities;
break;
case MINECRAFT:
contextPointer = minecraftGlContext;
@@ -221,14 +275,6 @@ public class GlProxy
GLFW.glfwMakeContextCurrent(contextPointer);
GL.setCapabilities(newGlCapabilities);
// used for debugging
if (newContext == GlProxyContext.LOD_BUILDER)
lodBuilderOwnerThread = Thread.currentThread();
else if (newContext == GlProxyContext.NONE && currentContext == GlProxyContext.LOD_BUILDER)
lodBuilderOwnerThread = null;
}
@@ -243,8 +289,6 @@ 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)
@@ -255,7 +299,7 @@ public class GlProxy
" has a unknown OpenGl context: [" + currentContext + "]. "
+ "Minecraft context [" + minecraftGlContext + "], "
+ "LodBuilder context [" + lodBuilderGlContext + "], "
+ "LodRender context [" + lodRenderGlContext + "], no context [0].");
+ "no context [0].");
}
@@ -19,12 +19,14 @@
package com.seibel.lod.render;
import java.nio.FloatBuffer;
import java.util.HashSet;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.NVFogDistance;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.math.Matrix4f;
@@ -43,6 +45,7 @@ import com.seibel.lod.objects.NearFarFogSettings;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.proxy.GlProxy;
import com.seibel.lod.render.shader.LodShaderProgram;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodUtil;
@@ -52,6 +55,7 @@ import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.level.ChunkPos;
@@ -63,7 +67,7 @@ import net.minecraft.world.phys.Vec3;
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 10-31-2021
* @version 11-8-2021
*/
public class LodRenderer
{
@@ -153,9 +157,10 @@ public class LodRenderer
* the async process of generating the Buffers that hold those LODs.
* @param lodDim The dimension to draw, if null doesn't replace the current dimension.
* @param mcModelViewMatrix This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method
* @param mcProjectionMatrix
* @param partialTicks how far into the current tick this method was called.
*/
public void drawLODs(LodDimension lodDim, PoseStack mcModelViewMatrix, float partialTicks, ProfilerFiller newProfiler)
public void drawLODs(LodDimension lodDim, PoseStack mcModelViewMatrix, Matrix4f mcProjectionMatrix, float partialTicks, ProfilerFiller newProfiler)
{
//=================================//
// determine if LODs should render //
@@ -234,28 +239,13 @@ public class LodRenderer
else
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
GL15.glDisable(GL15.GL_TEXTURE_2D);
GL15.glEnable(GL15.GL_CULL_FACE);
GL15.glEnable(GL15.GL_COLOR_MATERIAL);
GL15.glEnable(GL15.GL_DEPTH_TEST);
// enable transparent rendering
GL15.glBlendFunc(GL15.GL_SRC_ALPHA, GL15.GL_ONE_MINUS_SRC_ALPHA);
GL15.glEnable(GL15.GL_BLEND);
// disable the lights Minecraft uses
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];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
mcProjectionMatrix.transpose();
Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcModelViewMatrix, partialTicks);
@@ -267,19 +257,21 @@ public class LodRenderer
farPlaneBlockDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
setupProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks);
Matrix4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks);
// commented out until we can add shaders to handle lighting
//setupLighting(lodDim, partialTicks);
// 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.fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1;
// // 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.fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1;
ShaderInstance mcShader = RenderSystem.getShader();
NearFarFogSettings fogSettings = determineFogSettings();
@@ -298,7 +290,8 @@ public class LodRenderer
Camera camera = mc.getGameRenderer().getMainCamera();
Vector3f cameraDir = camera.getLookVector();
boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
// TODO re-enable once rendering is totally working
boolean cullingDisabled = true; //LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported;
// used to determine what type of fog to render
@@ -309,6 +302,31 @@ public class LodRenderer
RegionPos vboCenterRegionPos = new RegionPos(vbosCenter);
// can be used when testing shaders
//glProxy.createShaderProgram();
LodShaderProgram shaderProgram = glProxy.lodShaderProgram;
shaderProgram.use();
// determine the VertexArrayObject's element positions
int posAttrib = shaderProgram.getAttributeLocation("vPosition");
shaderProgram.enableVertexAttribute(posAttrib);
int colAttrib = shaderProgram.getAttributeLocation("color");
shaderProgram.enableVertexAttribute(colAttrib);
// upload the required uniforms
int mvmUniform = shaderProgram.getUniformLocation("modelViewMatrix");
shaderProgram.setUniform(mvmUniform, modelViewMatrix);
int projUniform = shaderProgram.getUniformLocation("projectionMatrix");
shaderProgram.setUniform(projUniform, projectionMatrix);
// render each of the buffers
for (int x = 0; x < vbos.length; x++)
{
for (int z = 0; z < vbos.length; z++)
@@ -319,19 +337,19 @@ public class LodRenderer
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(camera.getBlockPosition(), cameraDir, vboPos.blockPos()))
{
if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth)
&& (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth))
setupFog(fogSettings.near.distance, fogSettings.near.quality);
else
setupFog(fogSettings.far.distance, fogSettings.far.quality);
// TODO add fog to the fragment shader
// if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth)
// && (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth))
// setupFog(fogSettings.near.distance, fogSettings.near.quality);
// else
// setupFog(fogSettings.far.distance, fogSettings.far.quality);
if (storageBufferIds != null && renderBufferStorage)
for (int i = 0; i < storageBufferIds[x][z].length; i++)
drawArrays(modelViewMatrix, storageBufferIds[x][z][i], vbos[x][z][i].indexCount);
drawArrays(storageBufferIds[x][z][i], vbos[x][z][i].indexCount, posAttrib, colAttrib);
else
for (int i = 0; i < vbos[x][z].length; i++)
drawArrays(modelViewMatrix, vbos[x][z][i].vertextBufferId, vbos[x][z][i].indexCount);
drawArrays(vbos[x][z][i].vertextBufferId, vbos[x][z][i].indexCount, posAttrib, colAttrib);
}
}
}
@@ -348,23 +366,11 @@ public class LodRenderer
profiler.popPush("LOD cleanup");
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
GL15.glEnable(GL15.GL_TEXTURE_2D);
GL15.glDisable(LOD_GL_LIGHT_NUMBER);
GL15.glDisable(GL15.GL_BLEND);
// re-enable the lights Minecraft uses
GL15.glEnable(GL15.GL_LIGHT0);
GL15.glEnable(GL15.GL_LIGHT1);
GL15.glDisable(GL15.GL_LIGHTING);
GL15.glDisable(GL15.GL_BLEND); // TODO: what should this be reset to?
// reset the fog settings so the normal chunks
// will be drawn correctly
cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance);
RenderSystem.setShader(() -> mcShader);
// reset the projection matrix so anything drawn after
// the LODs will use the correct projection matrix
gameRender.resetProjectionMatrix(mcProjectionMatrix);
// clear the depth buffer so anything drawn is drawn
// clear the depth buffer so everything drawn is drawn
// over the LODs
GL15.glClear(GL15.GL_DEPTH_BUFFER_BIT);
@@ -374,28 +380,29 @@ public class LodRenderer
}
/** This is where the actual drawing happens. */
private void drawArrays(Matrix4f modelViewMatrix, int glBufferId, int vertexCount)
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib)
{
if (glBufferId == 0)
return;
// pre draw setup
// can be used to check for OpenGL errors
// int error = GL15.glGetError();
// ClientProxy.LOGGER.info(Integer.toHexString(error));
// bind the buffer we are going to draw
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, glBufferId);
LodUtil.LOD_VERTEX_FORMAT.setupBufferState();
// set up the model view matrix
// 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.glDrawArrays(GL15.GL_QUADS, 0, vertexCount);
// post draw cleanup
// GL15.glPopMatrix();
// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LodUtil.LOD_VERTEX_FORMAT.clearBufferState();
GL30.glBindVertexArray(GlProxy.getInstance().vertexArrayObjectId);
// let OpenGL know how our buffer is set up
int vertexByteCount = (Float.BYTES * 3) + (Byte.BYTES * 4);
GL20.glEnableVertexAttribArray(posAttrib);
GL20.glVertexAttribPointer(posAttrib, 3, GL15.GL_FLOAT, false, vertexByteCount, 0);
GL20.glEnableVertexAttribArray(colAttrib);
GL20.glVertexAttribPointer(colAttrib, 4, GL15.GL_UNSIGNED_BYTE, true, vertexByteCount, Float.BYTES * 3);
// draw the LODs
GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, vertexCount);
}
@@ -552,15 +559,15 @@ public class LodRenderer
}
/**
* create a new projection matrix and send it over to the GPU
* create and return a new projection matrix based on MC's projection matrix
* @param currentProjectionMatrix this is Minecraft's current projection matrix
* @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance
* @param partialTicks how many ticks into the frame we are
*/
private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks)
private Matrix4f createProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks)
{
// create the new projection matrix
Matrix4f lodPoj =
Matrix4f lodProj =
Matrix4f.perspective(
getFov(partialTicks, true),
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
@@ -586,10 +593,9 @@ public class LodRenderer
// edit the lod projection to match Minecraft's
// (so the LODs line up with the real world)
lodPoj.multiply(distortionMatrix);
lodProj.multiply(distortionMatrix);
// send the projection over to the GPU
gameRender.resetProjectionMatrix(lodPoj);
return lodProj;
}
@@ -678,7 +684,7 @@ public class LodRenderer
lodBufferBuilder.destroyBuffers();
}
// TODO move this into the MC wrapper
private double getFov(float partialTicks, boolean useFovSetting)
{
return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting);
@@ -787,8 +793,6 @@ public class LodRenderer
/** Determines if the LODs should have a fullRegen or partialRegen */
private void determineIfLodsShouldRegenerate(LodDimension lodDim, float partialTicks)
{
short chunkRenderDistance = (short) mc.getRenderDistance();
int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2;
@@ -943,6 +947,8 @@ public class LodRenderer
vanillaRenderedChunksChanged = true;
vanillaRenderedChunksEmptySkip = true;
}
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
}
}
@@ -0,0 +1,116 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.render.shader;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.lwjgl.opengl.GL20;
import com.seibel.lod.proxy.ClientProxy;
/**
* This object holds a OpenGL reference to a shader
* and allows for reading in and compiling a shader file.
*
* @author James Seibel
* @version 11-8-2021
*/
public class LodShader
{
/** 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.
*
* @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
*/
public static LodShader loadShader(int type, String path, boolean absoluteFilePath) throws Exception
{
StringBuilder stringBuilder = new StringBuilder();
try
{
// open the file
InputStream in = absoluteFilePath ? new FileInputStream(path) : LodShader.class.getClassLoader().getResourceAsStream(path);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// read in the file
String line;
while ((line = reader.readLine()) != null)
stringBuilder.append(line).append("\n");
}
catch (IOException e)
{
ClientProxy.LOGGER.error("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
}
CharSequence shaderFileSource = stringBuilder.toString();
return createShader(type, shaderFileSource);
}
/**
* 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) throws Exception
{
LodShader shader = new LodShader(type);
GL20.glShaderSource(shader.id, source);
shader.compile();
return shader;
}
/**
* Compiles the shader and checks it's status afterwards.
* @throws Exception if the shader fails to compile
*/
public void compile() throws Exception
{
GL20.glCompileShader(id);
// check if the shader compiled
int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS);
if (status != GL20.GL_TRUE)
throw new Exception(GL20.glGetShaderInfoLog(id));
}
}
@@ -0,0 +1,183 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.render.shader;
import java.nio.FloatBuffer;
import org.lwjgl.opengl.GL20;
import org.lwjgl.system.MemoryStack;
import com.mojang.math.Matrix4f;
/**
* This object holds the reference to a OpenGL shader program
* and contains a few methods that can be used with OpenGL shader programs.
* The reason for many of these simple wrapper methods is as reminders of what
* can (and needs to be) done with a shader program.
*
* @author James Seibel
* @version 11-8-2021
*/
public class LodShaderProgram
{
/** Stores the handle of the program. */
public final int id;
/** Creates a shader program. */
public LodShaderProgram()
{
id = GL20.glCreateProgram();
}
/** Calls GL20.glUseProgram(this.id) */
public void use()
{
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);
}
/**
* Links the shader program to the current OpenGL context.
* @throws Exception Exception if the program failed to link
*/
public void link() throws Exception
{
GL20.glLinkProgram(this.id);
checkLinkStatus();
}
/**
* Checks if the program was linked successfully.
* @throws Exception if the program failed to link
*/
public void checkLinkStatus() throws Exception
{
int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS);
if (status != GL20.GL_TRUE)
throw new Exception(GL20.glGetProgramInfoLog(this.id));
}
/**
* Gets the location of an attribute variable with specified name.
* Calls GL20.glGetAttribLocation(id, name)
*
* @param name Attribute name
*
* @return Location of the attribute
*/
public int getAttributeLocation(CharSequence name)
{
return GL20.glGetAttribLocation(id, name);
}
/**
* 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);
}
/**
* Gets the location of an 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.
*/
public int getUniformLocation(CharSequence name)
{
return GL20.glGetUniformLocation(id, name);
}
/**
* Sets the uniform variable for specified location.
*
* @param location Uniform location
* @param value Value to set
*/
public void setUniform(int location, int value)
{
GL20.glUniform1i(location, value);
}
/**
* Sets the uniform variable for specified location.
*
* @param location Uniform location
* @param value Value to set
*/
public void setUniform(int location, Matrix4f value)
{
try (MemoryStack stack = MemoryStack.stackPush())
{
FloatBuffer buffer = stack.mallocFloat(4 * 4);
value.store(buffer);
GL20.glUniformMatrix4fv(location, false, buffer);
}
}
}
+26
View File
@@ -0,0 +1,26 @@
#version 150 core
in vec4 vertexColor;
//in vec2 textureCoord;
out vec4 fragColor;
//uniform sampler2D texImage;
/**
* Fragment Shader
*
* author: James Seibel
* version: 11-8-2021
*/
void main()
{
// TODO: add a white texture to support Optifine shaders
//vec4 textureColor = texture(texImage, textureCoord);
//fragColor = vertexColor * textureColor;
// very simple fragment shader, just return the vertix's color
fragColor = vertexColor;
}
+29
View File
@@ -0,0 +1,29 @@
#version 150 core
in vec3 vPosition;
in vec4 color;
out vec4 vertexColor;
//out vec2 textureCoord;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
/**
* Vertex Shader
*
* author: James Seibel
* version: 11-8-2021
*/
void main()
{
// TODO: add a simple white texture to support Optifine shaders
//textureCoord = textureCoord;
vertexColor = color;
// the vPosition needs to be converted to a vec4 so it can be multiplied
// by the 4x4 matrices
gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1);
}