phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE);
+ PHANTOM_TO_BUFFER_ID.put(phantom, this.id);
+ BUFFER_ID_TO_PHANTOM.put(this.id, phantom);
+
+ }
+
+ protected void destroyAsync()
+ {
+ if (this.id == 0)
+ {
+ // the buffer has already been closed
+ return;
+ }
+
+ destroyBufferIdAsync(this.id);
+
+ this.id = 0;
+ this.size = 0;
+ }
+ private static void destroyBufferIdAsync(int id)
+ {
+ // remove and clear the phantom reference if present
+ if (BUFFER_ID_TO_PHANTOM.containsKey(id))
+ {
+ Reference extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.get(id);
+
+ // if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again
+ // this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA
+ // to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it
+ phantom.clear();
+
+ PHANTOM_TO_BUFFER_ID.remove(phantom);
+ BUFFER_ID_TO_PHANTOM.remove(id);
+ }
+
+ RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
+ {
+ // destroy the buffer if it exists,
+ // the buffer may not exist if the destroy method is called twice
+ if (GL32.glIsBuffer(id))
+ {
+ GLMC.glDeleteBuffers(id);
+ bufferCount.decrementAndGet();
+
+ if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
+ {
+ LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]");
+ }
+ }
+ });
+ }
+
+
+
+ //==================//
+ // buffer uploading //
+ //==================//
+
+ /**
+ * Assumes the GL Context is already bound.
+ * Will create the VBO if one exist.
+ */
+ public void uploadBuffer(ByteBuffer bb, EDhApiGpuUploadMethod uploadMethod, int maxExpansionSize, int bufferHint)
+ {
+ LodUtil.assertTrue(!uploadMethod.useEarlyMapping, "UploadMethod signal that this should use Mapping instead of uploadBuffer!");
+ int bbSize = bb.limit() - bb.position();
+ if (bbSize > maxExpansionSize)
+ {
+ LodUtil.assertNotReach("maxExpansionSize is [" + maxExpansionSize + "] but buffer size is [" + bbSize + "]!");
+ }
+
+ // Don't upload an empty buffer
+ if (bbSize == 0)
+ {
+ return;
+ }
+
+ // make sure the buffer is ready for uploading
+ this.createOrChangeBufferTypeForUpload(uploadMethod);
+
+ switch (uploadMethod)
+ {
+ //case NONE:
+ // return;
+ case AUTO:
+ LodUtil.assertNotReach("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!");
+ case BUFFER_STORAGE:
+ this.uploadBufferStorage(bb, bufferHint);
+ break;
+ case DATA:
+ this.uploadBufferData(bb, bufferHint);
+ break;
+ case SUB_DATA:
+ this.uploadSubData(bb, maxExpansionSize, bufferHint);
+ break;
+ default:
+ LodUtil.assertNotReach("Unknown GpuUploadMethod!");
+ }
+ }
+ /** Requires the buffer to be bound */
+ protected void uploadBufferStorage(ByteBuffer bb, int bufferStorageHint)
+ {
+ LodUtil.assertTrue(this.bufferStorage, "Buffer is not bufferStorage but its trying to use bufferStorage upload method!");
+
+ int bbSize = bb.limit() - bb.position();
+ this.destroyAsync();
+ this.create(true);
+ this.bind();
+ GL44.glBufferStorage(this.getBufferBindingTarget(), bb, 0);
+ this.size = bbSize;
+ }
+ /** Requires the buffer to be bound */
+ protected void uploadBufferData(ByteBuffer bb, int bufferDataHint)
+ {
+ LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use bufferData upload method!");
+
+ int bbSize = bb.limit() - bb.position();
+ GL32.glBufferData(this.getBufferBindingTarget(), bb, bufferDataHint);
+ this.size = bbSize;
+ }
+ /** Requires the buffer to be bound */
+ protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint)
+ {
+ LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use subData upload method!");
+
+ int bbSize = bb.limit() - bb.position();
+ if (this.size < bbSize || this.size > bbSize * BUFFER_SHRINK_TRIGGER)
+ {
+ int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER);
+ if (newSize > maxExpansionSize) newSize = maxExpansionSize;
+ GL32.glBufferData(this.getBufferBindingTarget(), newSize, bufferDataHint);
+ this.size = newSize;
+ }
+ GL32.glBufferSubData(this.getBufferBindingTarget(), 0, bb);
+ }
+
+
+
+ //===========//
+ // overrides //
+ //===========//
+
+ @Override
+ public void close() { this.destroyAsync(); }
+
+ @Override
+ public String toString()
+ {
+ return (this.bufferStorage ? "" : "Static-") + this.getClass().getSimpleName() +
+ "[id:" + this.id + ",size:" + this.size + (this.isMapped ? ",MAPPED" : "") + "]";
+ }
+
+
+
+ //================//
+ // helper methods //
+ //================//
+
+ /**
+ * Makes sure the buffer exists and is of the correct format
+ * before uploading.
+ */
+ private void createOrChangeBufferTypeForUpload(EDhApiGpuUploadMethod uploadMethod)
+ {
+ // create/change the buffer type if necessary
+ if (uploadMethod.useBufferStorage != this.bufferStorage)
+ {
+ // recreate if the buffer storage type changed
+ this.bind();
+ this.destroyAsync();
+ this.create(uploadMethod.useBufferStorage);
+ this.bind();
+ }
+ else
+ {
+ // Prevent uploading to the null buffer (ID 0).
+ // This can happen if the buffer was deleted previously.
+ if (this.id == 0)
+ {
+ this.create(this.bufferStorage);
+ }
+
+ this.bind();
+ }
+ }
+
+
+
+ //================//
+ // static cleanup //
+ //================//
+
+ private static void runPhantomReferenceCleanupLoop()
+ {
+ while (true)
+ {
+ try
+ {
+ try
+ {
+ Thread.sleep(PHANTOM_REF_CHECK_TIME_IN_MS);
+ }
+ catch (InterruptedException ignore) { }
+
+
+ Reference extends GLBuffer> phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
+ while (phantomRef != null)
+ {
+ // destroy the buffer if it hasn't been cleared yet
+ if (PHANTOM_TO_BUFFER_ID.containsKey(phantomRef))
+ {
+ int id = PHANTOM_TO_BUFFER_ID.get(phantomRef);
+ destroyBufferIdAsync(id);
+ //LOGGER.warn("Buffer Phantom collected, ID: ["+id+"]");
+ }
+
+ phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
+ }
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Unexpected error in buffer cleanup thread: [" + e.getMessage() + "].", e);
+ }
+ }
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/GLElementBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/GLElementBuffer.java
new file mode 100644
index 000000000..d69818bf6
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/GLElementBuffer.java
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.buffer;
+
+import org.lwjgl.opengl.GL32;
+
+/**
+ * This is a container for a OpenGL
+ * VBO (Vertex Buffer Object).
+ *
+ * @author James Seibel
+ * @version 11-20-2021
+ */
+public class GLElementBuffer extends GLBuffer
+{
+ /**
+ * When uploading to a buffer that is too small, recreate it this many times
+ * bigger than the upload payload
+ */
+ protected int indicesCount = 0;
+ public int getIndicesCount() { return this.indicesCount; }
+ protected int type = GL32.GL_UNSIGNED_INT;
+ public int getType() { return type; }
+
+ public GLElementBuffer(boolean isBufferStorage)
+ {
+ super(isBufferStorage);
+ }
+
+ @Override
+ public void destroyAsync()
+ {
+ super.destroyAsync();
+ this.indicesCount = 0;
+ }
+
+ @Override
+ public int getBufferBindingTarget()
+ {
+ return GL32.GL_ELEMENT_ARRAY_BUFFER;
+ }
+
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/GLVertexBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/GLVertexBuffer.java
new file mode 100644
index 000000000..ad9582239
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/GLVertexBuffer.java
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.buffer;
+
+import java.nio.ByteBuffer;
+
+import org.lwjgl.opengl.GL32;
+
+import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
+
+/**
+ * This is a container for a OpenGL
+ * VBO (Vertex Buffer Object).
+ *
+ * @author James Seibel
+ * @version 11-20-2021
+ */
+public class GLVertexBuffer extends GLBuffer
+{
+ /**
+ * When uploading to a buffer that is too small, recreate it this many times
+ * bigger than the upload payload
+ */
+ protected int vertexCount = 0;
+ public int getVertexCount() { return this.vertexCount; }
+ // FIXME: This setter is needed for premapping buffer to manually set the vertexCount. Fix this.
+ public void setVertexCount(int vertexCount) { this.vertexCount = vertexCount; }
+
+
+ public GLVertexBuffer(boolean isBufferStorage)
+ {
+ super(isBufferStorage);
+ }
+
+
+
+ @Override
+ public void destroyAsync()
+ {
+ super.destroyAsync();
+ this.vertexCount = 0;
+ }
+
+ @Override
+ public int getBufferBindingTarget() { return GL32.GL_ARRAY_BUFFER; }
+
+ /**
+ * bufferSize is the number of shared verticies.
+ * This number will be higher when actually rendered since each box's face needs 2 triangles
+ * with 2 shared verticies.
+ */
+ public void uploadBuffer(ByteBuffer byteBuffer, int bufferSize, EDhApiGpuUploadMethod uploadMethod, int maxExpensionSize)
+ {
+ if (bufferSize < 0)
+ {
+ throw new IllegalArgumentException("VertCount is negative!");
+ }
+
+ // If size is zero, just ignore it.
+ if (byteBuffer.limit() - byteBuffer.position() != 0)
+ {
+ boolean useBuffStorage = uploadMethod.useBufferStorage;
+ super.uploadBuffer(byteBuffer, uploadMethod, maxExpensionSize, useBuffStorage ? 0 : GL32.GL_STATIC_DRAW);
+ }
+
+ // /4 to get the number of cubes
+ // *6 to get the number of verticies (2 triangles, 3 verticies each)
+ this.vertexCount = (bufferSize / 4) * 6;
+ }
+
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/QuadElementBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/QuadElementBuffer.java
new file mode 100644
index 000000000..953d4dae4
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/buffer/QuadElementBuffer.java
@@ -0,0 +1,192 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.buffer;
+
+import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.GLEnums;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.system.MemoryUtil;
+
+import java.nio.ByteBuffer;
+
+/** AKA Index Buffer TODO RENAME */
+public class QuadElementBuffer extends GLElementBuffer
+{
+ private static final DhLogger LOGGER = new DhLoggerBuilder().build();
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+ //region
+
+ public QuadElementBuffer() { super(false); }
+
+ public void reserve(int quadCount)
+ {
+ if (quadCount < 0)
+ {
+ throw new IllegalArgumentException("quadCount must be greater than 0");
+ }
+ if (quadCount == 0)
+ {
+ // shouldn't happen, but just in case
+ return;
+ }
+
+ this.indicesCount = quadCount * 6; // 2 triangles per quad
+ if (this.indicesCount >= this.getCapacity()
+ && this.indicesCount < this.getCapacity() * BUFFER_SHRINK_TRIGGER)
+ {
+ return;
+ }
+ int vertexCount = quadCount * 4; // 4 vertices per quad
+
+ if (vertexCount < 255)
+ {
+ // Reserve 1 for the reset index
+ this.type = GL32.GL_UNSIGNED_BYTE;
+ }
+ else if (vertexCount < 65535)
+ {
+ // Reserve 1 for the reset index
+ this.type = GL32.GL_UNSIGNED_SHORT;
+ }
+ else
+ {
+ this.type = GL32.GL_UNSIGNED_INT;
+ }
+
+ ByteBuffer buffer = MemoryUtil.memAlloc(this.indicesCount * GLEnums.getTypeSize(this.type));
+ buildBuffer(quadCount, buffer, this.type);
+ this.bind();
+ super.uploadBuffer(buffer, EDhApiGpuUploadMethod.DATA,
+ this.indicesCount * GLEnums.getTypeSize(this.type), GL32.GL_STATIC_DRAW);
+
+ MemoryUtil.memFree(buffer);
+ }
+
+ //endregion
+
+
+
+ //=========//
+ // getters //
+ //=========//
+ //region
+
+ public int getCapacity() { return super.getSize() / GLEnums.getTypeSize(this.getType()); }
+
+ //endregion
+
+
+
+ //==========//
+ // building //
+ //==========//
+ //region
+
+ public static void buildBuffer(int quadCount, ByteBuffer buffer, int type)
+ {
+ switch (type)
+ {
+ case GL32.GL_UNSIGNED_BYTE:
+ buildBufferByte(quadCount, buffer);
+ break;
+ case GL32.GL_UNSIGNED_SHORT:
+ buildBufferShort(quadCount, buffer);
+ break;
+ case GL32.GL_UNSIGNED_INT:
+ buildBufferInt(quadCount, buffer);
+ break;
+ default:
+ throw new IllegalStateException("Unknown buffer type: [" + type + "].");
+ }
+ }
+
+ private static void buildBufferByte(int quadCount, ByteBuffer buffer)
+ {
+ for (int i = 0; i < quadCount; i++)
+ {
+ int vIndex = i * 4;
+ // First triangle
+ buffer.put((byte) (vIndex));
+ buffer.put((byte) (vIndex + 1));
+ buffer.put((byte) (vIndex + 2));
+ // Second triangle
+ buffer.put((byte) (vIndex + 2));
+ buffer.put((byte) (vIndex + 3));
+ buffer.put((byte) (vIndex));
+ }
+ if (buffer.hasRemaining())
+ {
+ throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
+ }
+ buffer.rewind();
+ }
+ private static void buildBufferShort(int quadCount, ByteBuffer buffer)
+ {
+ for (int i = 0; i < quadCount; i++)
+ {
+ int vIndex = i * 4;
+ // First triangle
+ buffer.putShort((short) (vIndex));
+ buffer.putShort((short) (vIndex + 1));
+ buffer.putShort((short) (vIndex + 2));
+ // Second triangle
+ buffer.putShort((short) (vIndex + 2));
+ buffer.putShort((short) (vIndex + 3));
+ buffer.putShort((short) (vIndex));
+ }
+ if (buffer.hasRemaining())
+ {
+ throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
+ }
+ buffer.rewind();
+ }
+ private static void buildBufferInt(int quadCount, ByteBuffer buffer)
+ {
+ for (int i = 0; i < quadCount; i++)
+ {
+ int vIndex = i * 4;
+ // First triangle
+ buffer.putInt(vIndex);
+ buffer.putInt(vIndex + 1);
+ buffer.putInt(vIndex + 2);
+ // Second triangle
+ buffer.putInt(vIndex + 2);
+ buffer.putInt(vIndex + 3);
+ buffer.putInt(vIndex);
+ }
+ if (buffer.hasRemaining())
+ {
+ throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
+ }
+ buffer.rewind();
+ }
+
+ //endregion
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/shader/Shader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/shader/Shader.java
new file mode 100644
index 000000000..cd2174386
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/shader/Shader.java
@@ -0,0 +1,184 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.shader;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import org.lwjgl.PointerBuffer;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.opengl.GL32C;
+import org.lwjgl.system.MemoryStack;
+import org.lwjgl.system.MemoryUtil;
+import org.lwjgl.system.NativeType;
+
+/**
+ * This object holds a OpenGL reference to a shader
+ * and allows for reading in and compiling a shader file.
+ */
+public class Shader
+{
+ private static final DhLogger LOGGER = new DhLoggerBuilder()
+ .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
+ .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
+ .build();
+
+
+ /** OpenGL shader ID */
+ public final int id;
+
+
+
+ //==============//
+ // constructors //
+ //==============//
+ //region
+
+ /**
+ * Creates a shader with specified type.
+ *
+ * @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
+ * @param sourceString File path of the shader
+ * @throws RuntimeException if the shader fails to compile
+ */
+ public Shader(int type, String sourceString)
+ {
+ LOGGER.info("Loading shader with type: ["+type+"]");
+ LOGGER.debug("Source: \n["+sourceString+"]");
+ if (sourceString == null || sourceString.isEmpty())
+ {
+ throw new IllegalArgumentException("No shader source given.");
+ }
+
+ // Create an empty shader object
+ this.id = GL32.glCreateShader(type);
+ if (this.id == 0)
+ {
+ throw new IllegalArgumentException("Failed to create shader with type ["+type+"] and Source: \n["+sourceString+"].");
+ }
+
+ safeShaderSource(this.id, sourceString);
+ GL32.glCompileShader(this.id);
+ // check if the shader compiled
+ int status = GL32.glGetShaderi(this.id, GL32.GL_COMPILE_STATUS);
+ if (status != GL32.GL_TRUE)
+ {
+
+ String message = "Shader compiler error. Details: [" + GL32.glGetShaderInfoLog(this.id) + "]\n";
+ message += "Source: \n[" + sourceString + "]";
+ this.free(); // important!
+ throw new RuntimeException(message);
+ }
+ LOGGER.info("Shader loaded sucessfully.");
+ }
+
+ //endregion
+
+
+
+ //=========//
+ // helpers //
+ //=========//
+ //region
+
+ /**
+ * Identical in function to {@link GL32C#glShaderSource(int, CharSequence)} but
+ * passes a null pointer for string length to force the driver to rely on the null
+ * terminator for string length. This is a workaround for an apparent flaw with some
+ * AMD drivers that don't receive or interpret the length correctly, resulting in
+ * an access violation when the driver tries to read past the string memory.
+ *
+ * Hat tip to fewizz for the find and the fix.
+ *
+ *
Source: https://github.com/vram-guild/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
+ */
+ private static void safeShaderSource(@NativeType("GLuint") int glId, @NativeType("GLchar const **") CharSequence source)
+ {
+ final MemoryStack stack = MemoryStack.stackGet();
+ final int stackPointer = stack.getPointer();
+
+ try
+ {
+ final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
+ final PointerBuffer pointers = stack.mallocPointer(1);
+ pointers.put(sourceBuffer);
+
+ GL32.nglShaderSource(glId, 1, pointers.address0(), 0);
+ org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
+ }
+ finally
+ {
+ stack.setPointer(stackPointer);
+ }
+ }
+
+ public void free() { GL32.glDeleteShader(this.id); }
+
+ public static String loadFile(String path, boolean absoluteFilePath)
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ try
+ {
+ // open the file
+ InputStream in;
+ if (absoluteFilePath)
+ {
+ // Throws FileNotFoundException
+ in = new FileInputStream(path); // Note: this should use OS path seperator
+ }
+ else
+ {
+ in = Shader.class.getClassLoader().getResourceAsStream(path); // Note: path seperator should be '/'
+ if (in == null)
+ {
+ throw new FileNotFoundException("Shader file not found in resource: " + 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)
+ {
+ throw new RuntimeException("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
+ }
+
+ return stringBuilder.toString();
+ }
+
+ //endregion
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/shader/ShaderProgram.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/shader/ShaderProgram.java
new file mode 100644
index 000000000..97ef510f4
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/shader/ShaderProgram.java
@@ -0,0 +1,225 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.shader;
+
+import java.awt.Color;
+import java.nio.FloatBuffer;
+
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.system.MemoryStack;
+
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
+
+
+/**
+ * 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.
+ */
+public class ShaderProgram
+{
+ /** Stores the handle of the program. */
+ public final int id;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+ //region
+
+ public ShaderProgram(String vertResourcePath, String fragResourcePath, String attribute) { this(vertResourcePath, fragResourcePath, new String[]{ attribute }); }
+ /**
+ * @param vertResourcePath the relative path the vertex shader should be found
+ * @param fragResourcePath the relative path the fragment shader should be found
+ */
+ public ShaderProgram(String vertResourcePath, String fragResourcePath, String[] attributes)
+ {
+ this.id = GL32.glCreateProgram();
+
+ {
+ String shaderString = Shader.loadFile(vertResourcePath, false);
+ Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, shaderString);
+ GL32.glAttachShader(this.id, vertShader.id);
+ vertShader.free();
+ }
+
+ {
+ String shaderString = Shader.loadFile(fragResourcePath, false);
+ Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, shaderString);
+ GL32.glAttachShader(this.id, fragShader.id);
+ fragShader.free();
+ }
+
+ for (int i = 0; i < attributes.length; i++)
+ {
+ GL32.glBindAttribLocation(this.id, i, attributes[i]);
+ }
+ GL32.glLinkProgram(this.id);
+
+ int status = GL32.glGetProgrami(this.id, GL32.GL_LINK_STATUS);
+ if (status != GL32.GL_TRUE)
+ {
+ String message = "Shader Link Error. Details: " + GL32.glGetProgramInfoLog(this.id);
+ this.free(); // important!
+ throw new RuntimeException(message);
+ }
+ GL32.glUseProgram(this.id); // This HAVE to be a direct call to prevent calling the overloaded version
+ }
+
+ //endregion
+
+
+
+ //=========//
+ // binding //
+ //=========//
+ //region
+
+ public void bind() { GL32.glUseProgram(this.id); }
+ public void unbind() { GL32.glUseProgram(0); }
+
+ public void free() { GL32.glDeleteProgram(this.id); }
+
+ //endregion
+
+
+
+ //============//
+ // attributes //
+ //============//
+ //region
+
+ /**
+ * 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
+ * @return Location of the attribute
+ * @throws RuntimeException if attribute not found
+ */
+ public int getAttributeLocation(CharSequence name)
+ {
+ int i = GL32.glGetAttribLocation(id, name);
+ if (i == -1) throw new RuntimeException("Attribute name not found: " + name);
+ return i;
+ }
+ /**
+ * Same as above but without throwing errors.
+ * Returns -1 if the attribute doesn't exist or has been optimized out.
+ */
+ public int tryGetAttributeLocation(CharSequence name)
+ { return GL32.glGetAttribLocation(this.id, name); }
+
+ //endregion
+
+
+
+ //==========//
+ // uniforms //
+ //==========//
+ //region
+
+ /**
+ * 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 Location of the Uniform
+ * @throws RuntimeException if uniform not found
+ */
+ public int getUniformLocation(CharSequence name) throws RuntimeException
+ {
+ int i = GL32.glGetUniformLocation(id, name);
+ if (i == -1)
+ {
+ throw new RuntimeException("Uniform name not found: " + name);
+ }
+ return i;
+ }
+
+ // Same as above but without throwing errors.
+ // Return -1 if uniform doesn't exist or has been optimized out
+ public int tryGetUniformLocation(CharSequence name)
+ { return GL32.glGetUniformLocation(this.id, name); }
+
+ /** Requires a bound ShaderProgram. */
+ public void setUniform(int location, boolean value) { GL32.glUniform1i(location, value ? 1 : 0); }
+ /** @see ShaderProgram#setUniform(int, boolean) */
+ public void trySetUniform(int location, boolean value) { if (location != -1) { this.setUniform(location, value); } }
+
+ /** Requires a bound ShaderProgram. */
+ public void setUniform(int location, int value) { GL32.glUniform1i(location, value); }
+ /** @see ShaderProgram#setUniform(int, int) */
+ public void trySetUniform(int location, int value) { if (location != -1) { this.setUniform(location, value); } }
+
+ /** Requires a bound ShaderProgram. */
+ public void setUniform(int location, float value) { GL32.glUniform1f(location, value); }
+ /** @see ShaderProgram#setUniform(int, float) */
+ public void trySetUniform(int location, float value) { if (location != -1) { this.setUniform(location, value); } }
+
+ /** Requires a bound ShaderProgram. */
+ public void setUniform(int location, Vec3f value) { GL32.glUniform3f(location, value.x, value.y, value.z); }
+ /** @see ShaderProgram#setUniform(int, Vec3f) */
+ public void trySetUniform(int location, Vec3f value) { if (location != -1) { this.setUniform(location, value); } }
+
+ /** Requires a bound ShaderProgram. */
+ public void setUniform(int location, DhApiVec3i value) { GL32.glUniform3i(location, value.x, value.y, value.z); }
+ /** @see ShaderProgram#setUniform(int, Mat4f) */
+ public void trySetUniform(int location, DhApiVec3i value) { if (location != -1) { this.setUniform(location, value); } }
+
+ /** Requires a bound ShaderProgram. */
+ public void setUniform(int location, Mat4f value)
+ {
+ try (MemoryStack stack = MemoryStack.stackPush())
+ {
+ FloatBuffer buffer = stack.mallocFloat(4 * 4);
+ value.store(buffer);
+ GL32.glUniformMatrix4fv(location, false, buffer);
+ }
+ }
+ /** @see ShaderProgram#setUniform(int, Mat4f) */
+ public void trySetUniform(int location, Mat4f value) { if (location != -1) { this.setUniform(location, value); } }
+
+ /**
+ * Converts the color's RGBA values into values between 0 and 1.
+ * Requires a bound ShaderProgram.
+ */
+ public void setUniform(int location, Color value)
+ {
+ GL32.glUniform4f(location,
+ value.getRed() / 256.0f,
+ value.getGreen() / 256.0f,
+ value.getBlue() / 256.0f,
+ value.getAlpha() / 256.0f);
+ }
+ /** @see ShaderProgram#setUniform(int, Color) */
+ public void trySetUniform(int location, Color value) { if (location != -1) { this.setUniform(location, value); } }
+
+ //endregion
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DHDepthTexture.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DHDepthTexture.java
new file mode 100644
index 000000000..f687f7941
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DHDepthTexture.java
@@ -0,0 +1,62 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import org.lwjgl.opengl.GL11C;
+import org.lwjgl.opengl.GL13C;
+import org.lwjgl.opengl.GL43C;
+
+import java.nio.ByteBuffer;
+
+public class DHDepthTexture
+{
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ private int id;
+ public DHDepthTexture(int width, int height, EDhDepthBufferFormat format)
+ {
+ this.id = GL43C.glGenTextures();
+
+ this.resize(width, height, format);
+
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST);
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST);
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE);
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE);
+
+ // disable mip-mapping since DH is just going to draw straight to the screen
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
+
+ GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
+ }
+
+ // For internal use by Iris for copying data. Do not use this in DH.
+ public DHDepthTexture(int id) { this.id = id; }
+
+ public void resize(int width, int height, EDhDepthBufferFormat format)
+ {
+ GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, this.getTextureId());
+ GL43C.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, format.getGlInternalFormat(), width, height, 0,
+ format.getGlType(), format.getGlFormat(), (ByteBuffer) null);
+ }
+
+ public int getTextureId()
+ {
+ if (this.id == -1)
+ {
+ throw new IllegalStateException("Depth texture does not exist!");
+ }
+
+ return this.id;
+ }
+
+ public void destroy()
+ {
+ GLMC.glDeleteTextures(this.getTextureId());
+ this.id = -1;
+ }
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DhColorTexture.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DhColorTexture.java
new file mode 100644
index 000000000..4b4a8cf1b
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DhColorTexture.java
@@ -0,0 +1,183 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import org.joml.Vector2i;
+import org.lwjgl.opengl.GL11C;
+import org.lwjgl.opengl.GL13C;
+import org.lwjgl.opengl.GL43C;
+
+import java.nio.ByteBuffer;
+
+public class DhColorTexture
+{
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ private final EDhInternalTextureFormat internalFormat;
+ private final EDhPixelFormat format;
+ private final EDhPixelType type;
+ private int width;
+ private int height;
+
+ private boolean isValid;
+ /** AKA, the OpenGL name of this texture */
+ private final int id;
+
+ private static final ByteBuffer NULL_BUFFER = null;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public DhColorTexture(Builder builder)
+ {
+ this.isValid = true;
+
+ this.internalFormat = builder.internalFormat;
+ this.format = builder.format;
+ this.type = builder.type;
+
+ this.width = builder.width;
+ this.height = builder.height;
+
+ this.id = GL43C.glGenTextures();
+
+ boolean isPixelFormatInteger = builder.internalFormat.getPixelFormat().isInteger();
+ this.setupTexture(this.id, builder.width, builder.height, !isPixelFormatInteger); // this binds the texture
+
+ // Clean up after ourselves
+ // This is strictly defensive to ensure that other buggy code doesn't tamper with our textures
+ GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
+ }
+
+
+
+ //=========//
+ // methods //
+ //=========//
+
+ private void setupTexture(int id, int width, int height, boolean allowsLinear)
+ {
+ this.resizeTexture(id, width, height);
+
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST);
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST);
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE);
+ GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE);
+
+ // disable mip-mapping since DH is just going to draw straight to the screen
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
+ }
+
+ private void resizeTexture(int texture, int width, int height)
+ {
+ GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, texture);
+ GL43C.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, this.internalFormat.getGlFormat(), width, height, 0, this.format.getGlFormat(), this.type.getGlFormat(), NULL_BUFFER);
+ }
+
+ void resize(Vector2i textureScaleOverride) { this.resize(textureScaleOverride.x, textureScaleOverride.y); }
+
+ // Package private, call CompositeRenderTargets#resizeIfNeeded instead.
+ public void resize(int width, int height)
+ {
+ this.throwIfInvalid();
+
+ this.width = width;
+ this.height = height;
+
+ this.resizeTexture(this.id, width, height);
+ }
+
+ public EDhInternalTextureFormat getInternalFormat() { return this.internalFormat; }
+
+ public int getTextureId()
+ {
+ this.throwIfInvalid();
+ return this.id;
+ }
+
+ public int getWidth() { return this.width; }
+
+ public int getHeight() { return this.height; }
+
+ public void destroy()
+ {
+ this.throwIfInvalid();
+ this.isValid = false;
+
+ GLMC.glDeleteTextures(this.id);
+ }
+
+ /** @throws IllegalStateException if the texture isn't valid */
+ private void throwIfInvalid()
+ {
+ if (!this.isValid)
+ {
+ throw new IllegalStateException("Attempted to use a deleted composite render target");
+ }
+ }
+
+ public static Builder builder() { return new Builder(); }
+
+
+
+ //================//
+ // helper classes //
+ //================//
+
+ public static class Builder
+ {
+ private EDhInternalTextureFormat internalFormat = EDhInternalTextureFormat.RGBA8;
+ private int width = 0;
+ private int height = 0;
+ private EDhPixelFormat format = EDhPixelFormat.RGBA;
+ private EDhPixelType type = EDhPixelType.UNSIGNED_BYTE;
+
+ private Builder()
+ {
+ // No-op
+ }
+
+ public Builder setInternalFormat(EDhInternalTextureFormat format)
+ {
+ this.internalFormat = format;
+ return this;
+ }
+
+ public Builder setDimensions(int width, int height)
+ {
+ if (width <= 0)
+ {
+ throw new IllegalArgumentException("Width must be greater than zero");
+ }
+
+ if (height <= 0)
+ {
+ throw new IllegalArgumentException("Height must be greater than zero");
+ }
+
+ this.width = width;
+ this.height = height;
+
+ return this;
+ }
+
+ public Builder setPixelFormat(EDhPixelFormat pixelFormat)
+ {
+ this.format = pixelFormat;
+ return this;
+ }
+
+ public Builder setPixelType(EDhPixelType pixelType)
+ {
+ this.type = pixelType;
+ return this;
+ }
+
+ public DhColorTexture build() { return new DhColorTexture(this); }
+
+ }
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DhFramebuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DhFramebuffer.java
new file mode 100644
index 000000000..2c6bc961b
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/DhFramebuffer.java
@@ -0,0 +1,152 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import org.lwjgl.opengl.GL32;
+
+public class DhFramebuffer implements IDhApiFramebuffer
+{
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+ private final Int2IntMap attachments;
+ private final int maxDrawBuffers;
+ private final int maxColorAttachments;
+ private boolean hasDepthAttachment;
+ private int id;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public DhFramebuffer()
+ {
+ this.id = GL32.glGenFramebuffers();
+
+ this.attachments = new Int2IntArrayMap();
+ this.maxDrawBuffers = GL32.glGetInteger(GL32.GL_MAX_DRAW_BUFFERS);
+ this.maxColorAttachments = GL32.glGetInteger(GL32.GL_MAX_COLOR_ATTACHMENTS);
+ this.hasDepthAttachment = false;
+ }
+
+ /** For internal use by Iris, do not remove. */
+ public DhFramebuffer(int id)
+ {
+ this.id = id;
+
+ this.attachments = new Int2IntArrayMap();
+ this.maxDrawBuffers = GL32.glGetInteger(GL32.GL_MAX_DRAW_BUFFERS);
+ this.maxColorAttachments = GL32.glGetInteger(GL32.GL_MAX_COLOR_ATTACHMENTS);
+ this.hasDepthAttachment = false;
+ }
+
+
+
+ //=========//
+ // methods //
+ //=========//
+
+ @Override
+ public void addDepthAttachment(int textureId, boolean isCombinedStencil)
+ {
+ this.bind();
+
+ int depthAttachment = isCombinedStencil ? GL32.GL_DEPTH_STENCIL_ATTACHMENT : GL32.GL_DEPTH_ATTACHMENT;
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, depthAttachment, GL32.GL_TEXTURE_2D, textureId, 0);
+
+ this.hasDepthAttachment = true;
+ }
+
+ @Override
+ public void addColorAttachment(int textureIndex, int textureId)
+ {
+ this.bind();
+
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0 + textureIndex, GL32.GL_TEXTURE_2D, textureId, 0);
+ this.attachments.put(textureIndex, textureId);
+ }
+
+ public void noDrawBuffers()
+ {
+ this.bind();
+ GL32.glDrawBuffers(new int[]{GL32.GL_NONE});
+ }
+
+ public void drawBuffers(int[] buffers)
+ {
+ int[] glBuffers = new int[buffers.length];
+ int index = 0;
+
+ if (buffers.length > this.maxDrawBuffers)
+ {
+ throw new IllegalArgumentException("Cannot write to more than " + this.maxDrawBuffers + " draw buffers on this GPU");
+ }
+
+ for (int buffer : buffers)
+ {
+ if (buffer >= this.maxColorAttachments)
+ {
+ throw new IllegalArgumentException("Only " + this.maxColorAttachments + " color attachments are supported on this GPU, but an attempt was made to write to a color attachment with index " + buffer);
+ }
+
+ glBuffers[index++] = GL32.GL_COLOR_ATTACHMENT0 + buffer;
+ }
+
+ this.bind();
+ GL32.glDrawBuffers(new int[]{GL32.GL_NONE});
+ }
+
+ public void readBuffer(int buffer)
+ {
+ this.bind();
+ GL32.glReadBuffer(GL32.GL_COLOR_ATTACHMENT0 + buffer);
+ }
+
+ public int getColorAttachment(int index) { return this.attachments.get(index); }
+
+ public boolean hasDepthAttachment() { return this.hasDepthAttachment; }
+
+ @Override
+ public void bind()
+ {
+ if (this.id == -1)
+ {
+ throw new IllegalStateException("Framebuffer does not exist!");
+ }
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.id);
+ }
+
+ public void bindAsReadBuffer() { GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, this.id); }
+
+ public void bindAsDrawBuffer() { GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, this.id); }
+
+ @Override
+ public void destroy()
+ {
+ GL32.glDeleteFramebuffers(this.id);
+ this.id = -1;
+ }
+
+ @Override
+ public int getStatus()
+ {
+ this.bind();
+ int status = GL32.glCheckFramebufferStatus(GL32.GL_FRAMEBUFFER);
+ return status;
+ }
+
+ @Override
+ public int getId() { return this.id; }
+
+
+
+ //=============//
+ // API methods //
+ //=============//
+
+ public boolean overrideThisFrame() { return true; }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhDepthBufferFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhDepthBufferFormat.java
new file mode 100644
index 000000000..734f4bb25
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhDepthBufferFormat.java
@@ -0,0 +1,114 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+import org.jetbrains.annotations.Nullable;
+import org.lwjgl.opengl.GL30C;
+import org.lwjgl.opengl.GL43C;
+
+public enum EDhDepthBufferFormat
+{
+ DEPTH(false),
+ DEPTH16(false),
+ DEPTH24(false),
+ DEPTH32(false),
+ DEPTH32F(false),
+ DEPTH_STENCIL(true),
+ DEPTH24_STENCIL8(true),
+ DEPTH32F_STENCIL8(true);
+
+
+
+ private final boolean combinedStencil;
+
+ EDhDepthBufferFormat(boolean combinedStencil) { this.combinedStencil = combinedStencil; }
+
+
+
+ @Nullable
+ public static EDhDepthBufferFormat fromGlEnum(int glenum)
+ {
+ switch (glenum)
+ {
+ case GL30C.GL_DEPTH_COMPONENT:
+ return EDhDepthBufferFormat.DEPTH;
+ case GL30C.GL_DEPTH_COMPONENT16:
+ return EDhDepthBufferFormat.DEPTH16;
+ case GL30C.GL_DEPTH_COMPONENT24:
+ return EDhDepthBufferFormat.DEPTH24;
+ case GL30C.GL_DEPTH_COMPONENT32:
+ return EDhDepthBufferFormat.DEPTH32;
+ case GL30C.GL_DEPTH_COMPONENT32F:
+ return EDhDepthBufferFormat.DEPTH32F;
+ case GL30C.GL_DEPTH_STENCIL:
+ return EDhDepthBufferFormat.DEPTH_STENCIL;
+ case GL30C.GL_DEPTH24_STENCIL8:
+ return EDhDepthBufferFormat.DEPTH24_STENCIL8;
+ case GL30C.GL_DEPTH32F_STENCIL8:
+ return EDhDepthBufferFormat.DEPTH32F_STENCIL8;
+ default:
+ return null;
+ }
+ }
+
+ public static EDhDepthBufferFormat fromGlEnumOrDefault(int glenum)
+ {
+ EDhDepthBufferFormat format = fromGlEnum(glenum);
+ if (format == null)
+ {
+ // yolo, just assume it's GL_DEPTH_COMPONENT
+ return EDhDepthBufferFormat.DEPTH;
+ }
+ return format;
+ }
+
+ public int getGlInternalFormat()
+ {
+ switch (this)
+ {
+ case DEPTH:
+ return GL30C.GL_DEPTH_COMPONENT;
+ case DEPTH16:
+ return GL30C.GL_DEPTH_COMPONENT16;
+ case DEPTH24:
+ return GL30C.GL_DEPTH_COMPONENT24;
+ case DEPTH32:
+ return GL30C.GL_DEPTH_COMPONENT32;
+ case DEPTH32F:
+ return GL30C.GL_DEPTH_COMPONENT32F;
+ case DEPTH_STENCIL:
+ return GL30C.GL_DEPTH_STENCIL;
+ case DEPTH24_STENCIL8:
+ return GL30C.GL_DEPTH24_STENCIL8;
+ case DEPTH32F_STENCIL8:
+ return GL30C.GL_DEPTH32F_STENCIL8;
+ }
+
+ throw new AssertionError("unreachable");
+ }
+
+ public int getGlType() { return isCombinedStencil() ? GL30C.GL_DEPTH_STENCIL : GL30C.GL_DEPTH_COMPONENT; }
+
+ public int getGlFormat()
+ {
+ switch (this)
+ {
+ case DEPTH:
+ case DEPTH16:
+ return GL43C.GL_UNSIGNED_SHORT;
+ case DEPTH24:
+ case DEPTH32:
+ return GL43C.GL_UNSIGNED_INT;
+ case DEPTH32F:
+ return GL30C.GL_FLOAT;
+ case DEPTH_STENCIL:
+ case DEPTH24_STENCIL8:
+ return GL30C.GL_UNSIGNED_INT_24_8;
+ case DEPTH32F_STENCIL8:
+ return GL30C.GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
+ }
+
+ throw new AssertionError("unreachable");
+ }
+
+ public boolean isCombinedStencil() { return combinedStencil; }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhInternalTextureFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhInternalTextureFormat.java
new file mode 100644
index 000000000..a9a4851fc
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhInternalTextureFormat.java
@@ -0,0 +1,130 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+import org.lwjgl.opengl.GL11C;
+import org.lwjgl.opengl.GL30C;
+import org.lwjgl.opengl.GL31C;
+
+import java.util.Locale;
+import java.util.Optional;
+
+public enum EDhInternalTextureFormat
+{
+ RGBA(GL11C.GL_RGBA, EGlVersion.GL_11, EDhPixelFormat.RGBA),
+
+ // 8-bit normalized
+ R8(GL30C.GL_R8, EGlVersion.GL_30, EDhPixelFormat.RED),
+ RG8(GL30C.GL_RG8, EGlVersion.GL_30, EDhPixelFormat.RG),
+ RGB8(GL11C.GL_RGB8, EGlVersion.GL_11, EDhPixelFormat.RGB),
+ RGBA8(GL11C.GL_RGBA8, EGlVersion.GL_11, EDhPixelFormat.RGBA),
+
+ // 8-bit signed normalized
+ R8_SNORM(GL31C.GL_R8_SNORM, EGlVersion.GL_31, EDhPixelFormat.RED),
+ RG8_SNORM(GL31C.GL_RG8_SNORM, EGlVersion.GL_31, EDhPixelFormat.RG),
+ RGB8_SNORM(GL31C.GL_RGB8_SNORM, EGlVersion.GL_31, EDhPixelFormat.RGB),
+ RGBA8_SNORM(GL31C.GL_RGBA8_SNORM, EGlVersion.GL_31, EDhPixelFormat.RGBA),
+
+ // 16-bit normalized
+ R16(GL30C.GL_R16, EGlVersion.GL_30, EDhPixelFormat.RED),
+ RG16(GL30C.GL_RG16, EGlVersion.GL_30, EDhPixelFormat.RG),
+ RGB16(GL11C.GL_RGB16, EGlVersion.GL_11, EDhPixelFormat.RGB),
+ RGBA16(GL11C.GL_RGBA16, EGlVersion.GL_11, EDhPixelFormat.RGBA),
+
+ // 16-bit signed normalized
+ R16_SNORM(GL31C.GL_R16_SNORM, EGlVersion.GL_31, EDhPixelFormat.RED),
+ RG16_SNORM(GL31C.GL_RG16_SNORM, EGlVersion.GL_31, EDhPixelFormat.RG),
+ RGB16_SNORM(GL31C.GL_RGB16_SNORM, EGlVersion.GL_31, EDhPixelFormat.RGB),
+ RGBA16_SNORM(GL31C.GL_RGBA16_SNORM, EGlVersion.GL_31, EDhPixelFormat.RGBA),
+
+ // 16-bit float
+ R16F(GL30C.GL_R16F, EGlVersion.GL_30, EDhPixelFormat.RED),
+ RG16F(GL30C.GL_RG16F, EGlVersion.GL_30, EDhPixelFormat.RG),
+ RGB16F(GL30C.GL_RGB16F, EGlVersion.GL_30, EDhPixelFormat.RGB),
+ RGBA16F(GL30C.GL_RGBA16F, EGlVersion.GL_30, EDhPixelFormat.RGBA),
+
+ // 32-bit float
+ R32F(GL30C.GL_R32F, EGlVersion.GL_30, EDhPixelFormat.RED),
+ RG32F(GL30C.GL_RG32F, EGlVersion.GL_30, EDhPixelFormat.RG),
+ RGB32F(GL30C.GL_RGB32F, EGlVersion.GL_30, EDhPixelFormat.RGB),
+ RGBA32F(GL30C.GL_RGBA32F, EGlVersion.GL_30, EDhPixelFormat.RGBA),
+
+ // 8-bit integer
+ R8I(GL30C.GL_R8I, EGlVersion.GL_30, EDhPixelFormat.RED_INTEGER),
+ RG8I(GL30C.GL_RG8I, EGlVersion.GL_30, EDhPixelFormat.RG_INTEGER),
+ RGB8I(GL30C.GL_RGB8I, EGlVersion.GL_30, EDhPixelFormat.RGB_INTEGER),
+ RGBA8I(GL30C.GL_RGBA8I, EGlVersion.GL_30, EDhPixelFormat.RGBA_INTEGER),
+
+ // 8-bit unsigned integer
+ R8UI(GL30C.GL_R8UI, EGlVersion.GL_30, EDhPixelFormat.RED_INTEGER),
+ RG8UI(GL30C.GL_RG8UI, EGlVersion.GL_30, EDhPixelFormat.RG_INTEGER),
+ RGB8UI(GL30C.GL_RGB8UI, EGlVersion.GL_30, EDhPixelFormat.RGB_INTEGER),
+ RGBA8UI(GL30C.GL_RGBA8UI, EGlVersion.GL_30, EDhPixelFormat.RGBA_INTEGER),
+
+ // 16-bit integer
+ R16I(GL30C.GL_R16I, EGlVersion.GL_30, EDhPixelFormat.RED_INTEGER),
+ RG16I(GL30C.GL_RG16I, EGlVersion.GL_30, EDhPixelFormat.RG_INTEGER),
+ RGB16I(GL30C.GL_RGB16I, EGlVersion.GL_30, EDhPixelFormat.RGB_INTEGER),
+ RGBA16I(GL30C.GL_RGBA16I, EGlVersion.GL_30, EDhPixelFormat.RGBA_INTEGER),
+
+ // 16-bit unsigned integer
+ R16UI(GL30C.GL_R16UI, EGlVersion.GL_30, EDhPixelFormat.RED_INTEGER),
+ RG16UI(GL30C.GL_RG16UI, EGlVersion.GL_30, EDhPixelFormat.RG_INTEGER),
+ RGB16UI(GL30C.GL_RGB16UI, EGlVersion.GL_30, EDhPixelFormat.RGB_INTEGER),
+ RGBA16UI(GL30C.GL_RGBA16UI, EGlVersion.GL_30, EDhPixelFormat.RGBA_INTEGER),
+
+ // 32-bit integer
+ R32I(GL30C.GL_R32I, EGlVersion.GL_30, EDhPixelFormat.RED_INTEGER),
+ RG32I(GL30C.GL_RG32I, EGlVersion.GL_30, EDhPixelFormat.RG_INTEGER),
+ RGB32I(GL30C.GL_RGB32I, EGlVersion.GL_30, EDhPixelFormat.RGB_INTEGER),
+ RGBA32I(GL30C.GL_RGBA32I, EGlVersion.GL_30, EDhPixelFormat.RGBA_INTEGER),
+
+ // 32-bit unsigned integer
+ R32UI(GL30C.GL_R32UI, EGlVersion.GL_30, EDhPixelFormat.RED_INTEGER),
+ RG32UI(GL30C.GL_RG32UI, EGlVersion.GL_30, EDhPixelFormat.RG_INTEGER),
+ RGB32UI(GL30C.GL_RGB32UI, EGlVersion.GL_30, EDhPixelFormat.RGB_INTEGER),
+ RGBA32UI(GL30C.GL_RGBA32UI, EGlVersion.GL_30, EDhPixelFormat.RGBA_INTEGER),
+
+ // Mixed
+ R3_G3_B2(GL11C.GL_R3_G3_B2, EGlVersion.GL_11, EDhPixelFormat.RGB),
+ RGB5_A1(GL11C.GL_RGB5_A1, EGlVersion.GL_11, EDhPixelFormat.RGBA),
+ RGB10_A2(GL11C.GL_RGB10_A2, EGlVersion.GL_11, EDhPixelFormat.RGBA),
+ R11F_G11F_B10F(GL30C.GL_R11F_G11F_B10F, EGlVersion.GL_30, EDhPixelFormat.RGB),
+ RGB9_E5(GL30C.GL_RGB9_E5, EGlVersion.GL_30, EDhPixelFormat.RGB);
+
+
+
+ private final int glFormat;
+ private final EGlVersion minimumGlVersion;
+ private final EDhPixelFormat expectedPixelFormat;
+
+
+
+ EDhInternalTextureFormat(int glFormat, EGlVersion minimumGlVersion, EDhPixelFormat expectedPixelFormat)
+ {
+ this.glFormat = glFormat;
+ this.minimumGlVersion = minimumGlVersion;
+ this.expectedPixelFormat = expectedPixelFormat;
+ }
+
+
+
+ public static Optional fromString(String name)
+ {
+ try
+ {
+ return Optional.of(EDhInternalTextureFormat.valueOf(name.toUpperCase(Locale.US)));
+ }
+ catch (IllegalArgumentException e)
+ {
+ return Optional.empty();
+ }
+ }
+
+ public int getGlFormat() { return this.glFormat; }
+
+ public EDhPixelFormat getPixelFormat() { return this.expectedPixelFormat; }
+
+ public EGlVersion getMinimumGlVersion() { return this.minimumGlVersion; }
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhPixelFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhPixelFormat.java
new file mode 100644
index 000000000..8427e62ea
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhPixelFormat.java
@@ -0,0 +1,60 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+import org.lwjgl.opengl.GL11C;
+import org.lwjgl.opengl.GL12C;
+import org.lwjgl.opengl.GL30C;
+
+import java.util.Locale;
+import java.util.Optional;
+
+public enum EDhPixelFormat
+{
+ RED(GL11C.GL_RED, EGlVersion.GL_11, false),
+ RG(GL30C.GL_RG, EGlVersion.GL_30, false),
+ RGB(GL11C.GL_RGB, EGlVersion.GL_11, false),
+ BGR(GL12C.GL_BGR, EGlVersion.GL_12, false),
+ RGBA(GL11C.GL_RGBA, EGlVersion.GL_11, false),
+ BGRA(GL12C.GL_BGRA, EGlVersion.GL_12, false),
+ RED_INTEGER(GL30C.GL_RED_INTEGER, EGlVersion.GL_30, true),
+ RG_INTEGER(GL30C.GL_RG_INTEGER, EGlVersion.GL_30, true),
+ RGB_INTEGER(GL30C.GL_RGB_INTEGER, EGlVersion.GL_30, true),
+ BGR_INTEGER(GL30C.GL_BGR_INTEGER, EGlVersion.GL_30, true),
+ RGBA_INTEGER(GL30C.GL_RGBA_INTEGER, EGlVersion.GL_30, true),
+ BGRA_INTEGER(GL30C.GL_BGRA_INTEGER, EGlVersion.GL_30, true);
+
+
+
+ private final int glFormat;
+ private final EGlVersion minimumGlVersion;
+ private final boolean isInteger;
+
+
+
+ EDhPixelFormat(int glFormat, EGlVersion minimumGlVersion, boolean isInteger)
+ {
+ this.glFormat = glFormat;
+ this.minimumGlVersion = minimumGlVersion;
+ this.isInteger = isInteger;
+ }
+
+
+
+ public static Optional fromString(String name)
+ {
+ try
+ {
+ return Optional.of(EDhPixelFormat.valueOf(name.toUpperCase(Locale.US)));
+ }
+ catch (IllegalArgumentException e)
+ {
+ return Optional.empty();
+ }
+ }
+
+ public int getGlFormat() { return this.glFormat; }
+
+ public EGlVersion getMinimumGlVersion() { return this.minimumGlVersion; }
+
+ public boolean isInteger() { return this.isInteger; }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhPixelType.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhPixelType.java
new file mode 100644
index 000000000..5d3c5d176
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EDhPixelType.java
@@ -0,0 +1,64 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+import org.lwjgl.opengl.GL11C;
+import org.lwjgl.opengl.GL12C;
+import org.lwjgl.opengl.GL30C;
+
+import java.util.Locale;
+import java.util.Optional;
+
+public enum EDhPixelType
+{
+ BYTE(GL11C.GL_BYTE, EGlVersion.GL_11),
+ SHORT(GL11C.GL_SHORT, EGlVersion.GL_11),
+ INT(GL11C.GL_INT, EGlVersion.GL_11),
+ HALF_FLOAT(GL30C.GL_HALF_FLOAT, EGlVersion.GL_30),
+ FLOAT(GL11C.GL_FLOAT, EGlVersion.GL_11),
+ UNSIGNED_BYTE(GL11C.GL_UNSIGNED_BYTE, EGlVersion.GL_11),
+ UNSIGNED_BYTE_3_3_2(GL12C.GL_UNSIGNED_BYTE_3_3_2, EGlVersion.GL_12),
+ UNSIGNED_BYTE_2_3_3_REV(GL12C.GL_UNSIGNED_BYTE_2_3_3_REV, EGlVersion.GL_12),
+ UNSIGNED_SHORT(GL11C.GL_UNSIGNED_SHORT, EGlVersion.GL_11),
+ UNSIGNED_SHORT_5_6_5(GL12C.GL_UNSIGNED_SHORT_5_6_5, EGlVersion.GL_12),
+ UNSIGNED_SHORT_5_6_5_REV(GL12C.GL_UNSIGNED_SHORT_5_6_5_REV, EGlVersion.GL_12),
+ UNSIGNED_SHORT_4_4_4_4(GL12C.GL_UNSIGNED_SHORT_4_4_4_4, EGlVersion.GL_12),
+ UNSIGNED_SHORT_4_4_4_4_REV(GL12C.GL_UNSIGNED_SHORT_4_4_4_4_REV, EGlVersion.GL_12),
+ UNSIGNED_SHORT_5_5_5_1(GL12C.GL_UNSIGNED_SHORT_5_5_5_1, EGlVersion.GL_12),
+ UNSIGNED_SHORT_1_5_5_5_REV(GL12C.GL_UNSIGNED_SHORT_1_5_5_5_REV, EGlVersion.GL_12),
+ UNSIGNED_INT(GL11C.GL_UNSIGNED_INT, EGlVersion.GL_11),
+ UNSIGNED_INT_8_8_8_8(GL12C.GL_UNSIGNED_INT_8_8_8_8, EGlVersion.GL_12),
+ UNSIGNED_INT_8_8_8_8_REV(GL12C.GL_UNSIGNED_INT_8_8_8_8_REV, EGlVersion.GL_12),
+ UNSIGNED_INT_10_10_10_2(GL12C.GL_UNSIGNED_INT_10_10_10_2, EGlVersion.GL_12),
+ UNSIGNED_INT_2_10_10_10_REV(GL12C.GL_UNSIGNED_INT_2_10_10_10_REV, EGlVersion.GL_12);
+
+
+
+ private final int glFormat;
+ private final EGlVersion minimumGlVersion;
+
+
+
+ EDhPixelType(int glFormat, EGlVersion minimumGlVersion)
+ {
+ this.glFormat = glFormat;
+ this.minimumGlVersion = minimumGlVersion;
+ }
+
+
+
+ public static Optional fromString(String name)
+ {
+ try
+ {
+ return Optional.of(EDhPixelType.valueOf(name.toUpperCase(Locale.US)));
+ }
+ catch (IllegalArgumentException e)
+ {
+ return Optional.empty();
+ }
+ }
+
+ public int getGlFormat() { return glFormat; }
+
+ public EGlVersion getMinimumGlVersion() { return minimumGlVersion; }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EGlVersion.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EGlVersion.java
new file mode 100644
index 000000000..6a256d2d3
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/texture/EGlVersion.java
@@ -0,0 +1,9 @@
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.texture;
+
+public enum EGlVersion
+{
+ GL_11,
+ GL_12,
+ GL_30,
+ GL_31
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/AbstractVertexAttribute.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/AbstractVertexAttribute.java
new file mode 100644
index 000000000..224ad8b0b
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/AbstractVertexAttribute.java
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.GLProxy;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * Base for binding/unbinding Vertex Attribute objects (VAO's).
+ *
+ * @see VertexAttributePostGL43
+ * @see VertexAttributePreGL43
+ */
+public abstract class AbstractVertexAttribute
+{
+ /** Stores the handle of the AbstractVertexAttribute. */
+ public final int id;
+
+
+
+ //==============//
+ // constructors //
+ //==============//
+
+ // This will bind AbstractVertexAttribute
+ protected AbstractVertexAttribute()
+ {
+ this.id = GL32.glGenVertexArrays();
+ GL32.glBindVertexArray(this.id);
+ }
+
+ public static AbstractVertexAttribute create()
+ {
+ if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
+ {
+ return new VertexAttributePostGL43();
+ }
+ else
+ {
+ return new VertexAttributePreGL43();
+ }
+ }
+
+
+
+ //=========//
+ // binding //
+ //=========//
+
+ public void bind() { GL32.glBindVertexArray(this.id); }
+ public void unbind() { GL32.glBindVertexArray(0); }
+
+ /** Always remember to always free your resources! */
+ public void free() { GL32.glDeleteVertexArrays(this.id); }
+
+
+
+ //==================//
+ // abstract methods //
+ //==================//
+
+ /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
+ public abstract void bindBufferToAllBindingPoints(int buffer);
+ /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
+ public abstract void bindBufferToBindingPoint(int buffer, int bindingPoint);
+ /** Requires both AbstractVertexAttribute to be bound */
+ public abstract void unbindBuffersFromAllBindingPoint();
+ /** Requires both AbstractVertexAttribute to be bound */
+ public abstract void unbindBuffersFromBindingPoint(int bindingPoint);
+ /** Requires both AbstractVertexAttribute to be bound */
+ public abstract void setVertexAttribute(int bindingPoint, int attributeIndex, VertexPointer attribute);
+ /** Requires both AbstractVertexAttribute to be bound */
+ public abstract void completeAndCheck(int expectedStrideSize);
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexAttributePostGL43.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexAttributePostGL43.java
new file mode 100644
index 000000000..7733fbf50
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexAttributePostGL43.java
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute;
+
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import org.lwjgl.opengl.GL43;
+
+/**
+ * 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 is just use ONE native call when
+ * binding to a buffer.
+ *
+ * Since I no longer need to implement binding points, I also no
+ * longer needs to keep track of Pointers.
+ */
+public final class VertexAttributePostGL43 extends AbstractVertexAttribute
+{
+ private static final DhLogger LOGGER = new DhLoggerBuilder()
+ .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
+ .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
+ .build();
+
+
+ int numberOfBindingPoints = 0;
+ int strideSize = 0;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ /** This will bind the {@link AbstractVertexAttribute} */
+ public VertexAttributePostGL43()
+ {
+ super(); // also bind AbstractVertexAttribute
+ }
+
+
+
+ //=========//
+ // binding //
+ //=========//
+
+ /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
+ @Override
+ public void bindBufferToAllBindingPoints(int buffer)
+ {
+ for (int i = 0; i < this.numberOfBindingPoints; i++)
+ {
+ GL43.glBindVertexBuffer(i, buffer, 0, this.strideSize);
+ }
+ }
+
+ /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
+ @Override
+ public void bindBufferToBindingPoint(int buffer, int bindingPoint)
+ {
+ GL43.glBindVertexBuffer(bindingPoint, buffer, 0, this.strideSize);
+ }
+
+
+
+ //===========//
+ // unbinding //
+ //===========//
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void unbindBuffersFromAllBindingPoint()
+ {
+ for (int i = 0; i < this.numberOfBindingPoints; i++)
+ {
+ GL43.glBindVertexBuffer(i, 0, 0, 0);
+ }
+ }
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void unbindBuffersFromBindingPoint(int bindingPoint)
+ {
+ GL43.glBindVertexBuffer(bindingPoint, 0, 0, 0);
+ }
+
+
+
+ //==========================//
+ // manual attribute setting //
+ //==========================//
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void setVertexAttribute(int bindingPoint, int attributeIndex, VertexPointer attribute)
+ {
+ if (attribute.useInteger)
+ {
+ GL43.glVertexAttribIFormat(attributeIndex, attribute.elementCount, attribute.glType, this.strideSize);
+ }
+ else
+ {
+ GL43.glVertexAttribFormat(attributeIndex, attribute.elementCount, attribute.glType,
+ attribute.normalized, this.strideSize); // Here strideSize is new attrib offset
+ }
+
+ this.strideSize += attribute.byteSize;
+ if (this.numberOfBindingPoints <= bindingPoint)
+ {
+ this.numberOfBindingPoints = bindingPoint + 1;
+ }
+ GL43.glVertexAttribBinding(attributeIndex, bindingPoint);
+ GL43.glEnableVertexAttribArray(attributeIndex);
+ }
+
+
+
+ //============//
+ // validation //
+ //============//
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void completeAndCheck(int expectedStrideSize)
+ {
+ if (this.strideSize != expectedStrideSize)
+ {
+ LOGGER.error("Vertex Attribute calculated stride size " + this.strideSize +
+ " does not match the provided expected stride size " + expectedStrideSize + "!");
+ throw new IllegalArgumentException("Vertex Attribute Incorrect Format");
+ }
+
+ LOGGER.info("Vertex Attribute (GL43+) completed. It contains " + this.numberOfBindingPoints
+ + " binding points and a stride size of " + this.strideSize);
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexAttributePreGL43.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexAttributePreGL43.java
new file mode 100644
index 000000000..5c18b2b13
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexAttributePreGL43.java
@@ -0,0 +1,253 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import org.lwjgl.opengl.GL32;
+
+
+public final class VertexAttributePreGL43 extends AbstractVertexAttribute
+{
+ private static final DhLogger LOGGER = new DhLoggerBuilder()
+ .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
+ .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
+ .build();
+
+
+ // I tried to use raw arrays as much as possible since those lookups
+ // happen every frame, and the speed directly affects fps
+ int strideSize = 0;
+ int[][] bindingPointsToIndex;
+ VertexPointer[] pointers;
+ int[] pointersOffset;
+
+ TreeMap> bindingPointsToIndexBuilder;
+ ArrayList pointersBuilder;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ /** This will bind the {@link AbstractVertexAttribute} */
+ public VertexAttributePreGL43()
+ {
+ super(); // also bind AbstractVertexAttribute
+ this.bindingPointsToIndexBuilder = new TreeMap<>();
+ this.pointersBuilder = new ArrayList<>();
+ }
+
+
+
+ //=========//
+ // binding //
+ //=========//
+
+ /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
+ @Override
+ public void bindBufferToAllBindingPoints(int buffer)
+ {
+ for (int i = 0; i < this.pointers.length; i++)
+ {
+ GL32.glEnableVertexAttribArray(i);
+ }
+
+ for (int i = 0; i < this.pointers.length; i++)
+ {
+ VertexPointer pointer = this.pointers[i];
+ if (pointer == null)
+ {
+ continue;
+ }
+
+ if (pointer.useInteger)
+ {
+ GL32.glVertexAttribIPointer(i, pointer.elementCount, pointer.glType,
+ this.strideSize, this.pointersOffset[i]);
+ }
+ else
+ {
+ GL32.glVertexAttribPointer(i, pointer.elementCount, pointer.glType,
+ pointer.normalized, this.strideSize, this.pointersOffset[i]);
+ }
+ }
+ }
+
+ /** Requires both AbstractVertexAttribute and VertexBuffer to be bound */
+ @Override
+ public void bindBufferToBindingPoint(int buffer, int bindingPoint)
+ {
+ int[] bindingPointIndexes = this.bindingPointsToIndex[bindingPoint];
+
+ for (int bindingPointIndex : bindingPointIndexes)
+ {
+ GL32.glEnableVertexAttribArray(bindingPointIndex);
+ }
+
+ for (int bindingPointIndex : bindingPointIndexes)
+ {
+ VertexPointer pointer = this.pointers[bindingPointIndex];
+ if (pointer == null)
+ {
+ continue;
+ }
+
+ if (pointer.useInteger)
+ {
+ GL32.glVertexAttribIPointer(bindingPointIndex, pointer.elementCount, pointer.glType,
+ this.strideSize, this.pointersOffset[bindingPointIndex]);
+ }
+ else
+ {
+ GL32.glVertexAttribPointer(bindingPointIndex, pointer.elementCount, pointer.glType,
+ pointer.normalized, this.strideSize, this.pointersOffset[bindingPointIndex]);
+ }
+ }
+
+ }
+
+
+
+ //===========//
+ // unbinding //
+ //===========//
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void unbindBuffersFromAllBindingPoint()
+ {
+ for (int i = 0; i < this.pointers.length; i++)
+ {
+ GL32.glDisableVertexAttribArray(i);
+ }
+ }
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void unbindBuffersFromBindingPoint(int bindingPoint)
+ {
+ int[] bindingPointIndexes = this.bindingPointsToIndex[bindingPoint];
+ for (int bindingPointIndex : bindingPointIndexes)
+ {
+ GL32.glDisableVertexAttribArray(bindingPointIndex);
+ }
+ }
+
+
+
+ //==========================//
+ // manual attribute setting //
+ //==========================//
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void setVertexAttribute(int bindingPoint, int attributeIndex, VertexPointer attribute)
+ {
+ TreeSet intArray = this.bindingPointsToIndexBuilder.computeIfAbsent(bindingPoint, k -> new TreeSet<>());
+ intArray.add(attributeIndex);
+
+ while (this.pointersBuilder.size() <= attributeIndex)
+ {
+ // This is dumb, but ArrayList doesn't have a resize, And this code
+ // should only be run when it's building the Vertex Attribute anyway.
+ this.pointersBuilder.add(null);
+ }
+ this.pointersBuilder.set(attributeIndex, attribute);
+ }
+
+
+
+ //============//
+ // validation //
+ //============//
+
+ /** Requires AbstractVertexAttribute to be bound */
+ @Override
+ public void completeAndCheck(int expectedStrideSize)
+ {
+ int maxBindPointNumber = this.bindingPointsToIndexBuilder.lastKey();
+ this.bindingPointsToIndex = new int[maxBindPointNumber + 1][];
+
+ this.bindingPointsToIndexBuilder.forEach((Integer i, TreeSet set) ->
+ {
+ this.bindingPointsToIndex[i] = new int[set.size()];
+ Iterator iter = set.iterator();
+ for (int j = 0; j < set.size(); j++)
+ {
+ this.bindingPointsToIndex[i][j] = iter.next();
+ }
+ });
+
+ this.pointers = this.pointersBuilder.toArray(new VertexPointer[this.pointersBuilder.size()]);
+ this.pointersOffset = new int[this.pointers.length];
+ this.pointersBuilder = null; // Release the builder
+ this.bindingPointsToIndexBuilder = null; // Release the builder
+
+ // Check if all pointers are valid
+ int currentOffset = 0;
+ for (int i = 0; i < this.pointers.length; i++)
+ {
+ VertexPointer pointer = this.pointers[i];
+ if (pointer == null)
+ {
+ LOGGER.warn("Vertex Attribute index " + i + " is not set! No index should be skipped normally!");
+ continue;
+ }
+ this.pointersOffset[i] = currentOffset;
+ currentOffset += pointer.byteSize;
+ }
+
+ if (currentOffset != expectedStrideSize)
+ {
+ LOGGER.error("Vertex Attribute calculated stride size " + currentOffset +
+ " does not match the provided expected stride size " + expectedStrideSize + "!");
+ throw new IllegalArgumentException("Vertex Attribute Incorrect Format");
+ }
+ this.strideSize = currentOffset;
+ LOGGER.info("Vertex Attribute (pre GL43) completed.");
+
+ // Debug logging
+ LOGGER.debug("AttributeIndex: ElementCount, glType, normalized, strideSize, offset");
+
+ for (int i = 0; i < this.pointers.length; i++)
+ {
+ VertexPointer pointer = this.pointers[i];
+ if (pointer == null)
+ {
+ LOGGER.debug(i + ": Null!!!!");
+ }
+ else
+ {
+ LOGGER.debug(i + ": " + pointer.elementCount + ", " +
+ pointer.glType + ", " + pointer.normalized + ", " + this.strideSize + ", " + this.pointersOffset[i]);
+ }
+ }
+
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexPointer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexPointer.java
new file mode 100644
index 000000000..e05b2a621
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/glObject/vertexAttribute/VertexPointer.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute;
+
+import com.seibel.distanthorizons.coreapi.util.MathUtil;
+import org.lwjgl.opengl.GL32;
+
+public final class VertexPointer
+{
+ public final int elementCount;
+ public final int glType;
+ public final boolean normalized;
+ public final int byteSize;
+ public final boolean useInteger;
+
+
+
+ // basic constructors //
+
+ public VertexPointer(int elementCount, int glType, boolean normalized, int byteSize, boolean useInteger)
+ {
+ this.elementCount = elementCount;
+ this.glType = glType;
+ this.normalized = normalized;
+ this.byteSize = byteSize;
+ this.useInteger = useInteger;
+ }
+ public VertexPointer(int elementCount, int glType, boolean normalized, int byteSize)
+ {
+ this(elementCount, glType, normalized, byteSize, false);
+ }
+ private static int _align(int bytes) { return MathUtil.ceilDiv(bytes, 4) * 4; }
+
+
+
+ // named constructors //
+
+ public static VertexPointer addFloatPointer(boolean normalized) { return new VertexPointer(1, GL32.GL_FLOAT, normalized, Float.BYTES); }
+ public static VertexPointer addVec2Pointer(boolean normalized) { return new VertexPointer(2, GL32.GL_FLOAT, normalized, Float.BYTES * 2); }
+ public static VertexPointer addVec3Pointer(boolean normalized) { return new VertexPointer(3, GL32.GL_FLOAT, normalized, Float.BYTES * 3); }
+ public static VertexPointer addVec4Pointer(boolean normalized) { return new VertexPointer(4, GL32.GL_FLOAT, normalized, Float.BYTES * 4); }
+ /** Always aligned to 4 bytes */
+ public static VertexPointer addUnsignedBytePointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 4, useInteger); }
+ /** aligned to 4 bytes */
+ public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized, boolean useInteger)
+ { return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount), useInteger); }
+ public static VertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized, boolean useInteger)
+ { return new VertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount * 2), useInteger); }
+ public static VertexPointer addShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_SHORT, normalized, _align(elementCount * 2), useInteger); }
+ public static VertexPointer addIntPointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_INT, normalized, 4, useInteger); }
+ public static VertexPointer addIVec2Pointer(boolean normalized, boolean useInteger) { return new VertexPointer(2, GL32.GL_INT, normalized, 8, useInteger); }
+ public static VertexPointer addIVec3Pointer(boolean normalized, boolean useInteger) { return new VertexPointer(3, GL32.GL_INT, normalized, 12, useInteger); }
+ public static VertexPointer addIVec4Pointer(boolean normalized, boolean useInteger) { return new VertexPointer(4, GL32.GL_INT, normalized, 16, useInteger); }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ScreenQuad.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ScreenQuad.java
new file mode 100644
index 000000000..8405d39e7
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ScreenQuad.java
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute.AbstractVertexAttribute;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute.VertexPointer;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.system.MemoryUtil;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Renders a full-screen textured quad to the screen.
+ * Used in composite / deferred rendering (IE fog).
+ */
+public class ScreenQuad
+{
+ public static ScreenQuad INSTANCE = new ScreenQuad();
+
+ private static final float[] box_vertices = {
+ -1, -1,
+ 1, -1,
+ 1, 1,
+ -1, -1,
+ 1, 1,
+ -1, 1,
+ };
+
+ private AbstractVertexAttribute va;
+ private boolean init = false;
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ private ScreenQuad() { }
+
+ public void init()
+ {
+ if (this.init) return;
+ this.init = true;
+
+ this.va = AbstractVertexAttribute.create();
+ this.va.bind();
+
+ // Pos
+ this.va.setVertexAttribute(0, 0, VertexPointer.addVec2Pointer(false));
+ this.va.completeAndCheck(Float.BYTES * 2);
+
+ // Framebuffer
+ this.createBuffer();
+ }
+
+ public void render()
+ {
+ this.init();
+
+ //this.boxBuffer.bind();
+
+ this.va.bind();
+ //this.va.bindBufferToAllBindingPoints(this.boxBuffer.getId());
+
+ GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6);
+ }
+
+ private void createBuffer()
+ {
+ ByteBuffer buffer = MemoryUtil.memAlloc(box_vertices.length * Float.BYTES);
+ buffer.asFloatBuffer().put(box_vertices);
+ buffer.rewind();
+
+ //this.boxBuffer = new GLVertexBuffer(false);
+ //this.boxBuffer.bind();
+ //this.boxBuffer.uploadBuffer(buffer, box_vertices.length, EDhApiGpuUploadMethod.DATA, box_vertices.length * Float.BYTES);
+ MemoryUtil.memFree(buffer);
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/VanillaFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/VanillaFadeRenderer.java
new file mode 100644
index 000000000..70a777c2e
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/VanillaFadeRenderer.java
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.GLState;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fade.DhFadeShader;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fade.FadeApplyShader;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fade.VanillaFadeShader;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import org.lwjgl.opengl.GL32;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Handles fading MC and DH together via {@link VanillaFadeShader} and {@link FadeApplyShader}.
+ *
+ * {@link VanillaFadeShader} - draws the Fade to a texture.
+ * {@link FadeApplyShader} - draws the Fade texture to MC's FrameBuffer.
+ */
+public class VanillaFadeRenderer
+{
+ public static VanillaFadeRenderer INSTANCE = new VanillaFadeRenderer();
+
+ private static final DhLogger LOGGER = new DhLoggerBuilder().build();
+
+ private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ private boolean init = false;
+
+ private int width = -1;
+ private int height = -1;
+ private int fadeFramebuffer = -1;
+
+ private int fadeTexture = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ private VanillaFadeRenderer() { }
+
+ public void init()
+ {
+ if (this.init) return;
+ this.init = true;
+
+ VanillaFadeShader.INSTANCE.init();
+ FadeApplyShader.INSTANCE.init();
+ }
+
+ private void createFramebuffer(int width, int height)
+ {
+ if (this.fadeFramebuffer != -1)
+ {
+ GL32.glDeleteFramebuffers(this.fadeFramebuffer);
+ this.fadeFramebuffer = -1;
+ }
+
+ this.fadeFramebuffer = GL32.glGenFramebuffers();
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fadeFramebuffer);
+
+
+ // Applying the fade texture is only needed if MC is drawing to their own frame buffer,
+ // otherwise we can directly render to their texture
+ if (MC_RENDER.mcRendersToFrameBuffer())
+ {
+ if (this.fadeTexture != -1)
+ {
+ GLMC.glDeleteTextures(this.fadeTexture);
+ this.fadeTexture = -1;
+ }
+
+ this.fadeTexture = GL32.glGenTextures();
+ GLMC.glBindTexture(this.fadeTexture);
+ GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0);
+ }
+ else
+ {
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, MC_RENDER.getColorTextureId(), 0);
+ }
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, IClientLevelWrapper level)
+ {
+ int depthTextureId = BlazeLodRenderer.INSTANCE.getActiveDepthTextureId();
+ if (depthTextureId == -1)
+ {
+ // the renderer hasn't been set up yet
+ // trying to render fading may cause GL errors
+ return;
+ }
+
+
+
+ IProfilerWrapper profiler = MC_CLIENT.getProfiler();
+ profiler.pop(); // get out of "terrain"
+ profiler.push("DH-Vanilla Fade");
+
+
+ try(GLState mcState = new GLState())
+ {
+ profiler.push("Vanilla Fade Generate");
+
+ this.init();
+
+ // resize the framebuffer if necessary
+ int width = MC_RENDER.getTargetFramebufferViewportWidth();
+ int height = MC_RENDER.getTargetFramebufferViewportHeight();
+ if (this.width != width || this.height != height)
+ {
+ this.width = width;
+ this.height = height;
+ this.createFramebuffer(width, height);
+ }
+
+
+ VanillaFadeShader.INSTANCE.frameBuffer = this.fadeFramebuffer;
+ VanillaFadeShader.INSTANCE.setProjectionMatrix(mcModelViewMatrix, mcProjectionMatrix);
+ VanillaFadeShader.INSTANCE.setLevelMaxHeight(level.getMaxHeight());
+ VanillaFadeShader.INSTANCE.render(0);
+
+ // Applying the fade texture is only needed if MC is drawing to their own frame buffer,
+ // otherwise we can directly render to their texture
+ if (MC_RENDER.mcRendersToFrameBuffer())
+ {
+ profiler.popPush("Vanilla Fade Apply");
+
+ FadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture;
+ FadeApplyShader.INSTANCE.readFramebuffer = DhFadeShader.INSTANCE.frameBuffer;
+ FadeApplyShader.INSTANCE.drawFramebuffer = MC_RENDER.getTargetFramebuffer();
+ FadeApplyShader.INSTANCE.render(0);
+ }
+
+ profiler.pop();
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Unexpected error during fade render, error: ["+e.getMessage()+"].", e);
+ }
+ }
+
+ public void free()
+ {
+ VanillaFadeShader.INSTANCE.free();
+ FadeApplyShader.INSTANCE.free();
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/apply/DhApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/apply/DhApplyShader.java
new file mode 100644
index 000000000..718e6a3c9
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/apply/DhApplyShader.java
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.apply;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.GLState;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * Copies {@link BlazeLodRenderer}'s currently active color and depth texture to Minecraft's framebuffer.
+ */
+public class DhApplyShader extends AbstractShaderRenderer
+{
+ public static DhApplyShader INSTANCE = new DhApplyShader();
+
+ private static final DhLogger LOGGER = new DhLoggerBuilder().build();
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ // uniforms
+ public int gDhColorTextureUniform;
+ public int gDepthMapUniform;
+
+
+
+ //=======//
+ // setup //
+ //=======//
+ //region
+
+ private DhApplyShader() { }
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/apply.frag",
+ "vPosition"
+ );
+
+ // uniform setup
+ this.gDhColorTextureUniform = this.shader.getUniformLocation("gDhColorTexture");
+ this.gDepthMapUniform = this.shader.getUniformLocation("gDhDepthTexture");
+
+ }
+
+ @Override
+ protected void onApplyUniforms(float partialTicks) { }
+
+ //endregion
+
+
+
+ //========//
+ // render //
+ //========//
+ //region
+
+ @Override
+ protected void onRender()
+ {
+ if (MC_RENDER.mcRendersToFrameBuffer())
+ {
+ this.renderToFrameBuffer();
+ }
+ else
+ {
+ this.renderToMcTexture();
+ }
+ }
+ private void renderToFrameBuffer()
+ {
+ int targetFrameBuffer = MC_RENDER.getTargetFramebuffer();
+ if (targetFrameBuffer == -1)
+ {
+ return;
+ }
+
+
+ try (GLState state = new GLState())
+ {
+
+ GLMC.disableDepthTest();
+
+ // blending isn't needed, we're manually merging the MC and DH textures
+ // Note: this prevents the sun/moon and stars from rendering through transparent LODs,
+ // however this also fixes transparent LODs from glowing when rendered against the sky during the day
+ GLMC.disableBlend();
+
+ // old blending logic in case it's ever needed:
+ //GLMC.enableBlend();
+ //GL32.glBlendEquation(GL32.GL_FUNC_ADD);
+ //GLMC.glBlendFunc(GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveColorTextureId());
+ GL32.glUniform1i(this.gDhColorTextureUniform, 0);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE1);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveDepthTextureId());
+ GL32.glUniform1i(this.gDepthMapUniform, 1);
+
+ // Copy to MC's framebuffer
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, targetFrameBuffer);
+
+ ScreenQuad.INSTANCE.render();
+ }
+ // everything's been restored, except at this point the MC framebuffer should now be used instead
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, targetFrameBuffer);
+
+ }
+ private void renderToMcTexture()
+ {
+ int targetColorTextureId = MC_RENDER.getColorTextureId();
+ if (targetColorTextureId == -1)
+ {
+ return;
+ }
+
+ int dhFrameBufferId = BlazeLodRenderer.INSTANCE.getActiveFramebufferId();
+ if (dhFrameBufferId == -1)
+ {
+ return;
+ }
+
+ int mcFrameBufferId = MC_RENDER.getTargetFramebuffer();
+ if (mcFrameBufferId == -1)
+ {
+ return;
+ }
+
+
+
+ try (GLState state = new GLState())
+ {
+ GLMC.disableDepthTest();
+
+ // blending isn't needed, we're just directly merging the MC and DH textures
+ // Note: this prevents the sun/moon and stars from rendering through transparent LODs,
+ // however this also fixes
+ GLMC.disableBlend();
+
+ // old blending logic in case it's ever needed:
+ //GLMC.enableBlend();
+ //GL32.glBlendEquation(GL32.GL_FUNC_ADD);
+ //GLMC.glBlendFunc(GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveColorTextureId());
+ GL32.glUniform1i(this.gDhColorTextureUniform, 0);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE1);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveDepthTextureId());
+ GL32.glUniform1i(this.gDepthMapUniform, 1);
+
+
+
+ GL32.glFramebufferTexture(GL32.GL_DRAW_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, targetColorTextureId, 0);
+
+ // Copy to MC's texture via MC's framebuffer
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, dhFrameBufferId);
+
+ ScreenQuad.INSTANCE.render();
+ }
+ // everything's been restored, except at this point the MC framebuffer should now be used instead
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, mcFrameBufferId);
+
+ }
+
+ //endregion
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/DhFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/DhFadeRenderer.java
new file mode 100644
index 000000000..222f26fe9
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/DhFadeRenderer.java
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fade;
+
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.opengl.GL43C;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Handles fading MC and DH together via {@link DhFadeShader} and {@link FadeApplyShader}.
+ *
+ * {@link DhFadeShader} - draws the Fade to a texture.
+ * {@link FadeApplyShader} - draws the Fade texture to DH's framebuffer.
+ */
+public class DhFadeRenderer
+{
+
+ public static DhFadeRenderer INSTANCE = new DhFadeRenderer();
+ private static final DhLogger LOGGER = new DhLoggerBuilder().build();
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ private boolean init = false;
+
+ private int width = -1;
+ private int height = -1;
+ private int fadeFramebuffer = -1;
+
+ private int fadeTexture = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ private DhFadeRenderer() { }
+
+ public void init()
+ {
+ if (this.init) return;
+ this.init = true;
+
+ DhFadeShader.INSTANCE.init();
+ FadeApplyShader.INSTANCE.init();
+ }
+
+ private void createFramebuffer(int width, int height)
+ {
+ if (this.fadeFramebuffer != -1)
+ {
+ GL32.glDeleteFramebuffers(this.fadeFramebuffer);
+ this.fadeFramebuffer = -1;
+ }
+
+ this.fadeFramebuffer = GL32.glGenFramebuffers();
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fadeFramebuffer);
+
+
+ if (this.fadeTexture != -1)
+ {
+ GLMC.glDeleteTextures(this.fadeTexture);
+ this.fadeTexture = -1;
+ }
+
+ this.fadeTexture = GL32.glGenTextures();
+ {
+ GLMC.glBindTexture(this.fadeTexture);
+ GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
+
+ // disable mip-mapping since DH is just going to draw straight to the screen
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
+ }
+
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0);
+
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler)
+ {
+ try
+ {
+ profiler.push("Fade Generate");
+
+ this.init();
+
+ // resize the framebuffer if necessary
+ int width = MC_RENDER.getTargetFramebufferViewportWidth();
+ int height = MC_RENDER.getTargetFramebufferViewportHeight();
+ if (this.width != width || this.height != height)
+ {
+ this.width = width;
+ this.height = height;
+ this.createFramebuffer(width, height);
+ }
+
+
+ DhFadeShader.INSTANCE.frameBuffer = this.fadeFramebuffer;
+ DhFadeShader.INSTANCE.setProjectionMatrix(mcModelViewMatrix, mcProjectionMatrix);
+ DhFadeShader.INSTANCE.render(partialTicks);
+
+ // restored so we can write the fade texture to the main frame buffer
+ //mcState.restore();
+
+ profiler.popPush("Fade Apply");
+
+ FadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture;
+ FadeApplyShader.INSTANCE.readFramebuffer = DhFadeShader.INSTANCE.frameBuffer;
+ FadeApplyShader.INSTANCE.drawFramebuffer = BlazeLodRenderer.INSTANCE.getActiveFramebufferId();
+ FadeApplyShader.INSTANCE.render(partialTicks);
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Unexpected error during fade render, error: ["+e.getMessage()+"].", e);
+ }
+ finally
+ {
+ profiler.pop();
+ }
+ }
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/DhFadeShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/DhFadeShader.java
new file mode 100644
index 000000000..e8037a8d3
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/DhFadeShader.java
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fade;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import com.seibel.distanthorizons.core.util.RenderUtil;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import org.lwjgl.opengl.GL32;
+
+public class DhFadeShader extends AbstractShaderRenderer
+{
+ public static DhFadeShader INSTANCE = new DhFadeShader();
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ public int frameBuffer = -1;
+
+ private Mat4f inverseDhMvmProjMatrix;
+
+
+ // Uniforms
+
+ /** Inverted Model View Projection matrix */
+ public int uDhInvMvmProj = -1;
+
+ public int uDhDepthTexture = -1;
+ public int uMcColorTexture = -1;
+ public int uDhColorTexture = -1;
+
+ public int uStartFadeBlockDistance = -1;
+ public int uEndFadeBlockDistance = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public DhFadeShader() { }
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/fade/dhFade.frag",
+ "vPosition"
+ );
+
+ // all uniforms should be tryGet...
+ // because disabling fade can cause the GLSL to optimize out most (if not all) uniforms
+
+ // near fade
+ this.uDhInvMvmProj = this.shader.tryGetUniformLocation("uDhInvMvmProj");
+
+ this.uDhDepthTexture = this.shader.tryGetUniformLocation("uDhDepthTexture");
+ this.uMcColorTexture = this.shader.tryGetUniformLocation("uMcColorTexture");
+ this.uDhColorTexture = this.shader.tryGetUniformLocation("uDhColorTexture");
+
+ this.uStartFadeBlockDistance = this.shader.tryGetUniformLocation("uStartFadeBlockDistance");
+ this.uEndFadeBlockDistance = this.shader.tryGetUniformLocation("uEndFadeBlockDistance");
+
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ this.shader.setUniform(this.uDhInvMvmProj, this.inverseDhMvmProjMatrix);
+
+
+ float dhFarClipDistance = RenderUtil.getFarClipPlaneDistanceInBlocks();
+ float fadeStartDistance = dhFarClipDistance * 0.5f;
+ float fadeEndDistance = dhFarClipDistance * 0.9f;
+
+ this.shader.setUniform(this.uStartFadeBlockDistance, fadeStartDistance);
+ this.shader.setUniform(this.uEndFadeBlockDistance, fadeEndDistance);
+
+ }
+
+ public void setProjectionMatrix(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix)
+ {
+ Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(mcProjectionMatrix);
+ Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(mcModelViewMatrix);
+
+ Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix);
+ inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix);
+ inverseDhModelViewProjectionMatrix.invert();
+ this.inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix;
+ }
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ int depthTextureId = BlazeLodRenderer.INSTANCE.getActiveDepthTextureId();
+ int colorTextureId = BlazeLodRenderer.INSTANCE.getActiveColorTextureId();
+
+ if (depthTextureId == -1
+ || colorTextureId == -1)
+ {
+ // the renderer is currently being re-built and/or inactive,
+ // we don't need to/can't render fading
+ return;
+ }
+
+
+
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
+ GLMC.disableScissorTest();
+ GLMC.disableDepthTest();
+ GLMC.disableBlend();
+
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(depthTextureId);
+ GL32.glUniform1i(this.uDhDepthTexture, 0);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE1);
+ GLMC.glBindTexture(MC_RENDER.getColorTextureId());
+ GL32.glUniform1i(this.uMcColorTexture, 1);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE2);
+ GLMC.glBindTexture(colorTextureId);
+ GL32.glUniform1i(this.uDhColorTexture, 2);
+
+
+ ScreenQuad.INSTANCE.render();
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/FadeApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/FadeApplyShader.java
new file mode 100644
index 000000000..366755515
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/FadeApplyShader.java
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fade;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.VanillaFadeRenderer;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * Draws the Fade texture onto Minecraft's FrameBuffer.
+ *
+ * See Also:
+ * {@link VanillaFadeRenderer} - Parent to this shader.
+ * {@link VanillaFadeShader} - draws the Fade texture.
+ */
+public class FadeApplyShader extends AbstractShaderRenderer
+{
+ public static FadeApplyShader INSTANCE = new FadeApplyShader();
+
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+
+ public int fadeTexture;
+
+ public int readFramebuffer;
+ public int drawFramebuffer;
+
+ // uniforms
+ public int uFadeColorTextureUniform = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/fade/apply.frag",
+ "vPosition"
+ );
+
+ // uniform setup
+ this.uFadeColorTextureUniform = this.shader.getUniformLocation("uFadeColorTextureUniform");
+
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(this.fadeTexture);
+ GL32.glUniform1i(this.uFadeColorTextureUniform, 0);
+
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ GLMC.disableBlend();
+
+ // Depth testing must be disabled otherwise this application shader won't apply anything.
+ // setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually,
+ // it should be automatically restored after rendering is complete.
+ GLMC.disableDepthTest();
+
+
+ // apply the rendered Fade to Minecraft's framebuffer
+ GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, this.readFramebuffer);
+ GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, this.drawFramebuffer);
+
+ ScreenQuad.INSTANCE.render();
+
+ GLMC.enableDepthTest();
+
+ }
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/VanillaFadeShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/VanillaFadeShader.java
new file mode 100644
index 000000000..48f625d57
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fade/VanillaFadeShader.java
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fade;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import com.seibel.distanthorizons.core.util.RenderUtil;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import org.lwjgl.opengl.GL32;
+
+public class VanillaFadeShader extends AbstractShaderRenderer
+{
+ public static VanillaFadeShader INSTANCE = new VanillaFadeShader();
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ public int frameBuffer = -1;
+
+ private Mat4f inverseMcMvmProjMatrix;
+ private Mat4f inverseDhMvmProjMatrix;
+ private float levelMaxHeight;
+
+
+ // Uniforms
+ public int uMcDepthTexture = -1;
+ public int uDhDepthTexture = -1;
+ public int uCombinedMcDhColorTexture = -1;
+ public int uDhColorTexture = -1;
+
+ /** Inverted Model View Projection matrix */
+ public int uDhInvMvmProj = -1;
+ public int uMcInvMvmProj = -1;
+
+ public int uStartFadeBlockDistance = -1;
+ public int uEndFadeBlockDistance = -1;
+ public int uMaxLevelHeight = -1;
+
+ public int uOnlyRenderLods = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public VanillaFadeShader() { }
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/fade/vanillaFade.frag",
+ "vPosition"
+ );
+
+ // all uniforms should be tryGet...
+ // because disabling fade can cause the GLSL to optimize out most (if not all) uniforms
+
+ // near fade
+ this.uDhInvMvmProj = this.shader.tryGetUniformLocation("uDhInvMvmProj");
+ this.uMcInvMvmProj = this.shader.tryGetUniformLocation("uMcInvMvmProj");
+
+ this.uMcDepthTexture = this.shader.tryGetUniformLocation("uMcDepthTexture");
+ this.uDhDepthTexture = this.shader.tryGetUniformLocation("uDhDepthTexture");
+ this.uCombinedMcDhColorTexture = this.shader.tryGetUniformLocation("uCombinedMcDhColorTexture");
+ this.uDhColorTexture = this.shader.tryGetUniformLocation("uDhColorTexture");
+
+ this.uStartFadeBlockDistance = this.shader.tryGetUniformLocation("uStartFadeBlockDistance");
+ this.uEndFadeBlockDistance = this.shader.tryGetUniformLocation("uEndFadeBlockDistance");
+ this.uMaxLevelHeight = this.shader.tryGetUniformLocation("uMaxLevelHeight");
+
+ this.uOnlyRenderLods = this.shader.tryGetUniformLocation("uOnlyRenderLods");
+
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ this.shader.setUniform(this.uMcInvMvmProj, this.inverseMcMvmProjMatrix);
+ this.shader.setUniform(this.uDhInvMvmProj, this.inverseDhMvmProjMatrix);
+
+
+ float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks();
+ // this added value prevents the near clip plane and discard circle from touching, which looks bad
+ dhNearClipDistance += 16f;
+
+ // measured in blocks
+ // these multipliers in James' tests should provide a fairly smooth transition
+ // without having underdraw issues
+ float fadeStartDistance = dhNearClipDistance * 1.5f;
+ float fadeEndDistance = dhNearClipDistance * 1.9f;
+
+ this.shader.setUniform(this.uStartFadeBlockDistance, fadeStartDistance);
+ this.shader.setUniform(this.uEndFadeBlockDistance, fadeEndDistance);
+
+ this.shader.setUniform(this.uMaxLevelHeight, this.levelMaxHeight);
+
+ this.shader.setUniform(this.uOnlyRenderLods, Config.Client.Advanced.Debugging.lodOnlyMode.get());
+ }
+
+ public void setProjectionMatrix(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix)
+ {
+ Mat4f inverseMcModelViewProjectionMatrix = new Mat4f(mcProjectionMatrix);
+ inverseMcModelViewProjectionMatrix.multiply(mcModelViewMatrix);
+ inverseMcModelViewProjectionMatrix.invert();
+ this.inverseMcMvmProjMatrix = inverseMcModelViewProjectionMatrix;
+
+
+ Mat4f dhProjectionMatrix = RenderUtil.createLodProjectionMatrix(mcProjectionMatrix);
+ Mat4f dhModelViewMatrix = RenderUtil.createLodModelViewMatrix(mcModelViewMatrix);
+
+ Mat4f inverseDhModelViewProjectionMatrix = new Mat4f(dhProjectionMatrix);
+ inverseDhModelViewProjectionMatrix.multiply(dhModelViewMatrix);
+ inverseDhModelViewProjectionMatrix.invert();
+ this.inverseDhMvmProjMatrix = inverseDhModelViewProjectionMatrix;
+ }
+ public void setLevelMaxHeight(int levelMaxHeight) { this.levelMaxHeight = levelMaxHeight; }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ int depthTextureId = BlazeLodRenderer.INSTANCE.getActiveDepthTextureId();
+ int colorTextureId = BlazeLodRenderer.INSTANCE.getActiveColorTextureId();
+
+ if (depthTextureId == -1
+ || colorTextureId == -1)
+ {
+ // the renderer is currently being re-built and/or inactive,
+ // we don't need to/can't render fading
+ return;
+ }
+
+
+
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
+ GLMC.disableScissorTest();
+ GLMC.disableDepthTest();
+ GLMC.disableBlend();
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(MC_RENDER.getDepthTextureId());
+ GL32.glUniform1i(this.uMcDepthTexture, 0);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE1);
+ GLMC.glBindTexture(depthTextureId);
+ GL32.glUniform1i(this.uDhDepthTexture, 1);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE2);
+ GLMC.glBindTexture(MC_RENDER.getColorTextureId());
+ GL32.glUniform1i(this.uCombinedMcDhColorTexture, 2);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE3);
+ GLMC.glBindTexture(colorTextureId);
+ GL32.glUniform1i(this.uDhColorTexture, 3);
+
+
+ ScreenQuad.INSTANCE.render();
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogApplyShader.java
new file mode 100644
index 000000000..58fca26d3
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogApplyShader.java
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fog;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * Draws the Fog texture onto DH's FrameBuffer.
+ *
+ * See Also:
+ * {@link FogRenderer} - Parent to this shader.
+ * {@link FogShader} - draws the Fog texture.
+ */
+public class FogApplyShader extends AbstractShaderRenderer
+{
+ public static FogApplyShader INSTANCE = new FogApplyShader();
+
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ public int fogTexture;
+
+ // uniforms
+ public int colorTextureUniform;
+ public int depthTextureUniform;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/fog/apply.frag",
+ "vPosition"
+ );
+
+ // uniform setup
+ this.colorTextureUniform = this.shader.getUniformLocation("uColorTexture");
+ this.depthTextureUniform = this.shader.getUniformLocation("uDepthTexture");
+
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(this.fogTexture);
+ GL32.glUniform1i(this.colorTextureUniform, 0);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE1);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveDepthTextureId());
+ GL32.glUniform1i(this.depthTextureUniform, 1);
+
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ GLMC.enableBlend();
+ GL32.glBlendEquation(GL32.GL_FUNC_ADD);
+ GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
+
+ // Depth testing must be disabled otherwise this application shader won't apply anything.
+ // setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually,
+ // it should be automatically restored after rendering is complete.
+ GLMC.disableDepthTest();
+
+
+ // apply the rendered Fog to DH's framebuffer
+ GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, FogShader.INSTANCE.frameBuffer);
+ GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, BlazeLodRenderer.INSTANCE.getActiveFramebufferId());
+
+ ScreenQuad.INSTANCE.render();
+
+ GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, 0);
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogRenderer.java
new file mode 100644
index 000000000..04079baf7
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogRenderer.java
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fog;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.GLState;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.opengl.GL43C;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Handles adding SSAO via {@link FogShader} and {@link FogApplyShader}.
+ *
+ * {@link FogShader} - draws the Fog to a texture.
+ * {@link FogApplyShader} - draws the Fog texture to DH's FrameBuffer.
+ */
+public class FogRenderer
+{
+ public static FogRenderer INSTANCE = new FogRenderer();
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ private boolean init = false;
+
+ private int width = -1;
+ private int height = -1;
+ private int fogFramebuffer = -1;
+
+ private int fogTexture = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ private FogRenderer() { }
+
+ public void init()
+ {
+ if (this.init) return;
+ this.init = true;
+
+ FogShader.INSTANCE.init();
+ FogApplyShader.INSTANCE.init();
+ }
+
+ private void createFramebuffer(int width, int height)
+ {
+ if (this.fogFramebuffer != -1)
+ {
+ GL32.glDeleteFramebuffers(this.fogFramebuffer);
+ this.fogFramebuffer = -1;
+ }
+
+ if (this.fogTexture != -1)
+ {
+ GLMC.glDeleteTextures(this.fogTexture);
+ this.fogTexture = -1;
+ }
+
+ this.fogFramebuffer = GL32.glGenFramebuffers();
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer);
+
+ this.fogTexture = GLMC.glGenTextures();
+ {
+ GLMC.glBindTexture(this.fogTexture);
+ GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0);
+
+ // disable mip-mapping since DH is just going to draw straight to the screen
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
+ }
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ public void render(Mat4f modelViewProjectionMatrix, float partialTicks)
+ {
+ // GLState needed in MC 1.16.5 probably due to MC not manually setting each GL state they need before the next rendering step
+ try (GLState state = new GLState())
+ {
+ this.init();
+
+ // resize the framebuffer if necessary
+ int width = MC_RENDER.getTargetFramebufferViewportWidth();
+ int height = MC_RENDER.getTargetFramebufferViewportHeight();
+ if (this.width != width || this.height != height)
+ {
+ this.width = width;
+ this.height = height;
+ this.createFramebuffer(width, height);
+ }
+
+ FogShader.INSTANCE.frameBuffer = this.fogFramebuffer;
+ FogShader.INSTANCE.setProjectionMatrix(modelViewProjectionMatrix);
+ FogShader.INSTANCE.render(partialTicks);
+
+ FogApplyShader.INSTANCE.fogTexture = this.fogTexture;
+ FogApplyShader.INSTANCE.render(partialTicks);
+ }
+ }
+
+ public void free()
+ {
+ FogShader.INSTANCE.free();
+ FogApplyShader.INSTANCE.free();
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogSettings.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogSettings.java
new file mode 100644
index 000000000..714eab500
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogSettings.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fog;
+
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
+
+import java.util.Objects;
+
+/**
+ * Contains all configurable options related to fog.
+ *
+ * @version 2022-4-13
+ */
+public class FogSettings
+{
+ /** a FogSetting object with 0 for every value */
+ public static final FogSettings EMPTY = new FogSettings(0, 0, 0, 0, 0, EDhApiFogFalloff.LINEAR);
+
+
+ public final double start;
+ public final double end;
+ public final double min;
+ public final double max;
+ public final double density;
+ public final EDhApiFogFalloff fogType;
+
+ public FogSettings(double start, double end, double min, double max, double density, EDhApiFogFalloff fogType)
+ {
+ this.start = start;
+ this.end = end;
+ this.min = min;
+ this.max = max;
+ this.density = density;
+ this.fogType = fogType;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ FogSettings that = (FogSettings) o;
+ return Double.compare(that.start, start) == 0 && Double.compare(that.end, end) == 0 && Double.compare(that.min, min) == 0 && Double.compare(that.max, max) == 0 && Double.compare(that.density, density) == 0 && fogType == that.fogType;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(start, end, min, max, density, fogType);
+ }
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogShader.java
new file mode 100644
index 000000000..0ac0b00ab
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/fog/FogShader.java
@@ -0,0 +1,287 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.fog;
+
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode;
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection;
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import com.seibel.distanthorizons.core.util.LodUtil;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import org.lwjgl.opengl.GL32;
+
+import java.awt.*;
+
+public class FogShader extends AbstractShaderRenderer
+{
+ public static final FogShader INSTANCE = new FogShader();
+
+ private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+
+
+
+ public int frameBuffer;
+
+ private Mat4f inverseMvmProjMatrix;
+
+
+
+ //==========//
+ // Uniforms //
+ //==========//
+
+ public int uDepthMap;
+ /** Inverted Model View Projection matrix */
+ public int uInvMvmProj;
+
+ // fog uniforms
+ public int uFogColor;
+ public int uFogScale;
+ public int uFogVerticalScale;
+ public int uFogDebugMode;
+ public int uFogFalloffType;
+
+ // far fog
+ public int uFarFogStart;
+ public int uFarFogLength;
+ public int uFarFogMin;
+ public int uFarFogRange;
+ public int uFarFogDensity;
+
+ // height fog
+ public int uHeightFogStart;
+ public int uHeightFogLength;
+ public int uHeightFogMin;
+ public int uHeightFogRange;
+ public int uHeightFogDensity;
+
+ public int uHeightFogEnabled;
+ public int uHeightFogFalloffType;
+ public int uHeightBasedOnCamera;
+ public int uHeightFogBaseHeight;
+ public int uHeightFogAppliesUp;
+ public int uHeightFogAppliesDown;
+ public int uUseSphericalFog;
+ public int uHeightFogMixingMode;
+ public int uCameraBlockYPos;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public FogShader() { }
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/fog/fog.frag",
+ "vPosition"
+ );
+
+ // all uniforms should be tryGet...
+ // because disabling fog can cause the GLSL to optimize out most (if not all) uniforms
+
+ this.uDepthMap = this.shader.getUniformLocation("uDepthMap");
+ this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj");
+
+ // Fog uniforms
+ this.uFogScale = this.shader.getUniformLocation("uFogScale");
+ this.uFogVerticalScale = this.shader.getUniformLocation("uFogVerticalScale");
+ this.uFogColor = this.shader.getUniformLocation("uFogColor");
+ this.uFogDebugMode = this.shader.getUniformLocation("uFogDebugMode");
+ this.uFogFalloffType = this.shader.getUniformLocation("uFogFalloffType");
+
+ // fog config
+ this.uFarFogStart = this.shader.getUniformLocation("uFarFogStart");
+ this.uFarFogLength = this.shader.getUniformLocation("uFarFogLength");
+ this.uFarFogMin = this.shader.getUniformLocation("uFarFogMin");
+ this.uFarFogRange = this.shader.getUniformLocation("uFarFogRange");
+ this.uFarFogDensity = this.shader.getUniformLocation("uFarFogDensity");
+
+ // height fog
+ this.uHeightFogStart = this.shader.getUniformLocation("uHeightFogStart");
+ this.uHeightFogLength = this.shader.getUniformLocation("uHeightFogLength");
+ this.uHeightFogMin = this.shader.getUniformLocation("uHeightFogMin");
+ this.uHeightFogRange = this.shader.getUniformLocation("uHeightFogRange");
+ this.uHeightFogDensity = this.shader.getUniformLocation("uHeightFogDensity");
+
+ this.uHeightFogEnabled = this.shader.getUniformLocation("uHeightFogEnabled");
+ this.uHeightFogFalloffType = this.shader.getUniformLocation("uHeightFogFalloffType");
+ this.uHeightBasedOnCamera = this.shader.getUniformLocation("uHeightBasedOnCamera");
+ this.uHeightFogBaseHeight = this.shader.getUniformLocation("uHeightFogBaseHeight");
+ this.uHeightFogAppliesUp = this.shader.getUniformLocation("uHeightFogAppliesUp");
+ this.uHeightFogAppliesDown = this.shader.getUniformLocation("uHeightFogAppliesDown");
+ this.uUseSphericalFog = this.shader.getUniformLocation("uUseSphericalFog");
+ this.uHeightFogMixingMode = this.shader.getUniformLocation("uHeightFogMixingMode");
+ this.uCameraBlockYPos = this.shader.getUniformLocation("uCameraBlockYPos");
+
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH;
+
+
+
+ if (this.inverseMvmProjMatrix != null)
+ {
+ this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix);
+ }
+
+
+ // Fog uniforms
+ this.shader.setUniform(this.uFogColor, this.getFogColor(partialTicks));
+ this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance);
+ this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight());
+ // only used for debugging
+ this.shader.setUniform(this.uFogDebugMode, 0); // 1 = render everything with fog color // 7 = use debug rendering
+ this.shader.setUniform(this.uFogFalloffType, Config.Client.Advanced.Graphics.Fog.farFogFalloff.get().value);
+
+
+ // fog config
+ float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get();
+ float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get();
+ float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get();
+ float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get();
+ float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get();
+
+ // override fog if underwater
+ if (MC_RENDER.isFogStateSpecial())
+ {
+ // hide everything behind fog
+ farFogStart = 0.0f;
+ farFogEnd = 0.0f;
+ }
+
+ this.shader.setUniform(this.uFarFogStart, farFogStart);
+ this.shader.setUniform(this.uFarFogLength, farFogEnd - farFogStart);
+ this.shader.setUniform(this.uFarFogMin, farFogMin);
+ this.shader.setUniform(this.uFarFogRange, farFogMax - farFogMin);
+ this.shader.setUniform(this.uFarFogDensity, farFogDensity);
+
+
+ // height config
+ EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get();
+ boolean heightFogEnabled = heightFogMixingMode != EDhApiHeightFogMixMode.SPHERICAL && heightFogMixingMode != EDhApiHeightFogMixMode.CYLINDRICAL;
+ boolean useSphericalFog = heightFogMixingMode == EDhApiHeightFogMixMode.SPHERICAL;
+ EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get();
+
+ float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get();
+ float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get();
+ float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get();
+ float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get();
+ float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get();
+
+ this.shader.setUniform(this.uHeightFogStart, heightFogStart);
+ this.shader.setUniform(this.uHeightFogLength, heightFogEnd - heightFogStart);
+ this.shader.setUniform(this.uHeightFogMin, heightFogMin);
+ this.shader.setUniform(this.uHeightFogRange, heightFogMax - heightFogMin);
+ this.shader.setUniform(this.uHeightFogDensity, heightFogDensity);
+
+
+ this.shader.setUniform(this.uHeightFogEnabled, heightFogEnabled);
+ this.shader.setUniform(this.uHeightFogFalloffType, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get().value);
+ this.shader.setUniform(this.uHeightFogBaseHeight, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get());
+ this.shader.setUniform(this.uHeightBasedOnCamera, heightFogCameraDirection.basedOnCamera);
+ this.shader.setUniform(this.uHeightFogAppliesUp, heightFogCameraDirection.fogAppliesUp);
+ this.shader.setUniform(this.uHeightFogAppliesDown, heightFogCameraDirection.fogAppliesDown);
+ this.shader.setUniform(this.uUseSphericalFog, useSphericalFog);
+ this.shader.setUniform(this.uHeightFogMixingMode, heightFogMixingMode.value);
+ this.shader.setUniform(this.uCameraBlockYPos, (float)MC_RENDER.getCameraExactPosition().y);
+
+ }
+ private Color getFogColor(float partialTicks)
+ {
+ Color fogColor;
+
+ if (Config.Client.Advanced.Graphics.Fog.colorMode.get() == EDhApiFogColorMode.USE_SKY_COLOR)
+ {
+ fogColor = MC_RENDER.getSkyColor();
+ }
+ else
+ {
+ fogColor = MC_RENDER.getFogColor(partialTicks);
+ }
+
+ return fogColor;
+ }
+
+ public void setProjectionMatrix(Mat4f modelViewProjectionMatrix)
+ {
+ this.inverseMvmProjMatrix = new Mat4f(modelViewProjectionMatrix);
+ this.inverseMvmProjMatrix.invert();
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
+ GLMC.disableScissorTest();
+ GLMC.disableDepthTest();
+ GLMC.disableBlend();
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveDepthTextureId());
+ GL32.glUniform1i(this.uDepthMap, 0);
+
+ // this is necessary for MC 1.16 (IE Legacy OpenGL)
+ // otherwise the framebuffer isn't cleared correctly and the fog smears across the screen
+ if (MC_RENDER.runningLegacyOpenGL())
+ {
+ // in another part of the DH code we set the fog color to opaque, here it needs to be transparent
+ float[] clearColorValues = new float[4];
+ GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues);
+ GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 0.0f);
+
+ GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT);
+ }
+
+
+ ScreenQuad.INSTANCE.render();
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAOApplyShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAOApplyShader.java
new file mode 100644
index 000000000..0da10356a
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAOApplyShader.java
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ssao;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import com.seibel.distanthorizons.core.util.RenderUtil;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * Draws the SSAO texture onto DH's FrameBuffer.
+ *
+ * See Also:
+ * {@link SSAORenderer} - Parent to this shader.
+ * {@link SSAOShader} - draws the SSAO texture.
+ */
+public class SSAOApplyShader extends AbstractShaderRenderer
+{
+ public static SSAOApplyShader INSTANCE = new SSAOApplyShader();
+
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ public int ssaoTexture;
+
+ // uniforms
+ public int gSSAOMapUniform;
+ public int gDepthMapUniform;
+ public int gViewSizeUniform;
+ public int gBlurRadiusUniform;
+ public int gNearUniform;
+ public int gFarUniform;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/ssao/apply.frag",
+ "vPosition"
+ );
+
+ // uniform setup
+ this.gSSAOMapUniform = this.shader.getUniformLocation("gSSAOMap");
+ this.gDepthMapUniform = this.shader.getUniformLocation("gDepthMap");
+ this.gViewSizeUniform = this.shader.tryGetUniformLocation("gViewSize");
+ this.gBlurRadiusUniform = this.shader.tryGetUniformLocation("gBlurRadius");
+ this.gNearUniform = this.shader.tryGetUniformLocation("gNear");
+ this.gFarUniform = this.shader.tryGetUniformLocation("gFar");
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveDepthTextureId());
+ GL32.glUniform1i(this.gDepthMapUniform, 0);
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE1);
+ GLMC.glBindTexture(this.ssaoTexture);
+ GL32.glUniform1i(this.gSSAOMapUniform, 1);
+
+ GL32.glUniform1i(this.gBlurRadiusUniform, 2);
+
+ if (this.gViewSizeUniform >= 0)
+ {
+ GL32.glUniform2f(this.gViewSizeUniform,
+ MC_RENDER.getTargetFramebufferViewportWidth(),
+ MC_RENDER.getTargetFramebufferViewportHeight());
+ }
+
+ if (this.gNearUniform >= 0)
+ {
+ GL32.glUniform1f(this.gNearUniform,
+ RenderUtil.getNearClipPlaneInBlocks());
+ }
+
+ if (this.gFarUniform >= 0)
+ {
+ float farClipPlane = RenderUtil.getFarClipPlaneDistanceInBlocks();
+ GL32.glUniform1f(this.gFarUniform, farClipPlane);
+ }
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ GLMC.enableBlend();
+ GL32.glBlendEquation(GL32.GL_FUNC_ADD);
+ GLMC.glBlendFuncSeparate(GL32.GL_ZERO, GL32.GL_SRC_ALPHA, GL32.GL_ZERO, GL32.GL_ONE);
+
+ // Depth testing must be disabled otherwise this application shader won't apply anything.
+ // setting this isn't necessary in vanilla, but some mods may change this, requiring it to be set manually,
+ // it should be automatically restored after rendering is complete.
+ GLMC.disableDepthTest();
+
+ // apply the rendered SSAO to the LODs
+ GLMC.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, SSAOShader.INSTANCE.frameBuffer);
+ GLMC.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, BlazeLodRenderer.INSTANCE.getActiveFramebufferId());
+
+
+ ScreenQuad.INSTANCE.render();
+ }
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAORenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAORenderer.java
new file mode 100644
index 000000000..88d3172c7
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAORenderer.java
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ssao;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.GLState;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.opengl.GL43C;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Handles adding SSAO via {@link SSAOShader} and {@link SSAOApplyShader}.
+ *
+ * {@link SSAOShader} - draws the SSAO to a texture.
+ * {@link SSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer.
+ */
+public class SSAORenderer
+{
+ public static SSAORenderer INSTANCE = new SSAORenderer();
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ private boolean init = false;
+
+ private int width = -1;
+ private int height = -1;
+ private int ssaoFramebuffer = -1;
+
+ private int ssaoTexture = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ private SSAORenderer() { }
+
+ public void init()
+ {
+ if (this.init) return;
+ this.init = true;
+
+ SSAOShader.INSTANCE.init();
+ SSAOApplyShader.INSTANCE.init();
+ }
+
+ private void createFramebuffer(int width, int height)
+ {
+ if (this.ssaoFramebuffer != -1)
+ {
+ GL32.glDeleteFramebuffers(this.ssaoFramebuffer);
+ this.ssaoFramebuffer = -1;
+ }
+
+ if (this.ssaoTexture != -1)
+ {
+ GLMC.glDeleteTextures(this.ssaoTexture);
+ this.ssaoTexture = -1;
+ }
+
+ this.ssaoFramebuffer = GL32.glGenFramebuffers();
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer);
+
+ this.ssaoTexture = GLMC.glGenTextures();
+ {
+ GLMC.glBindTexture(this.ssaoTexture);
+ GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_R16F, width, height, 0, GL32.GL_RED, GL32.GL_HALF_FLOAT, (ByteBuffer) null);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
+
+ // disable mip-mapping since DH is just going to draw straight to the screen
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
+ GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
+ }
+
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.ssaoTexture, 0);
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ public void render(Mat4f projectionMatrix, float partialTicks)
+ {
+ try(GLState state = new GLState())
+ {
+ this.init();
+
+ // resize the framebuffer if necessary
+ int width = MC_RENDER.getTargetFramebufferViewportWidth();
+ int height = MC_RENDER.getTargetFramebufferViewportHeight();
+ if (this.width != width || this.height != height)
+ {
+ this.width = width;
+ this.height = height;
+ this.createFramebuffer(width, height);
+ }
+
+ SSAOShader.INSTANCE.frameBuffer = this.ssaoFramebuffer;
+ SSAOShader.INSTANCE.setProjectionMatrix(projectionMatrix);
+ SSAOShader.INSTANCE.render(partialTicks);
+
+ SSAOApplyShader.INSTANCE.ssaoTexture = this.ssaoTexture;
+ SSAOApplyShader.INSTANCE.render(partialTicks);
+ }
+ }
+
+ public void free()
+ {
+ SSAOShader.INSTANCE.free();
+ SSAOApplyShader.INSTANCE.free();
+ }
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAOShader.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAOShader.java
new file mode 100644
index 000000000..36e285d24
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/postProcessing/ssao/SSAOShader.java
@@ -0,0 +1,142 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ssao;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.postProcessing.ScreenQuad;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
+import com.seibel.distanthorizons.common.render.nativeGl.util.AbstractShaderRenderer;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * Draws the SSAO to a texture.
+ *
+ * See Also:
+ * {@link SSAORenderer} - Parent to this shader.
+ * {@link SSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer.
+ */
+public class SSAOShader extends AbstractShaderRenderer
+{
+ public static SSAOShader INSTANCE = new SSAOShader();
+
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+
+ public int frameBuffer;
+
+ private Mat4f projection;
+ private Mat4f invertedProjection;
+
+
+ // uniforms
+ public int uProj;
+ public int uInvProj;
+ public int uSampleCount;
+ public int uRadius;
+ public int uStrength;
+ public int uMinLight;
+ public int uBias;
+ public int uDepthMap;
+ public int uFadeDistanceInBlocks;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/quadApply.vert",
+ "shaders/ssao/ao.frag",
+ "vPosition"
+ );
+
+ // uniform setup
+ this.uProj = this.shader.getUniformLocation("uProj");
+ this.uInvProj = this.shader.getUniformLocation("uInvProj");
+ this.uSampleCount = this.shader.getUniformLocation("uSampleCount");
+ this.uRadius = this.shader.getUniformLocation("uRadius");
+ this.uStrength = this.shader.getUniformLocation("uStrength");
+ this.uMinLight = this.shader.getUniformLocation("uMinLight");
+ this.uBias = this.shader.getUniformLocation("uBias");
+ this.uDepthMap = this.shader.getUniformLocation("uDepthMap");
+ this.uFadeDistanceInBlocks = this.shader.getUniformLocation("uFadeDistanceInBlocks");
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ public void setProjectionMatrix(Mat4f projectionMatrix)
+ {
+ this.projection = projectionMatrix;
+
+ this.invertedProjection = new Mat4f(projectionMatrix);
+ this.invertedProjection.invert();
+ }
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ this.shader.setUniform(this.uProj, this.projection);
+
+ this.shader.setUniform(this.uInvProj, this.invertedProjection);
+
+ this.shader.setUniform(this.uSampleCount, 6);
+ this.shader.setUniform(this.uRadius, 4.0f);
+ this.shader.setUniform(this.uStrength, 0.2f);
+ this.shader.setUniform(this.uMinLight, 0.25f);
+ this.shader.setUniform(this.uBias, 0.02f);
+ this.shader.setUniform(this.uFadeDistanceInBlocks, 1_600.0f);
+
+ GL32.glUniform1i(this.uDepthMap, 0);
+
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
+ GLMC.disableScissorTest();
+ GLMC.disableDepthTest();
+ GLMC.disableBlend();
+
+ GLMC.glActiveTexture(GL32.GL_TEXTURE0);
+ GLMC.glBindTexture(BlazeLodRenderer.INSTANCE.getActiveDepthTextureId());
+
+ ScreenQuad.INSTANCE.render();
+ }
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/test/GlTestRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/test/GlTestRenderer.java
new file mode 100644
index 000000000..8daab7b83
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/test/GlTestRenderer.java
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.test;
+
+import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.logging.DhLogger;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.buffer.GLVertexBuffer;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute.AbstractVertexAttribute;
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.vertexAttribute.VertexPointer;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+
+import org.lwjgl.opengl.GL32;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Renders a UV colored quad
+ * to the center of the screen to confirm DH's
+ * apply shader is running correctly
+ */
+public class GlTestRenderer
+{
+ public static final DhLogger LOGGER = new DhLoggerBuilder().build();
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+ private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
+
+ public static final GlTestRenderer INSTANCE = new GlTestRenderer();
+
+ // Render a square with uv color
+ private static final float[] VERTICES =
+ {
+ // PosX,Y, ColorR,G,B,A
+ -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
+ 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
+ 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f,
+ };
+
+
+
+ ShaderProgram basicShader;
+ GLVertexBuffer vbo;
+ AbstractVertexAttribute va;
+ boolean init = false;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+ //region
+
+ private GlTestRenderer() { }
+
+ public void init()
+ {
+ if (this.init)
+ {
+ return;
+ }
+
+ LOGGER.info("init");
+ this.init = true;
+ this.va = AbstractVertexAttribute.create();
+ this.va.bind();
+ // Pos
+ this.va.setVertexAttribute(0, 0, VertexPointer.addVec2Pointer(false));
+ // Color
+ this.va.setVertexAttribute(0, 1, VertexPointer.addVec4Pointer(false));
+ this.va.completeAndCheck(Float.BYTES * 6);
+ this.basicShader = new ShaderProgram(
+ "shaders/test/vert.vert",
+ "shaders/test/frag.frag",
+ new String[]{"vPosition", "color"});
+
+ this.createBuffer();
+ }
+
+ private void createBuffer()
+ {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(VERTICES.length * Float.BYTES);
+ // Fill buffer with vertices.
+ buffer.order(ByteOrder.nativeOrder());
+ buffer.asFloatBuffer().put(VERTICES);
+ buffer.rewind();
+
+ this.vbo = new GLVertexBuffer(false);
+ this.vbo.bind();
+ this.vbo.uploadBuffer(buffer, 4, EDhApiGpuUploadMethod.DATA, VERTICES.length * Float.BYTES);
+ }
+
+ //endregion
+
+
+
+ //========//
+ // render //
+ //========//
+ //region
+
+ public void render()
+ {
+ this.init();
+
+ this.basicShader.bind();
+ this.va.bind();
+
+ this.vbo.bind();
+ this.va.bindBufferToAllBindingPoints(this.vbo.getId());
+
+ // Render the square
+ GL32.glDrawArrays(GL32.GL_TRIANGLE_FAN, 0, 4);
+ }
+
+ //endregion
+
+
+
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/AbstractShaderRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/AbstractShaderRenderer.java
new file mode 100644
index 000000000..92e4a1e68
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/AbstractShaderRenderer.java
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.util;
+
+import com.seibel.distanthorizons.common.render.nativeGl.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import org.lwjgl.opengl.GL32;
+
+public abstract class AbstractShaderRenderer
+{
+ protected static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+
+
+ protected ShaderProgram shader;
+
+ protected boolean init = false;
+
+
+ protected AbstractShaderRenderer() {}
+
+ public void init()
+ {
+ if (this.init) return;
+ this.init = true;
+
+ this.onInit();
+ }
+
+ public void render(float partialTicks)
+ {
+ this.init();
+
+ this.shader.bind();
+
+ this.onApplyUniforms(partialTicks);
+
+ int width = MC_RENDER.getTargetFramebufferViewportWidth();
+ int height = MC_RENDER.getTargetFramebufferViewportHeight();
+ GL32.glViewport(0, 0, width, height);
+
+ this.onRender();
+
+ this.shader.unbind();
+ }
+
+ public void free()
+ {
+ if (this.shader != null)
+ {
+ this.shader.free();
+ }
+ }
+
+ protected void onInit() {}
+
+ protected void onApplyUniforms(float partialTicks) {}
+
+ protected void onRender() {}
+}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/LodVertexFormat.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/LodVertexFormat.java
new file mode 100644
index 000000000..509c9e872
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/LodVertexFormat.java
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.util.vertexFormat;
+
+import java.util.stream.Collectors;
+
+import com.google.common.collect.ImmutableList;
+
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/**
+ * This is used to represent a single vertex
+ * stored in GPU memory,
+ *
+ * A (almost) exact copy of Minecraft's
+ * VertexFormat class, several methods
+ * were commented out since we didn't need them.
+ *
+ * @author James Seibel
+ * @version 12-9-2021
+ */
+public class LodVertexFormat
+{
+ /** the format of data stored in the GPU buffers */
+ public static final LodVertexFormat DH_VERTEX_FORMAT = VertexFormats.POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT_MATERIAL_ID_NORMAL_INDEX;
+
+
+ private final ImmutableList elements;
+ private final IntList offsets = new IntArrayList();
+ private final int byteSize;
+
+ public LodVertexFormat(ImmutableList elementList)
+ {
+ this.elements = elementList;
+ int i = 0;
+
+ for (LodVertexFormatElement LodVertexFormatElement : elementList)
+ {
+ this.offsets.add(i);
+ i += LodVertexFormatElement.getByteSize();
+ }
+
+ this.byteSize = i;
+ }
+
+ public int getByteSize()
+ {
+ return this.byteSize;
+ }
+
+ public ImmutableList getElements()
+ {
+ return this.elements;
+ }
+
+
+ // Forge added method
+ public int getOffset(int index)
+ {
+ return offsets.getInt(index);
+ }
+
+
+
+ @Override
+ public String toString() { return "format: " + this.elements.size() + " elements: " + this.elements.stream().map(Object::toString).collect(Collectors.joining(" ")); }
+
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj != null && this.getClass() == obj.getClass())
+ {
+ LodVertexFormat vertexFormat = (LodVertexFormat) obj;
+ return this.byteSize == vertexFormat.byteSize && this.elements.equals(vertexFormat.elements);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() { return this.elements.hashCode(); }
+
+
+
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/LodVertexFormatElement.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/LodVertexFormatElement.java
new file mode 100644
index 000000000..cdc7dc41a
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/LodVertexFormatElement.java
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.util.vertexFormat;
+
+import org.lwjgl.opengl.GL32;
+
+/**
+ * This object is used to build LodVertexFormats.
+ *
+ * A (almost) exact copy of Minecraft's
+ * VertexFormatElement class.
+ * A number of things were removed from the original
+ * object since we didn't need them, specifically "usage".
+ *
+ * @author James Seibel
+ * @version 11-13-2021
+ */
+public class LodVertexFormatElement
+{
+ private final LodVertexFormatElement.DataType dataType;
+ /** James isn't sure what index is for */
+ private final int index;
+ private final int count;
+ private final int byteSize;
+ private final boolean isPadding;
+
+ public LodVertexFormatElement(int newIndex, LodVertexFormatElement.DataType newType, int newCount, boolean isPadding)
+ {
+ this.dataType = newType;
+ this.index = newIndex;
+ this.count = newCount;
+ this.byteSize = newType.getSize() * this.count;
+ this.isPadding = isPadding;
+ }
+
+ public final boolean getIsPadding()
+ {
+ return isPadding;
+ }
+
+ public final LodVertexFormatElement.DataType getType()
+ {
+ return this.dataType;
+ }
+
+ public final int getIndex()
+ {
+ return this.index;
+ }
+
+ public final int getByteSize()
+ {
+ return this.byteSize;
+ }
+
+ // added by Forge
+ public int getElementCount()
+ {
+ return count;
+ }
+
+
+
+ public enum DataType
+ {
+ FLOAT(4, "Float", GL32.GL_FLOAT),
+ UBYTE(1, "Unsigned Byte", GL32.GL_UNSIGNED_BYTE),
+ BYTE(1, "Byte", GL32.GL_BYTE),
+ USHORT(2, "Unsigned Short", GL32.GL_UNSIGNED_SHORT),
+ SHORT(2, "Short", GL32.GL_SHORT),
+ UINT(4, "Unsigned Int", GL32.GL_UNSIGNED_INT),
+ INT(4, "Int", GL32.GL_INT);
+
+ private final int size;
+ private final String name;
+ private final int glType;
+
+ DataType(int sizeInBytes, String newName, int openGlDataType)
+ {
+ this.size = sizeInBytes;
+ this.name = newName;
+ this.glType = openGlDataType;
+ }
+
+ public int getSize()
+ {
+ return this.size;
+ }
+
+ public String getName()
+ {
+ return this.name;
+ }
+
+ public int getGlType()
+ {
+ return this.glType;
+ }
+ }
+
+
+
+
+ @Override
+ public int hashCode()
+ {
+ int i = this.dataType.hashCode();
+ i = 31 * i + this.index;
+ return 31 * i + this.count;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.count + "," + this.dataType.getName();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj != null && this.getClass() == obj.getClass())
+ {
+ LodVertexFormatElement LodVertexFormatElement = (LodVertexFormatElement) obj;
+ if (this.count != LodVertexFormatElement.count)
+ {
+ return false;
+ }
+ else if (this.index != LodVertexFormatElement.index)
+ {
+ return false;
+ }
+ else if (this.dataType != LodVertexFormatElement.dataType)
+ {
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/VertexFormats.java b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/VertexFormats.java
new file mode 100644
index 000000000..dc4e04e64
--- /dev/null
+++ b/common/src/main/java/com/seibel/distanthorizons/common/render/nativeGl/util/vertexFormat/VertexFormats.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL 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 Lesser 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.common.render.nativeGl.util.vertexFormat;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A (almost) exact copy of MC's
+ * DefaultVertexFormats class.
+ */
+public class VertexFormats
+{
+ public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(3, LodVertexFormatElement.DataType.USHORT, 3, false);
+ public static final LodVertexFormatElement ELEMENT_COLOR = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 4, false);
+ public static final LodVertexFormatElement ELEMENT_BYTE_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1, true);
+
+ public static final LodVertexFormatElement ELEMENT_LIGHT = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 1, false);
+ public static final LodVertexFormatElement ELEMENT_IRIS_MATERIAL_INDEX = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1, false);
+ public static final LodVertexFormatElement ELEMENT_IRIS_NORMAL_INDEX = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1, false);
+
+
+ public static final LodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT_MATERIAL_ID_NORMAL_INDEX = new LodVertexFormat(ImmutableList.builder()
+ .add(ELEMENT_POSITION)
+ .add(ELEMENT_BYTE_PADDING)
+ .add(ELEMENT_LIGHT)
+ .add(ELEMENT_COLOR)
+ .add(ELEMENT_IRIS_MATERIAL_INDEX)
+ .add(ELEMENT_IRIS_NORMAL_INDEX)
+ .add(ELEMENT_BYTE_PADDING)
+ .add(ELEMENT_BYTE_PADDING) // padding is to make sure the format is a multiple of 4
+ .build());
+
+}