Start moving OpenGL rendering to common 1

This commit is contained in:
James Seibel
2026-03-09 16:22:55 -05:00
parent 8240101a46
commit 27b66940be
55 changed files with 0 additions and 10755 deletions
@@ -1,69 +0,0 @@
package com.seibel.distanthorizons.core.render;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.joml.FrustumIntersection;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector3f;
public class DhFrustumBounds implements IDhApiCullingFrustum
{
private final FrustumIntersection frustum;
private final Vector3f boundsMin = new Vector3f();
private final Vector3f boundsMax = new Vector3f();
public float worldMinY;
public float worldMaxY;
//=============//
// constructor //
//=============//
public DhFrustumBounds()
{
this.frustum = new FrustumIntersection();
}
//=========//
// methods //
//=========//
@Override
public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection)
{
this.worldMinY = worldMinBlockY;
this.worldMaxY = worldMaxBlockY;
Matrix4f worldViewProjection = new Matrix4f(Mat4f.createJomlMatrix(dhWorldViewProjection));
this.frustum.set(worldViewProjection);
Matrix4fc matWorldViewProjectionInv = new Matrix4f(worldViewProjection).invert();
matWorldViewProjectionInv.frustumAabb(this.boundsMin, this.boundsMax);
}
@Override
public boolean intersects(int lodBlockPosMinX, int lodBlockPosMinZ, int lodBlockWidth, int lodDetailLevel)
{
Vector3f lodMin = new Vector3f(lodBlockPosMinX, this.worldMinY, lodBlockPosMinZ);
Vector3f lodMax = new Vector3f(lodBlockPosMinX + lodBlockWidth, this.worldMaxY, lodBlockPosMinZ + lodBlockWidth);
return this.frustum.testAab(lodMin, lodMax);
}
//=====================//
// overridable methods //
//=====================//
@Override
public int getPriority() { return IOverrideInjector.CORE_PRIORITY; }
}
@@ -1,42 +0,0 @@
package com.seibel.distanthorizons.core.render;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShadowCullingFrustum;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
import com.seibel.distanthorizons.core.util.math.Mat4f;
/**
* Dummy {@link IDhApiCullingFrustum} that allows everything through. <br>
* Useful when a frustum is required, but culling shouldn't be done.
*/
public class NeverCullFrustum implements IDhApiCullingFrustum, IDhApiShadowCullingFrustum
{
//=============//
// constructor //
//=============//
public NeverCullFrustum() { }
//=========//
// methods //
//=========//
@Override
public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection) { /* update isn't needed */ }
@Override
public boolean intersects(int lodBlockPosMinX, int lodBlockPosMinZ, int lodBlockWidth, int lodDetailLevel) { return true; }
//=====================//
// overridable methods //
//=====================//
@Override
public int getPriority() { return IOverrideInjector.CORE_PRIORITY; }
}
@@ -1,72 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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);
}
}
@@ -1,261 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject;
import static org.lwjgl.opengl.GL46.*;
// Turns GL int enums back to readable strings
public class GLEnums
{
public static String getString(int glEnum)
{
// blend stuff
switch (glEnum)
{
case GL_ZERO:
return "GL_ZERO";
case GL_ONE:
return "GL_ONE";
case GL_SRC_COLOR:
return "GL_SRC_COLOR";
case GL_ONE_MINUS_SRC_COLOR:
return "GL_ONE_MINUS_SRC_COLOR";
case GL_DST_COLOR:
return "GL_DST_COLOR";
case GL_ONE_MINUS_DST_COLOR:
return "GL_ONE_MINUS_DST_COLOR";
case GL_SRC_ALPHA:
return "GL_SRC_ALPHA";
case GL_ONE_MINUS_SRC_ALPHA:
return "GL_ONE_MINUS_SRC_ALPHA";
case GL_DST_ALPHA:
return "GL_DST_ALPHA";
case GL_ONE_MINUS_DST_ALPHA:
return "GL_ONE_MINUS_DST_ALPHA";
case GL_CONSTANT_COLOR:
return "GL_CONSTANT_COLOR";
case GL_ONE_MINUS_CONSTANT_COLOR:
return "GL_ONE_MINUS_CONSTANT_COLOR";
case GL_CONSTANT_ALPHA:
return "GL_CONSTANT_ALPHA";
case GL_ONE_MINUS_CONSTANT_ALPHA:
return "GL_ONE_MINUS_CONSTANT_ALPHA";
default:
}
// shader stuff
switch (glEnum)
{
case GL_VERTEX_SHADER:
return "GL_VERTEX_SHADER";
case GL_GEOMETRY_SHADER:
return "GL_GEOMETRY_SHADER";
case GL_FRAGMENT_SHADER:
return "GL_FRAGMENT_SHADER";
default:
}
// stencil stuff
switch (glEnum)
{
case GL_KEEP:
return "GL_KEEP";
case GL_ZERO:
return "GL_ZERO";
case GL_REPLACE:
return "GL_REPLACE";
case GL_INCR:
return "GL_INCR";
case GL_DECR:
return "GL_DECR";
case GL_INVERT:
return "GL_INVERT";
case GL_INCR_WRAP:
return "GL_INCR_WRAP";
case GL_DECR_WRAP:
return "GL_DECR_WRAP";
default:
}
// depth stuff
switch (glEnum)
{
case GL_NEVER:
return "GL_NEVER";
case GL_LESS:
return "GL_LESS";
case GL_EQUAL:
return "GL_EQUAL";
case GL_LEQUAL:
return "GL_LEQUAL";
case GL_GREATER:
return "GL_GREATER";
case GL_NOTEQUAL:
return "GL_NOTEQUAL";
case GL_GEQUAL:
return "GL_GEQUAL";
case GL_ALWAYS:
return "GL_ALWAYS";
default:
}
// Texture binding points
switch (glEnum)
{
case GL_TEXTURE0:
return "GL_TEXTURE0";
case GL_TEXTURE1:
return "GL_TEXTURE1";
case GL_TEXTURE2:
return "GL_TEXTURE2";
case GL_TEXTURE3:
return "GL_TEXTURE3";
case GL_TEXTURE4:
return "GL_TEXTURE4";
case GL_TEXTURE5:
return "GL_TEXTURE5";
case GL_TEXTURE6:
return "GL_TEXTURE6";
case GL_TEXTURE7:
return "GL_TEXTURE7";
case GL_TEXTURE8:
return "GL_TEXTURE8";
case GL_TEXTURE9:
return "GL_TEXTURE9";
case GL_TEXTURE10:
return "GL_TEXTURE10";
case GL_TEXTURE11:
return "GL_TEXTURE11";
case GL_TEXTURE12:
return "GL_TEXTURE12";
case GL_TEXTURE13:
return "GL_TEXTURE13";
case GL_TEXTURE14:
return "GL_TEXTURE14";
case GL_TEXTURE15:
return "GL_TEXTURE15";
case GL_TEXTURE16:
return "GL_TEXTURE16";
case GL_TEXTURE17:
return "GL_TEXTURE17";
case GL_TEXTURE18:
return "GL_TEXTURE18";
case GL_TEXTURE19:
return "GL_TEXTURE19";
case GL_TEXTURE20:
return "GL_TEXTURE20";
case GL_TEXTURE21:
return "GL_TEXTURE21";
case GL_TEXTURE22:
return "GL_TEXTURE22";
case GL_TEXTURE23:
return "GL_TEXTURE23";
case GL_TEXTURE24:
return "GL_TEXTURE24";
case GL_TEXTURE25:
return "GL_TEXTURE25";
case GL_TEXTURE26:
return "GL_TEXTURE26";
case GL_TEXTURE27:
return "GL_TEXTURE27";
case GL_TEXTURE28:
return "GL_TEXTURE28";
case GL_TEXTURE29:
return "GL_TEXTURE29";
case GL_TEXTURE30:
return "GL_TEXTURE30";
case GL_TEXTURE31:
return "GL_TEXTURE31";
default:
}
// Polygon modes
switch (glEnum)
{
case GL_POINT:
return "GL_POINT";
case GL_LINE:
return "GL_LINE";
case GL_FILL:
return "GL_FILL";
default:
}
// Culling modes
switch (glEnum)
{
case GL_FRONT:
return "GL_FRONT";
case GL_BACK:
return "GL_BACK";
case GL_FRONT_AND_BACK:
return "GL_FRONT_AND_BACK";
default:
}
// Types
switch (glEnum)
{
case GL_BYTE:
return "GL_BYTE";
case GL_UNSIGNED_BYTE:
return "GL_UNSIGNED_BYTE";
case GL_SHORT:
return "GL_SHORT";
case GL_UNSIGNED_SHORT:
return "GL_UNSIGNED_SHORT";
case GL_INT:
return "GL_INT";
case GL_UNSIGNED_INT:
return "GL_UNSIGNED_INT";
case GL_FLOAT:
return "GL_FLOAT";
case GL_DOUBLE:
return "GL_DOUBLE";
default:
}
return "GL_UNKNOWN(" + glEnum + ")";
}
public static int getTypeSize(int glTypeEnum)
{
switch (glTypeEnum)
{
case GL_BYTE:
case GL_UNSIGNED_BYTE:
return 1;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
return 2;
case GL_INT:
case GL_UNSIGNED_INT:
return 4;
case GL_FLOAT:
return 4;
case GL_DOUBLE:
return 8;
default:
throw new IllegalArgumentException("Unknown type enum: " + getString(glTypeEnum));
}
}
}
@@ -1,453 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject;
import com.seibel.distanthorizons.api.enums.config.EDhApiGLErrorHandlingMode;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.objects.GLMessages.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.opengl.GLUtil;
import java.io.PrintStream;
import java.util.Collections;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* A singleton that holds references to different openGL contexts
* and GPU capabilities.
*/
public class GLProxy
{
//private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
//private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
public static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
.build();
public static final Set<String> LOGGED_GL_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private static final ConcurrentLinkedQueue<Runnable> RENDER_THREAD_RUNNABLE_QUEUE = new ConcurrentLinkedQueue<>();
private static final Timer TIMER = TimerUtil.CreateTimer("Cleanup timer");
private static final long MS_BETWEEN_CLEANUP_TICKS = 1_000L;
private static final long MS_BEFORE_RUN_CLEANUP_TIMER = 1_000L;
private static GLProxy instance = null;
/** Minecraft's GL capabilities */
public final GLCapabilities glCapabilities;
public boolean namedObjectSupported = false; // ~OpenGL 4.5 (UNUSED CURRENTLY)
public boolean bufferStorageSupported = false; // ~OpenGL 4.4
public boolean vertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3
public boolean instancedArraysSupported = false;
public boolean vertexAttribDivisorSupported = false; // OpenGL 3.3 or newer
private final EDhApiGpuUploadMethod preferredUploadMethod;
public final GLMessageBuilder vanillaDebugMessageBuilder =
new GLMessageBuilder(
(type) ->
{
if (type == EGLMessageType.POP_GROUP)
return false;
else if (type == EGLMessageType.PUSH_GROUP)
return false;
else if (type == EGLMessageType.MARKER)
return false;
else
return true;
},
(severity) ->
{
// notifications can generally be ignored (if they are logged at all)
if (severity == EGLMessageSeverity.NOTIFICATION)
return false;
else
return true;
},
null
);
private long msSinceGlTasksRun = System.currentTimeMillis();
//=============//
// constructor //
//=============//
//region
private GLProxy() throws IllegalStateException
{
// this must be created on minecraft's render context to work correctly
if (GLFW.glfwGetCurrentContext() == 0L)
{
throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!");
}
LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error.");
LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "].");
//============================//
// get Minecraft's GL context //
//============================//
// get Minecraft's capabilities
this.glCapabilities = GL.getCapabilities();
// crash the game if the GPU doesn't support OpenGL 3.2
if (!this.glCapabilities.OpenGL32)
{
String supportedVersionInfo = this.getFailedVersionInfo(this.glCapabilities);
// See full requirement at above.
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName()
+ " and discovered this GPU doesn't meet the OpenGL requirements. Sorry I couldn't tell you sooner :(\n" +
"Additional info:\n" + supportedVersionInfo;
IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
MC.crashMinecraft(errorMessage, new UnsupportedOperationException("Distant Horizon OpenGL requirements not met"));
}
LOGGER.info("minecraftGlCapabilities:\n" + this.versionInfoToString(this.glCapabilities));
if (Config.Client.Advanced.Debugging.OpenGl.overrideVanillaGLLogger.get())
{
GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, this.vanillaDebugMessageBuilder), true));
}
//======================//
// get GPU capabilities //
//======================//
// UNUSED currently
// Check if we can use the named version of all calls, which is available in GL4.5 or after
this.namedObjectSupported = this.glCapabilities.glNamedBufferData != 0L; //Nullptr
// Check if we can use the Buffer Storage, which is available in GL4.4 or after
this.bufferStorageSupported = this.glCapabilities.glBufferStorage != 0L; // Nullptr
if (!this.bufferStorageSupported)
{
LOGGER.info("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods.");
}
// Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after
this.vertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr
// used by instanced rendering
this.vertexAttribDivisorSupported = this.glCapabilities.OpenGL33;
// denotes if ARBInstancedArrays.glVertexAttribDivisorARB() is available or not
// can be used as a backup if MC didn't create a GL 3.3+ context
this.instancedArraysSupported = this.glCapabilities.GL_ARB_instanced_arrays;
// get the best automatic upload method
String vendor = GL32.glGetString(GL32.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION"
if (EPlatform.get() != EPlatform.MACOS)
{
if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE"))
{
// NVIDIA card
this.preferredUploadMethod = this.bufferStorageSupported ? EDhApiGpuUploadMethod.BUFFER_STORAGE : EDhApiGpuUploadMethod.SUB_DATA;
}
else
{
// AMD or Intel card
this.preferredUploadMethod = this.bufferStorageSupported ? EDhApiGpuUploadMethod.BUFFER_STORAGE : EDhApiGpuUploadMethod.DATA;
}
}
else
{
// Mac may have an issue with Buffer Storage, so default to the most basic
// form of uploading
this.preferredUploadMethod = EDhApiGpuUploadMethod.DATA;
}
LOGGER.info("GPU Vendor [" + vendor + "] with OS [" + EPlatform.get().getName() + "], Preferred upload method is [" + this.preferredUploadMethod + "].");
TIMER.scheduleAtFixedRate(TimerUtil.createTimerTask(this::manualGlCleanupTick), MS_BETWEEN_CLEANUP_TICKS, MS_BETWEEN_CLEANUP_TICKS);
//==========//
// clean up //
//==========//
// GLProxy creation success
LOGGER.info(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
}
//endregion
//=========//
// getters //
//=========//
//region
public static boolean hasInstance() { return instance != null; }
/** @throws IllegalStateException if the Proxy hasn't been created yet and this is called outside the render thread */
public static GLProxy getInstance() throws IllegalStateException
{
if (instance == null)
{
instance = new GLProxy();
}
return instance;
}
public EDhApiGpuUploadMethod getGpuUploadMethod()
{
EDhApiGpuUploadMethod uploadOverride = Config.Client.Advanced.Debugging.OpenGl.glUploadMode.get();
if (uploadOverride == EDhApiGpuUploadMethod.AUTO)
{
return this.preferredUploadMethod;
}
return uploadOverride;
}
public static boolean runningOnRenderThread()
{
long currentContext = GLFW.glfwGetCurrentContext();
return currentContext != 0L; // if the context isn't null, it's the MC context
}
//endregion
//=========================//
// Worker Thread Runnables //
//=========================//
//region
public static void queueRunningOnRenderThread(Runnable renderCall)
{
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
RENDER_THREAD_RUNNABLE_QUEUE.add(() -> runOpenGlCall(renderCall, stackTrace));
}
private static void runOpenGlCall(Runnable renderCall, StackTraceElement[] stackTrace)
{
try
{
renderCall.run();
}
catch (Exception e)
{
RuntimeException error = new RuntimeException("Uncaught Exception during GL call execution:", e);
error.setStackTrace(stackTrace);
LOGGER.error("[" + Thread.currentThread().getName() + "] ran into an unexpected error running a GL call, Error: ["+ e.getMessage() +"].", error);
}
}
/**
* Doesn't do any thread/GL Context validation.
* Running this outside of the render thread may cause crashes or other issues.
*/
public void runRenderThreadTasks()
{
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
int frameLimit = MC_RENDER.getFrameLimit();
if (frameLimit <= 1)
{
frameLimit = 4; // 240 FPS
}
// https://fpstoms.com/
int msPerFrame = 1000 / frameLimit;
this.runRenderThreadTasks(msPerFrame);
}
private void runRenderThreadTasks(long msMaxRunTime)
{
long startTimeMs = System.currentTimeMillis();
this.msSinceGlTasksRun = startTimeMs;
Runnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
while(runnable != null)
{
runnable.run();
// only try running for 4ms (240 FPS) at a time to prevent random lag spikes
long currentTimeMs = System.currentTimeMillis();
long runDuration = currentTimeMs - startTimeMs;
if (runDuration > msMaxRunTime)
{
break;
}
runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
}
}
/**
* Should only be called if our render code isn't being hit for some reason.
* Normally this only happens if there's a mod that limits MC's framerate to 0.
*/
private void manualGlCleanupTick()
{
long nowMs = System.currentTimeMillis();
long msSinceLast = nowMs - this.msSinceGlTasksRun;
if (msSinceLast > MS_BEFORE_RUN_CLEANUP_TIMER)
{
return;
}
// We haven't gotten a frame for a while,
// this means we could have GL jobs building up.
// Run the queued tasks on MC's executor (hopefully this should always run,
// even if DH's render code isn't being hit).
IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
MC.executeOnRenderThread(() -> this.runRenderThreadTasks(1_000));
}
//endregion
//=========//
// logging //
//=========//
//region
/** this method is called on the render thread at the point of the GL Error */
private static void logMessage(GLMessage msg)
{
EDhApiGLErrorHandlingMode errorHandlingMode = Config.Client.Advanced.Debugging.OpenGl.glErrorHandlingMode.get();
if (errorHandlingMode == EDhApiGLErrorHandlingMode.IGNORE)
{
return;
}
boolean onlyLogOnce = Config.Client.Advanced.Debugging.OpenGl.onlyLogGlErrorsOnce.get();
String errorMessage = "GL ERROR [" + msg.id + "] from [" + msg.source + "]: [" + msg.message + "]"+(onlyLogOnce ? " this message will only be logged once" : "")+".";
if (onlyLogOnce
&& !LOGGED_GL_MESSAGES.add(errorMessage))
{
// this message has already been logged
return;
}
// create an exception so we get a stacktrace of where the message was triggered from
RuntimeException exception = new RuntimeException(errorMessage);
if (msg.type == EGLMessageType.ERROR || msg.type == EGLMessageType.UNDEFINED_BEHAVIOR)
{
// critical error
LOGGER.error(exception.getMessage(), exception);
if (errorHandlingMode == EDhApiGLErrorHandlingMode.LOG_THROW)
{
// will probably crash the game,
// good for quickly checking if there's a problem while preventing log spam
throw exception;
}
}
else
{
// non-critical log
EGLMessageSeverity severity = msg.severity;
if (severity == null)
{
// just in case the message was malformed
severity = EGLMessageSeverity.LOW;
}
switch (severity)
{
case HIGH:
LOGGER.error(exception.getMessage(), exception);
break;
case MEDIUM:
LOGGER.warn(exception.getMessage(), exception);
break;
case LOW:
LOGGER.info(exception.getMessage(), exception);
break;
case NOTIFICATION:
LOGGER.debug(exception.getMessage(), exception);
break;
}
}
}
//endregion
//================//
// helper methods //
//================//
//region
private String getFailedVersionInfo(GLCapabilities c)
{
return "Your OpenGL support:\n" +
"openGL version 3.2+: [" + c.OpenGL32 + "] <- REQUIRED\n" +
"Vertex Attribute Buffer Binding: [" + (c.glVertexAttribBinding != 0) + "] <- optional improvement\n" +
"Buffer Storage: [" + (c.glBufferStorage != 0) + "] <- optional improvement\n" +
"If you noticed that your computer supports higher OpenGL versions"
+ " but not the required version, try running the game in compatibility mode."
+ " (How you turn that on, I have no clue~)";
}
private String versionInfoToString(GLCapabilities c)
{
return "Your OpenGL support:\n" +
"openGL version 3.2+: [" + c.OpenGL32 + "] <- REQUIRED\n" +
"Vertex Attribute Buffer Binding: [" + (c.glVertexAttribBinding != 0) + "] <- optional improvement\n" +
"Buffer Storage: [" + (c.glBufferStorage != 0) + "] <- optional improvement\n";
}
//endregion
}
@@ -1,259 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.lwjgl.opengl.GL32;
public class GLState implements AutoCloseable
{
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
public int program;
public int vao;
public int vbo;
public int ebo;
public int fbo;
public int texture2D;
/** IE: GL_TEXTURE0, GL_TEXTURE1, etc. */
public int activeTextureNumber;
public int texture0;
public int texture1;
public int texture2;
public int texture3;
public int frameBufferTexture0;
public int frameBufferTexture1;
public int frameBufferDepthTexture;
public boolean blend;
public boolean scissor;
public int blendEqRGB;
public int blendEqAlpha;
public int blendSrcColor;
public int blendSrcAlpha;
public int blendDstColor;
public int blendDstAlpha;
public boolean depth;
public boolean writeToDepthBuffer;
public int depthFunc;
public boolean stencil;
public int stencilFunc;
public int stencilRef;
public int stencilMask;
public int[] view;
public boolean cull;
public int cullMode;
public int polyMode;
public GLState() { this.saveState(); }
public void saveState()
{
this.program = GL32.glGetInteger(GL32.GL_CURRENT_PROGRAM);
this.vao = GL32.glGetInteger(GL32.GL_VERTEX_ARRAY_BINDING);
this.vbo = GL32.glGetInteger(GL32.GL_ARRAY_BUFFER_BINDING);
this.ebo = GL32.glGetInteger(GL32.GL_ELEMENT_ARRAY_BUFFER_BINDING);
this.fbo = GL32.glGetInteger(GL32.GL_FRAMEBUFFER_BINDING);
this.texture2D = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
this.activeTextureNumber = GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE);
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
this.texture0 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
this.texture1 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(GL32.GL_TEXTURE2); // problem with Iris
this.texture2 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(GL32.GL_TEXTURE3);
this.texture3 = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GLMC.glActiveTexture(this.activeTextureNumber);
if (this.fbo != 0)
{
this.frameBufferTexture0 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
this.frameBufferTexture1 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
this.frameBufferDepthTexture = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
}
else
{
// attempting to get values from the default framebuffer can throw errors on Linux
this.frameBufferTexture0 = 0;
this.frameBufferTexture1 = 0;
this.frameBufferDepthTexture = 0;
}
this.blend = GL32.glIsEnabled(GL32.GL_BLEND);
this.scissor = GL32.glIsEnabled(GL32.GL_SCISSOR_TEST);
this.blendEqRGB = GL32.glGetInteger(GL32.GL_BLEND_EQUATION_RGB);
this.blendEqAlpha = GL32.glGetInteger(GL32.GL_BLEND_EQUATION_ALPHA);
this.blendSrcColor = GL32.glGetInteger(GL32.GL_BLEND_SRC_RGB);
this.blendSrcAlpha = GL32.glGetInteger(GL32.GL_BLEND_SRC_ALPHA);
this.blendDstColor = GL32.glGetInteger(GL32.GL_BLEND_DST_RGB);
this.blendDstAlpha = GL32.glGetInteger(GL32.GL_BLEND_DST_ALPHA);
this.depth = GL32.glIsEnabled(GL32.GL_DEPTH_TEST);
this.writeToDepthBuffer = GL32.glGetInteger(GL32.GL_DEPTH_WRITEMASK) == GL32.GL_TRUE;
this.depthFunc = GL32.glGetInteger(GL32.GL_DEPTH_FUNC);
this.stencil = GL32.glIsEnabled(GL32.GL_STENCIL_TEST);
this.stencilFunc = GL32.glGetInteger(GL32.GL_STENCIL_FUNC);
this.stencilRef = GL32.glGetInteger(GL32.GL_STENCIL_REF);
this.stencilMask = GL32.glGetInteger(GL32.GL_STENCIL_VALUE_MASK);
this.view = new int[4];
GL32.glGetIntegerv(GL32.GL_VIEWPORT, this.view);
this.cull = GL32.glIsEnabled(GL32.GL_CULL_FACE);
this.cullMode = GL32.glGetInteger(GL32.GL_CULL_FACE_MODE);
this.polyMode = GL32.glGetInteger(GL32.GL_POLYGON_MODE);
}
@Override
public void close()
{
// explicitly unbinding the frame buffer is necessary to prevent GL_CLEAR calls from hitting the wrong buffer
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, 0);
boolean frameBufferSet = false;
if (this.fbo != 0 && GL32.glIsFramebuffer(this.fbo))
{
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fbo);
frameBufferSet = true;
}
if (this.blend)
{
GLMC.enableBlend();
}
else
{
GLMC.disableBlend();
}
if (this.scissor)
{
GLMC.enableScissorTest();
}
else
{
GLMC.disableScissorTest();
}
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(GL32.glIsTexture(this.texture0) ? this.texture0 : 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE1);
GLMC.glBindTexture(GL32.glIsTexture(this.texture1) ? this.texture1 : 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE2);
GLMC.glBindTexture(GL32.glIsTexture(this.texture2) ? this.texture2 : 0);
GLMC.glActiveTexture(GL32.GL_TEXTURE3);
GLMC.glBindTexture(GL32.glIsTexture(this.texture3) ? this.texture3 : 0);
GLMC.glActiveTexture(this.activeTextureNumber);
GLMC.glBindTexture(GL32.glIsTexture(this.texture2D) ? this.texture2D : 0);
// attempting to set textures on the default frame buffer (ID 0) will throw errors
if (frameBufferSet)
{
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.frameBufferTexture0, 0);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_TEXTURE_2D, this.frameBufferTexture1, 0);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_TEXTURE_2D, this.frameBufferDepthTexture, 0);
}
GL32.glBindVertexArray(GL32.glIsVertexArray(this.vao) ? this.vao : 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, GL32.glIsBuffer(this.vbo) ? this.vbo : 0);
GL32.glBindBuffer(GL32.GL_ELEMENT_ARRAY_BUFFER, GL32.glIsBuffer(this.ebo) ? this.ebo: 0);
GL32.glUseProgram(GL32.glIsProgram(this.program) ? this.program : 0);
if (this.writeToDepthBuffer)
{
GLMC.enableDepthMask();
}
else
{
GLMC.disableDepthMask();
}
GLMC.glBlendFunc(this.blendSrcColor, this.blendDstColor);
GL32.glBlendEquationSeparate(this.blendEqRGB, this.blendEqAlpha);
GLMC.glBlendFuncSeparate(this.blendSrcColor, this.blendDstColor, this.blendSrcAlpha, this.blendDstAlpha);
if (this.depth)
{
GLMC.enableDepthTest();
}
else
{
GLMC.disableDepthTest();
}
GLMC.glDepthFunc(this.depthFunc);
if (this.stencil)
{
GL32.glEnable(GL32.GL_STENCIL_TEST);
}
else
{
GL32.glDisable(GL32.GL_STENCIL_TEST);
}
GL32.glStencilFunc(this.stencilFunc, this.stencilRef, this.stencilMask);
GL32.glViewport(this.view[0], this.view[1], this.view[2], this.view[3]);
if (this.cull)
{
GLMC.enableFaceCulling();
}
else
{
GLMC.disableFaceCulling();
}
GL32.glCullFace(this.cullMode);
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, this.polyMode);
}
@Override
public String toString()
{
return "GLState{" +
"program=" + this.program + ", vao=" + this.vao + ", vbo=" + this.vbo + ", ebo=" + this.ebo + ", fbo=" + this.fbo +
", text=" + GLEnums.getString(this.texture2D) + "@" + this.activeTextureNumber + ", text0=" + GLEnums.getString(this.texture0) +
", FB text0=" + this.frameBufferTexture0 +
", FB text1=" + this.frameBufferTexture1 +
", FB depth=" + this.frameBufferDepthTexture +
", blend=" + this.blend + ", scissor=" + this.scissor + ", blendMode=" + GLEnums.getString(this.blendSrcColor) + "," + GLEnums.getString(this.blendDstColor) +
", depth=" + this.depth +
", depthFunc=" + GLEnums.getString(this.depthFunc) + ", stencil=" + this.stencil +
", stencilFunc=" + GLEnums.getString(this.stencilFunc) + ", stencilRef=" + this.stencilRef + ", stencilMask=" + this.stencilMask +
", view={x:" + this.view[0] + ", y:" + this.view[1] +
", w:" + this.view[2] + ", h:" + this.view[3] + "}" + ", cull=" + this.cull +
", cullMode=" + GLEnums.getString(this.cullMode) + ", polyMode=" + GLEnums.getString(this.polyMode) +
'}';
}
}
@@ -1,344 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject.buffer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.core.config.Config;
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.glObject.GLProxy;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL44;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
public class GLBuffer implements AutoCloseable
{
private static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile)
.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat)
.build();
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3;
public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER;
/** the number of active buffers, can be used for debugging */
public static AtomicInteger bufferCount = new AtomicInteger(0);
private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000;
private static final ConcurrentHashMap<PhantomReference<? extends GLBuffer>, Integer> PHANTOM_TO_BUFFER_ID = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, PhantomReference<? extends GLBuffer>> BUFFER_ID_TO_PHANTOM = new ConcurrentHashMap<>();
private static final ReferenceQueue<GLBuffer> PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>();
private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("GLBuffer Cleanup");
protected int id;
public final int getId() { return this.id; }
protected int size = 0;
public int getSize() { return this.size; }
protected boolean bufferStorage;
public final boolean isBufferStorage() { return this.bufferStorage; }
protected boolean isMapped = false;
//==============//
// constructors //
//==============//
static { CLEANUP_THREAD.execute(() -> runPhantomReferenceCleanupLoop()); }
public GLBuffer(boolean isBufferStorage) { this.create(isBufferStorage); }
//=========//
// methods //
//=========//
// Should be override by subclasses
public int getBufferBindingTarget() { return GL32.GL_COPY_READ_BUFFER; }
public void bind() { GL32.glBindBuffer(this.getBufferBindingTarget(), this.id); }
public void unbind() { GL32.glBindBuffer(this.getBufferBindingTarget(), 0); }
//====================//
// create and destroy //
//====================//
protected void create(boolean asBufferStorage)
{
if (!GLProxy.runningOnRenderThread())
{
LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread.");
}
// destroy the old buffer if one is present
// (as of 2024-12-31 James didn't see this happen, but just in case)
if (this.id != 0)
{
destroyBufferIdAsync(this.id);
}
this.id = GLMC.glGenBuffers();
this.bufferStorage = asBufferStorage;
bufferCount.getAndIncrement();
PhantomReference<GLBuffer> 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);
}
GLProxy.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. <br>
* 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);
}
}
}
}
@@ -1,60 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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;
}
}
@@ -1,88 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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. <br>
* 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;
}
}
@@ -1,196 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject.buffer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.glObject.GLEnums;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/** 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
}
@@ -1,185 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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 com.seibel.distanthorizons.core.render.glObject.GLProxy;
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.
*
* <p>Hat tip to fewizz for the find and the fix.
*
* <p>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
}
@@ -1,230 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject.shader;
import java.awt.Color;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
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.Vec3d;
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. <br>
* 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. <br>
* 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
}
@@ -1,63 +0,0 @@
package com.seibel.distanthorizons.core.render.glObject.texture;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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;
}
}
@@ -1,184 +0,0 @@
package com.seibel.distanthorizons.core.render.glObject.texture;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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); }
}
}
@@ -1,153 +0,0 @@
package com.seibel.distanthorizons.core.render.glObject.texture;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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; }
}
@@ -1,114 +0,0 @@
package com.seibel.distanthorizons.core.render.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; }
}
@@ -1,130 +0,0 @@
package com.seibel.distanthorizons.core.render.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<EDhInternalTextureFormat> 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; }
}
@@ -1,60 +0,0 @@
package com.seibel.distanthorizons.core.render.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<EDhPixelFormat> 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; }
}
@@ -1,64 +0,0 @@
package com.seibel.distanthorizons.core.render.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<EDhPixelType> 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; }
}
@@ -1,9 +0,0 @@
package com.seibel.distanthorizons.core.render.glObject.texture;
public enum EGlVersion
{
GL_11,
GL_12,
GL_30,
GL_31
}
@@ -1,92 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject.vertexAttribute;
import com.seibel.distanthorizons.core.render.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);
}
@@ -1,156 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.glObject.vertexAttribute;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
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. <br><br>
*
* 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);
}
}
@@ -1,254 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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 com.seibel.distanthorizons.core.render.glObject.GLProxy;
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<Integer, TreeSet<Integer>> bindingPointsToIndexBuilder;
ArrayList<VertexPointer> 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<Integer> 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<Integer> set) ->
{
this.bindingPointsToIndex[i] = new int[set.size()];
Iterator<Integer> 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]);
}
}
}
}
@@ -1,72 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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); }
}
@@ -1,513 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
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.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLElementBuffer;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcDebugRenderer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.PriorityBlockingQueue;
/**
* Handles rendering the wireframe particles
* that are used for seeing what the system's doing.
*/
public class DebugRenderer
{
public static DebugRenderer INSTANCE = new DebugRenderer();
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
.maxCountPerSecond(1)
.build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
// rendering setup
private ShaderProgram basicShader;
private GLElementBuffer outlineIndexBuffer;
private AbstractVertexAttribute va;
private boolean init = false;
// used when rendering
private Mat4f dhMvmProjMatrixThisFrame;
private Vec3f camPosFloatThisFrame;
private final RendererLists rendererLists = new RendererLists();
private final PriorityBlockingQueue<BoxParticle> particles = new PriorityBlockingQueue<>();
///** A box from 0,0,0 to 1,1,1 */
//private static final float[] BOX_VERTICES = {
// //region
// // Pos x y z
// 0, 0, 0,
// 1, 0, 0,
// 1, 1, 0,
// 0, 1, 0,
// 0, 0, 1,
// 1, 0, 1,
// 1, 1, 1,
// 0, 1, 1,
// //endregion
//};
//
//private static final int[] BOX_OUTLINE_INDICES = {
// //region
// 0, 1,
// 1, 2,
// 2, 3,
// 3, 0,
//
// 4, 5,
// 5, 6,
// 6, 7,
// 7, 4,
//
// 0, 4,
// 1, 5,
// 2, 6,
// 3, 7,
// //endregion
//};
//=============//
// constructor //
//=============//
//region
private DebugRenderer() { }
//public void init()
//{
// if (this.init)
// {
// return;
// }
// this.init = true;
//
// this.va = AbstractVertexAttribute.create();
// this.va.bind();
// // Pos
// this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false));
// this.va.completeAndCheck(Float.BYTES * 3);
// this.basicShader = new ShaderProgram(
// "shaders/debug/vert.vert",
// "shaders/debug/frag.frag",
// "vPosition"
// );
// this.createBuffer();
//}
//
//private void createBuffer()
//{
// // box vertices
// ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES);
// boxVerticesBuffer.order(ByteOrder.nativeOrder());
// boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES);
// boxVerticesBuffer.rewind();
//
//
// // outline vertex indexes
// ByteBuffer boxOutlineBuffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES);
// boxOutlineBuffer.order(ByteOrder.nativeOrder());
// boxOutlineBuffer.asIntBuffer().put(BOX_OUTLINE_INDICES);
// boxOutlineBuffer.rewind();
// this.outlineIndexBuffer = new GLElementBuffer(false);
// this.outlineIndexBuffer.uploadBuffer(boxOutlineBuffer, EDhApiGpuUploadMethod.DATA, BOX_OUTLINE_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
//
//}
//endregion
//==============//
// registration //
//==============//
//region
public static void makeParticle(BoxParticle particle)
{
if (INSTANCE != null && Config.Client.Advanced.Debugging.DebugWireframe.enableRendering.get())
{
INSTANCE.particles.add(particle);
}
}
public static void register(IDebugRenderable renderable, ConfigEntry<Boolean> config) { if (INSTANCE != null) { INSTANCE.addRenderer(renderable, config); } }
public void addRenderer(IDebugRenderable renderable, ConfigEntry<Boolean> config) { this.rendererLists.addRenderable(renderable, config); }
public static void unregister(IDebugRenderable renderable, ConfigEntry<Boolean> config) { if (INSTANCE != null) { INSTANCE.removeRenderer(renderable, config); } }
private void removeRenderer(IDebugRenderable renderable, ConfigEntry<Boolean> config) { this.rendererLists.removeRenderable(renderable, config); }
public static void clearRenderables() { INSTANCE.rendererLists.clearRenderables(); }
//endregion
//===========//
// rendering //
//===========//
//region
public void render(RenderParams renderEventParam)
{
//this.dhMvmProjMatrixThisFrame = dhMvmProjMatrix;
//Vec3d camPos = MC_RENDER.getCameraExactPosition();
//this.camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z);
//this.init();
//GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
//GLMC.enableDepthTest();
//
//this.basicShader.bind();
//this.va.bind();
//
//
//this.outlineIndexBuffer.bind();
this.rendererLists.render(this);
// particle rendering
BoxParticle head = null;
while ((head = this.particles.poll()) != null && head.isDead())
{ /* remove dead particles */ }
if (head != null)
{
// re-add the popped off head
this.particles.add(head);
}
IMcDebugRenderer renderer = SingletonInjector.INSTANCE.get(IMcDebugRenderer.class);
renderer.render(renderEventParam, this.particles);
}
@Deprecated // TODO this should add all the boxes to a list so we can render them as a batch instead of individual draw calls
public void renderBox(Box box)
{
IMcDebugRenderer renderer = SingletonInjector.INSTANCE.get(IMcDebugRenderer.class);
renderer.render(box);
}
//public void render(Mat4f dhMvmProjMatrix)
//{
// this.dhMvmProjMatrixThisFrame = dhMvmProjMatrix;
// Vec3d camPos = MC_RENDER.getCameraExactPosition();
// this.camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z);
//
// this.init();
//
// GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
// GLMC.enableDepthTest();
//
// this.basicShader.bind();
// this.va.bind();
//
//
// this.outlineIndexBuffer.bind();
// this.rendererLists.render(this);
//
//
// // particle rendering
// BoxParticle head = null;
// while ((head = this.particles.poll()) != null && head.isDead())
// { /* remove dead particles */ }
// if (head != null)
// {
// // re-add the popped off head
// this.particles.add(head);
// }
//
//
// // box rendering
// GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
// for (BoxParticle particle : this.particles)
// {
// // a new box is created each time since the height will be different based on the time it's lived
// this.renderBox(particle.createNewRenderBox());
// }
//}
//
//public void renderBox(Box box)
//{
// Mat4f boxTransform = Mat4f.createTranslateMatrix(box.minPos.x - this.camPosFloatThisFrame.x, box.minPos.y - this.camPosFloatThisFrame.y, box.minPos.z - this.camPosFloatThisFrame.z);
// boxTransform.multiply(Mat4f.createScaleMatrix(box.maxPos.x - box.minPos.x, box.maxPos.y - box.minPos.y, box.maxPos.z - box.minPos.z));
//
// Mat4f transformMatrix = this.dhMvmProjMatrixThisFrame.copy();
// transformMatrix.multiply(boxTransform);
// this.basicShader.setUniform(this.basicShader.getUniformLocation("uTransform"), transformMatrix);
//
// this.basicShader.setUniform(this.basicShader.getUniformLocation("uColor"), box.color);
//
// GL32.glDrawElements(GL32.GL_LINES, BOX_OUTLINE_INDICES.length, GL32.GL_UNSIGNED_INT, 0);
//}
//endregion
//================//
// helper classes //
//================//
//region
public static final class Box
{
public Vec3f minPos;
public Vec3f maxPos;
public Color color;
public Box(long pos, float minY, float maxY, float marginPercent, Color color)
{
float edgeOffset = DhSectionPos.getBlockWidth(pos) * marginPercent;
int minBlockPosX = DhSectionPos.getMinCornerBlockX(pos);
int minBlockPosZ = DhSectionPos.getMinCornerBlockZ(pos);
int maxBlockPosX = minBlockPosX + DhSectionPos.getBlockWidth(pos);
int maxBlockPosZ = minBlockPosZ + DhSectionPos.getBlockWidth(pos);
this.minPos = new Vec3f(minBlockPosX + edgeOffset, minY, minBlockPosZ + edgeOffset);
this.maxPos = new Vec3f(maxBlockPosX - edgeOffset, maxY, maxBlockPosZ - edgeOffset);
this.color = color;
}
/** only used for */
public Box(Vec3f minPos, Vec3f maxPos, Color color)
{
this.minPos = minPos;
this.maxPos = maxPos;
this.color = color;
}
}
public static final class BoxParticle implements Comparable<BoxParticle>
{
public Box box;
public long startMsTime;
public long durationInMs;
public float yChange;
private BoxParticle(Box box, long startMsTime, long durationInMs, float yChange)
{
this.box = box;
this.startMsTime = startMsTime;
this.durationInMs = durationInMs;
this.yChange = yChange;
}
public BoxParticle(Box box, double secondDuration, float yChange)
{ this(box, System.currentTimeMillis(), (long) (secondDuration * 1_000), yChange); }
@Override
public int compareTo(@NotNull DebugRenderer.BoxParticle particle)
{ return Long.compare(this.startMsTime + this.durationInMs, particle.startMsTime + particle.durationInMs); }
/** will change each time it's called based on the yChange value and time */
public Box createNewRenderBox()
{
long nowMs = System.currentTimeMillis();
float percent = (nowMs - this.startMsTime) / (float) this.durationInMs;
percent = (float) Math.pow(percent, 4);
float yDiff = this.yChange * percent;
return new Box(
new Vec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z),
new Vec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z),
this.box.color);
}
public boolean isDead() { return (System.currentTimeMillis() - this.startMsTime) > this.durationInMs; }
}
private static class RendererLists
{
public final LinkedList<WeakReference<IDebugRenderable>> generalRenderableList = new LinkedList<>();
private final HashMap<ConfigEntry<Boolean>, LinkedList<WeakReference<IDebugRenderable>>> renderableListByConfig = new HashMap<>();
//==============//
// registration //
//==============//
//region
public void addRenderable(IDebugRenderable renderable, @Nullable ConfigEntry<Boolean> config)
{
synchronized (this)
{
if (config != null)
{
if (!this.renderableListByConfig.containsKey(config))
{
this.renderableListByConfig.put(config, new LinkedList<>());
}
LinkedList<WeakReference<IDebugRenderable>> renderableList = this.renderableListByConfig.get(config);
renderableList.add(new WeakReference<>(renderable));
}
else
{
this.generalRenderableList.add(new WeakReference<>(renderable));
}
}
}
public void removeRenderable(IDebugRenderable renderable, @Nullable ConfigEntry<Boolean> config)
{
synchronized (this)
{
if (config != null)
{
if (this.renderableListByConfig.containsKey(config))
{
LinkedList<WeakReference<IDebugRenderable>> renderableList = this.renderableListByConfig.get(config);
this.removeRenderableFromInternalList(renderableList, renderable);
}
}
else
{
this.removeRenderableFromInternalList(this.generalRenderableList, renderable);
}
}
}
private void removeRenderableFromInternalList(LinkedList<WeakReference<IDebugRenderable>> rendererList, IDebugRenderable renderable)
{
Iterator<WeakReference<IDebugRenderable>> iterator = rendererList.iterator();
while (iterator.hasNext())
{
WeakReference<IDebugRenderable> renderableRef = iterator.next();
if (renderableRef.get() == null)
{
iterator.remove();
continue;
}
if (renderableRef.get() == renderable)
{
iterator.remove();
return;
}
}
}
public void clearRenderables()
{
for (ConfigEntry<Boolean> config : this.renderableListByConfig.keySet())
{
LinkedList<WeakReference<IDebugRenderable>> renderableList = this.renderableListByConfig.get(config);
if (config.get() && renderableList != null)
{
renderableList.clear();
}
}
}
//endregion
//===========//
// rendering //
//===========//
//region
public void render(DebugRenderer debugRenderer)
{
this.renderList(debugRenderer, this.generalRenderableList);
for (ConfigEntry<Boolean> config : this.renderableListByConfig.keySet())
{
LinkedList<WeakReference<IDebugRenderable>> renderableList = this.renderableListByConfig.get(config);
if (config.get() && renderableList != null && renderableList.size() != 0)
{
this.renderList(debugRenderer, renderableList);
}
}
}
private void renderList(DebugRenderer debugRenderer, LinkedList<WeakReference<IDebugRenderable>> rendererList)
{
synchronized (this)
{
try
{
Iterator<WeakReference<IDebugRenderable>> iterator = rendererList.iterator();
while (iterator.hasNext())
{
WeakReference<IDebugRenderable> ref = iterator.next();
IDebugRenderable renderable = ref.get();
if (renderable == null)
{
iterator.remove();
continue;
}
renderable.debugRender(debugRenderer);
}
}
catch (Exception e)
{
RATE_LIMITED_LOGGER.error("Unexpected Debug renderer error, Error: "+e.getMessage(), e);
}
}
}
//endregion
}
//endregion
}
@@ -1,164 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
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.shaders.DhFadeShader;
import com.seibel.distanthorizons.core.render.renderer.shaders.FadeApplyShader;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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}. <br><br>
*
* {@link DhFadeShader} - draws the Fade to a texture. <br>
* {@link FadeApplyShader} - draws the Fade texture to DH's framebuffer. <br>
*/
public class DhFadeRenderer
{
public static DhFadeRenderer INSTANCE = new DhFadeRenderer();
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
}
@@ -1,226 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.shader.Shader;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexAttributePostGL43;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexAttributePreGL43;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
/**
* Handles rendering the normal LOD terrain.
* @see LodQuadBuilder
*/
public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShaderProgram
{
public final AbstractVertexAttribute vao;
// Uniforms
public int uCombinedMatrix = -1;
public int uModelOffset = -1;
public int uWorldYOffset = -1;
public int uMircoOffset = -1;
public int uEarthRadius = -1;
public int uLightMap = -1;
// fragment shader uniforms
public int uClipDistance = -1;
public int uDitherDhRendering = -1;
// Noise Uniforms
public int uNoiseEnabled = -1;
public int uNoiseSteps = -1;
public int uNoiseIntensity = -1;
public int uNoiseDropoff = -1;
// Debug Uniform
public int uIsWhiteWorld = -1;
//=============//
// constructor //
//=============//
// This will bind AbstractVertexAttribute
public DhTerrainShaderProgram()
{
super(
"shaders/standard.vert",
"shaders/flat_shaded.frag",
new String[]{"vPosition", "color"}
);
this.uCombinedMatrix = this.getUniformLocation("uCombinedMatrix");
this.uModelOffset = this.getUniformLocation("uModelOffset");
this.uWorldYOffset = this.getUniformLocation("uWorldYOffset");
this.uDitherDhRendering = this.getUniformLocation("uDitherDhRendering");
this.uMircoOffset = this.getUniformLocation("uMircoOffset");
this.uEarthRadius = this.getUniformLocation("uEarthRadius");
this.uLightMap = this.getUniformLocation("uLightMap");
// Fog/Clip Uniforms
this.uClipDistance = this.getUniformLocation("uClipDistance");
// Noise Uniforms
this.uNoiseEnabled = this.getUniformLocation("uNoiseEnabled");
this.uNoiseSteps = this.getUniformLocation("uNoiseSteps");
this.uNoiseIntensity = this.getUniformLocation("uNoiseIntensity");
this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff");
// Debug Uniform
this.uIsWhiteWorld = this.getUniformLocation("uIsWhiteWorld");
if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
{
this.vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute
}
else
{
this.vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute
}
this.vao.bind();
// short: x, y, z, meta
// meta: byte skylight, byte blocklight, byte microOffset
this.vao.setVertexAttribute(0, 0, VertexPointer.addUnsignedShortsPointer(4, false, true));
// byte: r, g, b, a
this.vao.setVertexAttribute(0, 1, VertexPointer.addUnsignedBytesPointer(4, true, false));
// byte: iris material ID, normal index, 2 spacers
this.vao.setVertexAttribute(0, 2, VertexPointer.addUnsignedBytesPointer(4, true, true));
try
{
int vertexByteCount = LodUtil.DH_VERTEX_FORMAT.getByteSize();
this.vao.completeAndCheck(vertexByteCount);
}
catch (RuntimeException e)
{
System.out.println(LodUtil.DH_VERTEX_FORMAT);
throw e;
}
}
//=========//
// methods //
//=========//
@Override
public void bind()
{
super.bind();
this.vao.bind();
}
@Override
public void unbind()
{
super.unbind();
this.vao.unbind();
}
@Override
public void free()
{
this.vao.free();
super.free();
}
@Override
public void bindVertexBuffer(int vbo) { this.vao.bindBufferToAllBindingPoints(vbo); }
@Override
public void fillUniformData(DhApiRenderParam renderParameters)
{
Mat4f combinedMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
combinedMatrix.multiply(renderParameters.dhModelViewMatrix);
super.bind();
// uniforms
this.setUniform(this.uCombinedMatrix, combinedMatrix);
this.setUniform(this.uMircoOffset, 0.01f); // 0.01 block offset
this.setUniform(this.uLightMap, ILightMapWrapper.BOUND_INDEX);
this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset);
this.setUniform(this.uDitherDhRendering, Config.Client.Advanced.Graphics.Quality.ditherDhFade.get());
float curveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get();
if (curveRatio < -1.0f || curveRatio > 1.0f)
{
curveRatio = /*6371KM*/ 6371000.0f / curveRatio;
}
else
{
// disable curvature if the config value is between -1 and 1
curveRatio = 0.0f;
}
this.setUniform(this.uEarthRadius, curveRatio);
// Noise Uniforms
this.setUniform(this.uNoiseEnabled, Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get());
this.setUniform(this.uNoiseSteps, Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get());
this.setUniform(this.uNoiseIntensity, Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get());
this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get());
// Debug
this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
// Clip Uniform
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks();
if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
// this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f;
}
this.setUniform(this.uClipDistance, dhNearClipDistance);
}
@Override
public void setModelOffsetPos(DhApiVec3f modelOffsetPos) { this.setUniform(this.uModelOffset, new Vec3f(modelOffsetPos)); }
@Override
public int getId() { return this.id; }
/** The base DH render program should always render */
@Override
public boolean overrideThisFrame() { return true; }
}
@@ -1,142 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.renderer.shaders.FogApplyShader;
import com.seibel.distanthorizons.core.render.renderer.shaders.FogShader;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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}. <br><br>
*
* {@link FogShader} - draws the Fog to a texture. <br>
* {@link FogApplyShader} - draws the Fog texture to DH's FrameBuffer. <br>
*/
public class FogRenderer
{
public static FogRenderer INSTANCE = new FogRenderer();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,767 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
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.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer;
import com.seibel.distanthorizons.core.render.glObject.texture.*;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.render.renderer.shaders.*;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcGenericRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcTestRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32;
/**
* This is where all the magic happens. <br>
* This is where LODs are draw to the world.
*/
public class LodRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
.build();
public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
.maxCountPerSecond(4)
.build();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
public static final LodRenderer INSTANCE = new LodRenderer();
// these ID's either what any render is currently using (since only one renderer can be active at a time), or just used previously
private int activeFramebufferId = -1;
private int activeColorTextureId = -1;
private int activeDepthTextureId = -1;
private int textureWidth;
private int textureHeight;
private IDhApiShaderProgram lodRenderProgram = null;
public QuadElementBuffer quadIBO = null;
private boolean renderObjectsCreated = false;
// framebuffer and texture ID's for this renderer
private IDhApiFramebuffer framebuffer;
/** will be null if MC's framebuffer is being used since MC already has a color texture */
@Nullable
private DhColorTexture nullableColorTexture;
private DHDepthTexture depthTexture;
/**
* If true the {@link LodRenderer#framebuffer} is the same as MC's.
* This should only be true in the case of Optifine so LODs won't be overwritten when shaders are enabled.
*/
private boolean usingMcFramebuffer = false;
//=============//
// constructor //
//=============//
private LodRenderer() { }
//===========//
// rendering //
//===========//
//region
/**
* This will draw both opaque and transparent LODs if
* {@link DhApiRenderProxy#getDeferTransparentRendering()} is false,
* otherwise it will only render opaque LODs.
*/
public void render(RenderParams renderParams, IProfilerWrapper profiler)
{ this.renderLodPass(renderParams, profiler, false); }
/**
* This method is designed for Iris to be able
* to draw water in a deferred rendering context.
* It needs to be updated with any major changes,
* but shouldn't be activated as per deferWaterRendering.
*/
public void renderDeferred(RenderParams renderParams, IProfilerWrapper profiler)
{ this.renderLodPass(renderParams, profiler, true); }
private void renderLodPass(RenderParams renderParams, IProfilerWrapper profiler, boolean runningDeferredPass)
{
//====================//
// validate rendering //
//====================//
boolean deferTransparentRendering = DhApiRenderProxy.INSTANCE.getDeferTransparentRendering();
if (runningDeferredPass
&& !deferTransparentRendering)
{
return;
}
boolean firstPass = !runningDeferredPass;
// RenderParams parameter validation should be done before this
if (!renderParams.validationRun)
{
throw new IllegalArgumentException("Render parameters validation");
}
RenderBufferHandler renderBufferHandler = renderParams.renderBufferHandler;
IMcGenericRenderer genericRenderer = renderParams.genericRenderer;
ILightMapWrapper lightmap = renderParams.lightmap;
//=================//
// rendering setup //
//=================//
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderSetupEvent.class, renderParams);
profiler.push("LOD GL setup");
if (!this.renderObjectsCreated)
{
boolean setupSuccess = this.createRenderObjects();
if (!setupSuccess)
{
// shouldn't normally happen, but just in case
return;
}
// only do this once, that way they can still be reverted if desired
if (Config.Client.Advanced.Graphics.overrideVanillaGraphicsSettings.get())
{
LOGGER.info("Overriding vanilla MC settings to better fit Distant Horizons... This behavior can be disabled in the Distant Horizons config.");
MC.disableVanillaClouds();
MC.disableVanillaChunkFadeIn();
MC.disableFabulousTransparency();
}
this.renderObjectsCreated = true;
}
this.setGLState(renderParams, firstPass);
lightmap.bind();
this.quadIBO.bind();
if (firstPass)
{
// we only need to sort/cull the LODs during the first frame
profiler.popPush("LOD build render list");
renderBufferHandler.buildRenderList(renderParams);
}
IDhApiShaderProgram lodShaderProgram = this.lodRenderProgram;
IDhApiShaderProgram lodShaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
if (lodShaderProgramOverride != null && lodShaderProgram.overrideThisFrame())
{
lodShaderProgram = lodShaderProgramOverride;
}
//===========//
// rendering //
//===========//
if (!runningDeferredPass)
{
//=========================//
// opaque and non-deferred //
// transparent rendering //
//=========================//
// opaque LODs
profiler.popPush("LOD Opaque");
this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ true);
// custom objects with SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
profiler.popPush("Custom Objects");
genericRenderer.render(renderParams, profiler, true);
}
// SSAO
if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get())
{
profiler.popPush("LOD SSAO");
SSAORenderer.INSTANCE.render(new Mat4f(renderParams.dhProjectionMatrix), renderParams.partialTicks);
}
// custom objects without SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
profiler.popPush("Custom Objects");
genericRenderer.render(renderParams, profiler, false);
}
// combined pass transparent rendering
if (!deferTransparentRendering
&& Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
profiler.popPush("LOD Transparent");
this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ false);
}
// far plane clip fading
if (Config.Client.Advanced.Graphics.Quality.dhFadeFarClipPlane.get()
// the fade shader messes with the GL state in a way Iris doesn't like,
// so skip it if a shader is active
&& (IRIS_ACCESSOR == null || !IRIS_ACCESSOR.isShaderPackInUse()))
{
profiler.popPush("Fade Far Clip Fade");
DhFadeRenderer.INSTANCE.render(
new Mat4f(renderParams.mcModelViewMatrix), new Mat4f(renderParams.mcProjectionMatrix),
renderParams.partialTicks, profiler);
}
// fog
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()
// this is done to fix issues with: underwater fog, blindness effect, etc.
|| renderParams.vanillaFogEnabled)
{
profiler.popPush("LOD Fog");
Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix);
combinedMatrix.multiply(renderParams.dhModelViewMatrix);
FogRenderer.INSTANCE.render(combinedMatrix, renderParams.partialTicks);
}
//=================//
// debug rendering //
//=================//
if (Config.Client.Advanced.Debugging.DebugWireframe.enableRendering.get())
{
profiler.popPush("Debug wireframes");
Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix);
combinedMatrix.multiply(renderParams.dhModelViewMatrix);
// Note: this can be very slow if a lot of boxes are being rendered
//DebugRenderer.INSTANCE.render(combinedMatrix); // TODO
}
//===================//
// optifine clean up //
//===================//
if (this.usingMcFramebuffer)
{
// If MC's framebuffer is being used the depth needs to be cleared to prevent rendering on top of MC.
// This should only happen when Optifine shaders are being used.
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
//=============================//
// Apply to the MC Framebuffer //
//=============================//
boolean cancelApplyShader = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeApplyShaderRenderEvent.class, renderParams);
if (!cancelApplyShader)
{
profiler.popPush("LOD Apply");
// Copy the LOD framebuffer to Minecraft's framebuffer
DhApplyShader.INSTANCE.render(renderParams.partialTicks);
}
}
else
{
//====================//
// deferred rendering //
//====================//
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
profiler.popPush("LOD Transparent");
this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ false);
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()
// this is done to fix issues with: underwater fog, blindness effect, etc.
|| renderParams.vanillaFogEnabled)
{
profiler.popPush("LOD Fog");
Mat4f combinedMatrix = new Mat4f(renderParams.dhProjectionMatrix);
combinedMatrix.multiply(renderParams.dhModelViewMatrix);
FogRenderer.INSTANCE.render(combinedMatrix, renderParams.partialTicks);
}
}
}
//================//
// render cleanup //
//================//
profiler.popPush("LOD cleanup");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderCleanupEvent.class, renderParams);
lightmap.unbind();
this.quadIBO.unbind();
lodShaderProgram.unbind();
// end of internal LOD profiling
profiler.pop();
}
//endregion
//=================//
// Setup Functions //
//=================//
//region
private void setGLState(
DhApiRenderParam renderEventParam,
boolean firstPass)
{
//===================//
// framebuffer setup //
//===================//
// get the active framebuffer
IDhApiFramebuffer framebuffer = this.framebuffer;
IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class);
if (framebufferOverride != null && framebufferOverride.overrideThisFrame())
{
framebuffer = framebufferOverride;
}
this.activeFramebufferId = framebuffer.getId();
framebuffer.bind();
//==========//
// bindings //
//==========//
// by default draw everything as triangles
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
GLMC.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA);
GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ZERO);
GL32.glDisable(GL32.GL_SCISSOR_TEST);
// Enable depth test and depth mask
GLMC.enableDepthTest();
GLMC.glDepthFunc(GL32.GL_LESS);
GLMC.enableDepthMask();
// This is required for MC versions 1.21.5+
// due to MC updating the lightmap by changing the viewport size
GL32.glViewport(0, 0, this.textureWidth, this.textureHeight);
this.lodRenderProgram.bind();
//==========//
// uniforms //
//==========//
IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
if (shaderProgramOverride != null)
{
shaderProgramOverride.fillUniformData(renderEventParam);
}
this.lodRenderProgram.fillUniformData(renderEventParam);
//===============//
// texture setup //
//===============//
// resize the textures if needed
if (MC_RENDER.getTargetFramebufferViewportWidth() != this.textureWidth
|| MC_RENDER.getTargetFramebufferViewportHeight() != this.textureHeight)
{
// just resizing the textures doesn't work when Optifine is present,
// so recreate the textures with the new size instead
this.createAndBindTextures();
}
// set the active textures
this.activeDepthTextureId = this.depthTexture.getTextureId();
if (this.nullableColorTexture != null)
{
this.activeColorTextureId = this.nullableColorTexture.getTextureId();
}
else
{
// get MC's color texture
this.activeColorTextureId = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
}
// needs to be fired after all the textures have been created/bound
boolean clearTextures = !ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeTextureClearEvent.class, renderEventParam);
if (clearTextures)
{
GL32.glClearDepth(1.0);
float[] clearColorValues = new float[4];
GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues);
GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 1.0f);
if (this.usingMcFramebuffer && framebufferOverride == null)
{
// Due to using MC/Optifine's framebuffer we need to re-bind the depth texture,
// otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues
framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil());
// don't clear the color texture, that removes the sky
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
}
else if (firstPass)
{
GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT);
}
}
}
private boolean createRenderObjects()
{
if (this.renderObjectsCreated)
{
LOGGER.warn("Renderer setup called but it has already completed setup!");
return false;
}
// GLProxy should have already been created by this point, but just in case create it now
GLProxy.getInstance();
LOGGER.info("Setting up renderer");
this.lodRenderProgram = new DhTerrainShaderProgram();
this.quadIBO = new QuadElementBuffer();
this.quadIBO.reserve(LodBufferContainer.MAX_QUADS_PER_BUFFER);
// create or get the frame buffer
if (AbstractOptifineAccessor.optifinePresent())
{
// use MC/Optifine's default Framebuffer so shaders won't remove the LODs
int currentFramebufferId = MC_RENDER.getTargetFramebuffer();
this.framebuffer = new DhFramebuffer(currentFramebufferId);
this.usingMcFramebuffer = true;
}
else
{
// normal use case
this.framebuffer = new DhFramebuffer();
this.usingMcFramebuffer = false;
}
// create and bind the necessary textures
this.createAndBindTextures();
if(this.framebuffer.getStatus() != GL32.GL_FRAMEBUFFER_COMPLETE)
{
// This generally means something wasn't bound, IE missing either the color or depth texture
LOGGER.warn("Framebuffer ["+this.framebuffer.getId()+"] isn't complete.");
return false;
}
LOGGER.info("Renderer setup complete");
return true;
}
@SuppressWarnings( "deprecation" ) // done to ignore DhApiColorDepthTextureCreatedEvent
private void createAndBindTextures()
{
int oldWidth = this.textureWidth;
int oldHeight = this.textureHeight;
this.textureWidth = MC_RENDER.getTargetFramebufferViewportWidth();
this.textureHeight = MC_RENDER.getTargetFramebufferViewportHeight();
DhApiTextureCreatedParam textureCreatedParam = new DhApiTextureCreatedParam(
oldWidth, oldHeight,
this.textureWidth, this.textureHeight
);
// DhApiColorDepthTextureCreatedEvent needs to be kept around since old versions of Iris need it
ApiEventInjector.INSTANCE.fireAllEvents(DhApiColorDepthTextureCreatedEvent.class, new DhApiColorDepthTextureCreatedEvent.EventParam(textureCreatedParam));
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeColorDepthTextureCreatedEvent.class, textureCreatedParam);
// also update the framebuffer override if present
IDhApiFramebuffer framebufferOverride = OverrideInjector.INSTANCE.get(IDhApiFramebuffer.class);
this.depthTexture = new DHDepthTexture(this.textureWidth, this.textureHeight, EDhDepthBufferFormat.DEPTH32F);
this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil());
if (framebufferOverride != null)
{
framebufferOverride.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil());
}
// if we are using MC's frame buffer, a color texture is already present and shouldn't need to be bound
if (!this.usingMcFramebuffer)
{
this.nullableColorTexture = DhColorTexture.builder().setDimensions(this.textureWidth, this.textureHeight)
.setInternalFormat(EDhInternalTextureFormat.RGBA8)
.setPixelType(EDhPixelType.UNSIGNED_BYTE)
.setPixelFormat(EDhPixelFormat.RGBA)
.build();
this.framebuffer.addColorAttachment(0, this.nullableColorTexture.getTextureId());
if (framebufferOverride != null)
{
framebufferOverride.addColorAttachment(0, this.nullableColorTexture.getTextureId());
}
}
else
{
this.nullableColorTexture = null;
}
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterColorDepthTextureCreatedEvent.class, textureCreatedParam);
}
//endregion
//===============//
// LOD rendering //
//===============//
//region
private void renderLodPass(IDhApiShaderProgram shaderProgram, RenderBufferHandler lodBufferHandler, RenderParams renderEventParam, boolean opaquePass)
{
//=======================//
// debug wireframe setup //
//=======================//
boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get();
if (renderWireframe)
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
GLMC.disableFaceCulling();
}
else
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
if (!opaquePass)
{
GLMC.enableBlend();
GLMC.enableDepthTest();
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);
}
else
{
GLMC.disableBlend();
}
//===========//
// rendering //
//===========//
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
if (IRIS_ACCESSOR != null)
{
// done to fix a bug with Iris where face culling isn't properly set or reverted in the MC state manager
// which causes Sodium to render some water chunks with their normal inverted
// https://github.com/IrisShaders/Iris/issues/2582
// https://github.com/IrisShaders/Iris/blob/1.21.9/common/src/main/java/net/irisshaders/iris/compat/dh/LodRendererEvents.java#L346
GLMC.enableFaceCulling();
}
//if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
//{
// // Normal LOD rendering
//
// SortedArraySet<LodBufferContainer> lodBufferContainer = lodBufferHandler.getColumnRenderBuffers();
// if (lodBufferContainer != null)
// {
// for (int lodIndex = 0; lodIndex < lodBufferContainer.size(); lodIndex++)
// {
// LodBufferContainer bufferContainer = lodBufferContainer.get(lodIndex);
// this.setShaderProgramMvmOffset(bufferContainer.minCornerBlockPos, shaderProgram, renderEventParam);
//
// GLVertexBuffer[] vbos = opaquePass ? bufferContainer.vbos : bufferContainer.vbosTransparent;
// for (int vboIndex = 0; vboIndex < vbos.length; vboIndex++)
// {
// GLVertexBuffer vbo = vbos[vboIndex];
// if (vbo == null)
// {
// continue;
// }
//
// if (vbo.getVertexCount() == 0)
// {
// continue;
// }
//
// vbo.bind();
// shaderProgram.bindVertexBuffer(vbo.getId());
// GL32.glDrawElements(
// GL32.GL_TRIANGLES,
// vbo.getVertexCount(),
// this.quadIBO.getType(), 0);
// vbo.unbind();
// }
// }
// }
//}
//else
{
// basic quad rendering
IMcTestRenderer testRenderer = SingletonInjector.INSTANCE.get(IMcTestRenderer.class);
testRenderer.render();
//TestRenderer.INSTANCE.render();
//McTestRenderer.INSTANCE.render();
}
//=========================//
// debug wireframe cleanup //
//=========================//
if (renderWireframe)
{
// default back to GL_FILL since all other rendering uses it
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
}
/**
* the MVM offset is needed so LODs can be rendered anywhere in the MC world
* without running into floating point percision loss.
*/
private void setShaderProgramMvmOffset(DhBlockPos pos, IDhApiShaderProgram shaderProgram, RenderParams renderEventParam) throws IllegalStateException
{
Vec3d camPos = renderEventParam.exactCameraPosition;
Vec3f modelPos = new Vec3f(
(float) (pos.getX() - camPos.x),
(float) (pos.getY() - camPos.y),
(float) (pos.getZ() - camPos.z));
shaderProgram.bind();
shaderProgram.setModelOffsetPos(modelPos);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos));
}
//endregion
//===============//
// API functions //
//===============//
//region
/** @return -1 if no frame buffer has been bound yet */
public int getActiveFramebufferId() { return this.activeFramebufferId; }
/** @return -1 if no texture has been bound yet */
public int getActiveColorTextureId() { return this.activeColorTextureId; }
/** @return -1 if no texture has been bound yet */
public int getActiveDepthTextureId() { return this.activeDepthTextureId; }
//endregion
}
@@ -1,223 +0,0 @@
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.world.IDhClientWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcGenericRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
/**
* An extension of {@link DhApiRenderParam}
* that allows additional validation and putting all
* rendering variables in a single place.
*/
public class RenderParams extends DhApiRenderParam
{
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 long TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS = 10_000;
private static boolean initialLoadingComplete = false;
public IDhClientWorld dhClientWorld;
public IDhClientLevel dhClientLevel;
/** more specific override of the API value {@link DhApiRenderParam#clientLevelWrapper} */
public IClientLevelWrapper clientLevelWrapper;
public ILightMapWrapper lightmap;
public RenderBufferHandler renderBufferHandler;
public IMcGenericRenderer genericRenderer;
public Vec3d exactCameraPosition;
/** @see DhRenderState#vanillaFogEnabled */
public boolean vanillaFogEnabled;
public boolean validationRun = false;
//=============//
// constructor //
//=============//
//region
public RenderParams(EDhApiRenderPass renderPass, DhRenderState renderState)
{
this(renderPass,
renderState.partialTickTime,
renderState.mcProjectionMatrix, renderState.mcModelViewMatrix,
renderState.clientLevelWrapper,
renderState.vanillaFogEnabled
);
}
private RenderParams(
EDhApiRenderPass renderPass,
float newPartialTicks,
Mat4f newMcProjectionMatrix, Mat4f newMcModelViewMatrix,
IClientLevelWrapper clientLevelWrapper,
boolean vanillaFogEnabled
)
{
super(renderPass,
newPartialTicks,
RenderUtil.getNearClipPlaneInBlocks(), RenderUtil.getFarClipPlaneDistanceInBlocks(),
newMcProjectionMatrix, newMcModelViewMatrix,
RenderUtil.createLodProjectionMatrix(newMcProjectionMatrix), RenderUtil.createLodModelViewMatrix(newMcModelViewMatrix),
clientLevelWrapper.getMinHeight(),
clientLevelWrapper);
this.dhClientWorld = SharedApi.tryGetDhClientWorld();
if (this.dhClientWorld != null)
{
this.dhClientLevel = (IDhClientLevel) this.dhClientWorld.getLevel(clientLevelWrapper);
if (this.dhClientLevel != null)
{
this.renderBufferHandler = this.dhClientLevel.getRenderBufferHandler();
this.genericRenderer = this.dhClientLevel.getGenericRenderer();
}
}
this.clientLevelWrapper = clientLevelWrapper;
this.lightmap = MC_RENDER.getLightmapWrapper(this.clientLevelWrapper);
if (MC_CLIENT.playerExists())
{
this.exactCameraPosition = MC_RENDER.getCameraExactPosition();
}
this.vanillaFogEnabled = vanillaFogEnabled;
}
//endregion
//======================//
// parameter validation //
//======================//
//region
/**
* Should be called before rendering is done.
* @return a message if LODs shouldn't be rendered, null if the LODs can render
*/
public String getValidationErrorMessage(long firstRenderTimeMs)
{
// Note: all strings here should be constants to prevent String allocations
this.validationRun = true;
if (!MC_CLIENT.playerExists())
{
return "No Player Exists";
}
if (this.dhClientWorld == null)
{
return "No DH Client World Loaded";
}
if (this.dhClientLevel == null)
{
return "No DH Client Level Loaded";
}
if (this.clientLevelWrapper == null)
{
return "No Client Level Wrapper Loaded";
}
if (this.lightmap == null)
{
return "No Lightmap Loaded";
}
if (this.renderBufferHandler == null)
{
return "No RenderBufferHandler Present";
}
if (this.genericRenderer == null)
{
return "No Generic Renderer Present";
}
if (this.dhModelViewMatrix == null
|| this.mcModelViewMatrix == null)
{
return "No MVM or Proj Matrix Given";
}
if (AbstractOptifineAccessor.optifinePresent()
&& MC_RENDER.getTargetFramebuffer() == -1)
{
// wait for MC to finish setting up their renderer
return "Optifine Target Frame Buffer not set";
}
// potential fix for a segfault when
// Sodium and DH are running together
if (EPlatform.get() == EPlatform.MACOS
&& !initialLoadingComplete)
{
// Once MC starts rendering, wait a few seconds so
// MC/Sodium can finish their shader compiling before DH does its own.
// This will allow DH to compile its own shaders after Sodium finishes
// compiling its own.
long nowMs = System.currentTimeMillis();
long firstAllowedRenderTimeMs = firstRenderTimeMs + TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS;
if (nowMs < firstAllowedRenderTimeMs)
{
return "Waiting for initial MC compile...";
}
// null shouldn't happen, but just in case
PriorityTaskPicker.Executor renderLoadExecutor = ThreadPoolUtil.getRenderLoadingExecutor();
if (renderLoadExecutor == null)
{
return "Waiting for DH Threadpool...";
}
// wait for DH to finish loading, by the time that's done
// java should have finished all of DH's JIT compiling,
// which will hopefully mean less concurrency and thus a lower
// chance of breaking
// (plus this gives Sodium/vanill a bit longer to finish their setup)
int taskCount = renderLoadExecutor.getQueueSize();
if (taskCount > 0)
{
return "Waiting for DH JIT compiling...";
}
initialLoadingComplete = true;
}
return null;
}
//endregion
}
@@ -1,142 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOApplyShader;
import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOShader;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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}. <br><br>
*
* {@link SSAOShader} - draws the SSAO to a texture. <br>
* {@link SSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer. <br>
*/
public class SSAORenderer
{
public static SSAORenderer INSTANCE = new SSAORenderer();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,98 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* 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 GLVertexBuffer boxBuffer;
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);
}
}
@@ -1,142 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
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.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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
*/
@Deprecated
public class TestRenderer
{
public static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
public static final TestRenderer INSTANCE = new TestRenderer();
// Render a square with uv color
private static final float[] VERTICES = {
// PosX,Y, ColorR,G,B,A
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
0.4f, -0.4f, 1.0f, 0.0f, 0.0f, 1.0f,
0.3f, 0.3f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.2f, 0.2f, 0.0f, 1.0f, 1.0f, 1.0f
};
ShaderProgram basicShader;
GLVertexBuffer vbo;
AbstractVertexAttribute va;
boolean init = false;
//=============//
// constructor //
//=============//
//region
private TestRenderer() { }
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
}
@@ -1,187 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.renderer.shaders.DhFadeShader;
import com.seibel.distanthorizons.core.render.renderer.shaders.FadeApplyShader;
import com.seibel.distanthorizons.core.render.renderer.shaders.VanillaFadeShader;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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}. <br><br>
*
* {@link VanillaFadeShader} - draws the Fade to a texture. <br>
* {@link FadeApplyShader} - draws the Fade texture to MC's FrameBuffer. <br>
*/
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,331 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcGenericRenderer;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Predicate;
public class BeaconRenderHandler
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
/** how often should we check if a beacon should be culled? */
private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000;
private static final Comparator<BeaconBeamDTO> NEGATIVE_BLOCKPOS_COMPARATOR = new NegativeInfiniteBlockPosComparator();
private final ReentrantLock updateLock = new ReentrantLock();
/** only contains the beacons currently being rendered (culled beacons will be missing) */
private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup;
/** contains all beacons that could be rendered (including those that are being culled) */
private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
/** contains all beacons that could be rendered */
private final HashSet<DhBlockPos> fullBeaconBlockPosSet = new HashSet<>();
private boolean cullingThreadRunning = false;
private boolean updateRenderDataNextFrame = false;
//=============//
// constructor //
//=============//
//region
public BeaconRenderHandler(@NotNull IMcGenericRenderer renderer)
{
this.activeBeaconBoxRenderGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0));
this.activeBeaconBoxRenderGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.activeBeaconBoxRenderGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
this.activeBeaconBoxRenderGroup.setSsaoEnabled(false);
this.activeBeaconBoxRenderGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded());
this.activeBeaconBoxRenderGroup.setPreRenderFunc(this::beforeRender);
renderer.add(this.activeBeaconBoxRenderGroup);
}
//endregion
//=================//
// render handling //
//=================//
//region
public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel)
{
try
{
this.updateLock.lock();
// how wide should each beacon be?
int beaconBlockWidth = 1;
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
{
beaconBlockWidth = DhSectionPos.getBlockWidth(detailLevel);
}
ArrayList<BeaconBeamDTO> sortedBeaconList = new ArrayList<>(beaconList);
// merge distant beams if requested
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
{
// sort beacons from neg inf -> pos inf
// so we can consistently merge adjacent beacons
sortedBeaconList.sort(NEGATIVE_BLOCKPOS_COMPARATOR);
// go through each beacon...
for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++)
{
BeaconBeamDTO outerBeacon = sortedBeaconList.get(outerIndex);
DhBlockPos outerBlockPos = outerBeacon.blockPos;
// ...and remove any beacons that are within the block width to prevent overlaps
for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++)
{
BeaconBeamDTO beaconToMerge = sortedBeaconList.get(mergeIndex);
DhBlockPos mergeBlockPos = beaconToMerge.blockPos;
int xDiff = mergeBlockPos.getX() - outerBlockPos.getX();
int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ();
// merge (remove) this beacon if
// it's close to the outer beacon
if (xDiff < beaconBlockWidth
&& zDiff < beaconBlockWidth)
{
sortedBeaconList.remove(mergeIndex);
mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top
}
}
}
}
//LOGGER.info("startRenderingBeacons ["+sortedBeaconList+"]");
// add each beacon to the renderer
for (int i = 0; i < sortedBeaconList.size(); i++)
{
BeaconBeamDTO beacon = sortedBeaconList.get(i);
if (!this.fullBeaconBlockPosSet.add(beacon.blockPos))
{
// skip already present beacons
continue;
}
int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get();
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
new DhApiVec3d(beacon.blockPos.getX() + beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beaconBlockWidth),
beacon.color,
EDhApiBlockMaterial.ILLUMINATED
);
this.activeBeaconBoxRenderGroup.add(beaconBox);
this.fullBeaconBoxList.add(beaconBox);
this.activeBeaconBoxRenderGroup.triggerBoxChange();
}
}
finally
{
this.updateLock.unlock();
}
}
public void stopRenderingBeaconsInRange(long pos)
{
try
{
this.updateLock.lock();
Predicate<DhApiRenderableBox> removeBoxPredicate = (DhApiRenderableBox box) ->
{
DhBlockPos blockPos = new DhBlockPos((int)box.minPos.x, (int)box.minPos.y, (int)box.minPos.z);
boolean contains = DhSectionPos.contains(pos, blockPos);
//if (contains)
//{
// LOGGER.info("stopRenderingBeaconsInRange ["+DhSectionPos.toString(pos)+"] ["+blockPos+"]");
//}
return contains;
};
this.activeBeaconBoxRenderGroup.removeIf(removeBoxPredicate);
this.fullBeaconBoxList.removeIf(removeBoxPredicate);
this.fullBeaconBlockPosSet.removeIf((DhBlockPos blockPos) -> DhSectionPos.contains(pos, blockPos));
this.activeBeaconBoxRenderGroup.triggerBoxChange();
}
finally
{
this.updateLock.unlock();
}
}
private void beforeRender(DhApiRenderParam renderEventParam)
{
if (Config.Client.Advanced.Graphics.Culling.disableBeaconDistanceCulling.get())
{
// this could be called only when the player moves, but it's an extremely cheap check,
// so there isn't much of a reason to bother
this.tryUpdateBeaconCullingAsync();
}
// this must be called on the render thread to prevent concurrency issues
if (this.updateRenderDataNextFrame)
{
this.activeBeaconBoxRenderGroup.triggerBoxChange();
this.updateRenderDataNextFrame = false;
}
this.activeBeaconBoxRenderGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get());
}
/** does nothing if the culling thread is already running */
private void tryUpdateBeaconCullingAsync()
{
ThreadPoolExecutor executor = ThreadPoolUtil.getBeaconCullingExecutor();
if (executor != null
&& !this.cullingThreadRunning)
{
this.cullingThreadRunning = true;
try
{
executor.execute(() ->
{
try
{
Thread.sleep(MAX_CULLING_FREQUENCY_IN_MS);
}
catch (InterruptedException ignore) { }
try
{
// lock to make sure we don't try adding beacons to the arrays while processing them
this.updateLock.lock();
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
// fading by the overdraw prevention amount helps reduce beacons from rendering strangely
// on the border of DH's render distance
float dhFadeDistance = RenderUtil.getNearClipPlaneInBlocks();
// Clear the existing box group so we can re-populate it.
// Since the box group is only used when we trigger an update, clearing it here
// and repopulating it is fine.
this.activeBeaconBoxRenderGroup.clear();
// While iterating over every beacon isn't a great way of doing this,
// when 940 beacons were tested this only took ~0.9 Milliseconds, so as long as
// we aren't freezing the render thread this method of culling works just fine.
for (DhApiRenderableBox box : this.fullBeaconBoxList)
{
// if a beacon is outside the vanilla render distance render it
double distance = Vec3d.getHorizontalDistance(cameraPos, box.minPos);
if (distance > dhFadeDistance)
{
this.activeBeaconBoxRenderGroup.add(box);
}
}
this.updateRenderDataNextFrame = true;
}
catch (Exception e)
{
LOGGER.error("Unexpected issue while updating beacon culling. Error: " + e.getMessage(), e);
}
finally
{
this.updateLock.unlock();
this.cullingThreadRunning = false;
}
});
}
catch (RejectedExecutionException ignore)
{ /* If this happens that means everything is already shut down and no culling is necessary */ }
}
}
//endregion
//================//
// helper classes //
//================//
//region
private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO>
{
@Override
public int compare(BeaconBeamDTO beacon1, BeaconBeamDTO beacon2)
{
DhBlockPos blockPos1 = beacon1.blockPos;
DhBlockPos blockPos2 = beacon2.blockPos;
// sort by X, then by Z
if (blockPos1.getX() != blockPos2.getX())
{
return Integer.compare(blockPos1.getX(), blockPos2.getX());
}
return Integer.compare(blockPos1.getZ(), blockPos2.getZ());
}
}
//endregion
}
@@ -1,617 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcGenericRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class CloudRenderHandler
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final String CLOUD_RESOURCE_TEXTURE_PATH = "assets/distanthorizons/textures/clouds.png";
private static final boolean DEBUG_BORDER_COLORS = false;
/**
* How wide an individual box is. <br>
* Measured in blocks.
*/
private static final int CLOUD_BOX_WIDTH = 128;
/** measured in blocks */
private static final int CLOUD_BOX_THICKNESS = 32;
/**
* How many cloud groups wide can we render at maximum? <br>
* 1 = 3x3 or 9 total <br>
* 2 = 5x5 or 25 total <br> <br>
*
* 5 seems like a good count since it can cover up to around 2048 render distance.
*/
private static final int CLOUD_INSTANCE_RADIUS_COUNT = 5;
private static final float MOVE_SPEED_IN_BLOCKS_PER_SECOND = 6.0f;
private final IDhApiRenderableBoxGroup[][] boxGroupByOffset
// radius * 2 to get the diameter
// + 1 so we get an odd number wide (needed so we can have a center position)
= new IDhApiRenderableBoxGroup[(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1][(CLOUD_INSTANCE_RADIUS_COUNT * 2) + 1];
private final IDhClientLevel level;
private final IMcGenericRenderer renderer;
/** cached array so we don't need to re-create it each frame for each cloud group */
private final Vec3d[] cullingCorners = new Vec3d[]
{
// the values of each will be overwritten during the culling pass
new Vec3d(),
new Vec3d(),
new Vec3d(),
new Vec3d(),
};
private boolean disabledWarningLogged = false;
//=============//
// constructor //
//=============//
//region
public CloudRenderHandler(IDhClientLevel level, IMcGenericRenderer renderer)
{
this.level = level;
this.renderer = renderer;
//=======================//
// get the cloud texture //
//=======================//
//region
// default to a single empty slot in case the texture is broken
boolean[][] cloudLocations = new boolean[1][1];
try
{
cloudLocations = getCloudsFromTexture();
}
catch (FileNotFoundException e)
{
LOGGER.error(e.getMessage(), e);
}
catch (IOException e)
{
LOGGER.error("Unexpected issue getting cloud texture, error: ["+e.getMessage()+"].", e);
}
if (cloudLocations.length != 0 &&
cloudLocations.length != cloudLocations[0].length)
{
LOGGER.warn("Non-square cloud texture found, some parts of the texture will be clipped off.");
}
//endregion
//===================//
// parse the texture //
//===================//
//region
int textureWidth = cloudLocations.length;
ArrayList<DhApiRenderableBox> boxList = new ArrayList<>(512);
for (int x = 0; x < textureWidth; x ++)
{
for (int z = 0; z < textureWidth; z ++)
{
if (cloudLocations[x][z])
{
// start a new box in Z direction
int startZ = z;
int startX = x;
int endZ = startZ;
int endX = x+1;
//==========================//
// merge in the Z direction //
//==========================//
// Find the cloud's length in the Z direction
while (endZ < textureWidth
&& cloudLocations[x][endZ])
{
endZ++;
}
// update the z iterator so we can skip over everything included in this cloud
z = endZ - 1;
//==========================//
// merge in the X direction //
//==========================//
for (int currentX = startX + 1; currentX < textureWidth; currentX++)
{
boolean canMergeInXDir = true;
// check if all locations in this column are true
for (int adjacentZ = startZ; adjacentZ < endZ; adjacentZ++)
{
if (!cloudLocations[currentX][adjacentZ])
{
// at least one pixel in the texture is false,
// so we can't merge in this direction
canMergeInXDir = false;
break;
}
}
if (canMergeInXDir)
{
// mark the adjacent column as processed
for (int currentZ = startZ; currentZ < endZ; currentZ++)
{
// by flipping all the pixels in the adjacent column to false,
// we don't have to worry about adding another cloud
cloudLocations[currentX][currentZ] = false;
}
endX = (currentX + 1);
}
else
{
break;
}
}
//============================//
// Create the renderable box //
//============================//
// endZ contains the last cloud index
// so the cloud now goes from startZ to endZ (inclusive)
int minXBlockPos = startX * CLOUD_BOX_WIDTH;
int minZBlockPos = startZ * CLOUD_BOX_WIDTH;
int maxXBlockPos = endX * CLOUD_BOX_WIDTH;
int maxZBlockPos = endZ * CLOUD_BOX_WIDTH;
// this color is changed at render time based on the level time
Color color = new Color(255,255,255,255);
if (DEBUG_BORDER_COLORS)
{
// equals is included so the boarder is 2 blocks wide, making it easier to see
if (x <= 1) { color = Color.RED; }
else if (x >= textureWidth - 2) { color = Color.GREEN; }
if (z <= 1) { color = Color.BLUE; }
else if (z >= textureWidth - 2) { color = Color.BLACK; }
}
DhApiRenderableBox box = new DhApiRenderableBox(
new DhApiVec3d(minXBlockPos, 0, minZBlockPos),
new DhApiVec3d(maxXBlockPos, CLOUD_BOX_THICKNESS, maxZBlockPos),
color,
EDhApiBlockMaterial.UNKNOWN
);
boxList.add(box);
}
}
}
//endregion
//========================//
// create the renderables //
//========================//
//region
// slightly lighter shading than the default
DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded();
cloudShading.north = cloudShading.south = 0.9f;
cloudShading.east = cloudShading.west = 0.8f;
cloudShading.top = 1.0f;
cloudShading.bottom = 0.7f;
for (int x = -CLOUD_INSTANCE_RADIUS_COUNT; x <= CLOUD_INSTANCE_RADIUS_COUNT; x++)
{
for (int z = -CLOUD_INSTANCE_RADIUS_COUNT; z <= CLOUD_INSTANCE_RADIUS_COUNT; z++)
{
IDhApiRenderableBoxGroup boxGroup = GenericRenderObjectFactory.INSTANCE.createRelativePositionedGroup(
ModInfo.NAME + ":Clouds",
new DhApiVec3d(0, 0, 0), // the offset will be set during rendering
boxList);
// since cloud colors are set by the level based on the time of day lighting should affect it
boxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
boxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
boxGroup.setSsaoEnabled(false);
boxGroup.setShading(cloudShading);
CloudParams cloudParams = new CloudParams(textureWidth, x, z);
boxGroup.setPreRenderFunc((renderParam) -> this.preRender(renderParam, cloudParams));
renderer.add(boxGroup);
this.boxGroupByOffset[x+CLOUD_INSTANCE_RADIUS_COUNT][z+CLOUD_INSTANCE_RADIUS_COUNT] = boxGroup;
}
}
}
//endregion
//===========//
// rendering //
//===========//
//region
private void preRender(DhApiRenderParam renderParam, CloudParams cloudParams)
{
IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[cloudParams.instanceOffsetX+CLOUD_INSTANCE_RADIUS_COUNT][cloudParams.instanceOffsetZ+CLOUD_INSTANCE_RADIUS_COUNT];
//===================//
// should we render? //
//===================//
boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get();
boxGroup.setActive(renderClouds);
if(!renderClouds)
{
return;
}
//if (!this.renderer.getInstancedRenderingAvailable())
//{
// if (!this.disabledWarningLogged)
// {
// this.disabledWarningLogged = true;
// LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled.");
// }
// boxGroup.setActive(false);
// return;
//}
IClientLevelWrapper clientLevelWrapper = this.level.getClientLevelWrapper();
if (clientLevelWrapper == null)
{
return;
}
//================//
// cloud movement //
//================//
long currentTime = System.currentTimeMillis();
float deltaTime = (currentTime - cloudParams.lastFrameTime) / 1000.0f; // Delta time in seconds
cloudParams.lastFrameTime = currentTime;
float deltaX = MOVE_SPEED_IN_BLOCKS_PER_SECOND * deltaTime;
// negative delta is to match vanilla's cloud movement
cloudParams.deltaOffsetX -= deltaX;
// wrap the cloud around after reaching the edge
cloudParams.deltaOffsetX %= cloudParams.widthInBlocks;
//============================//
// camera movement and offset //
//============================//
// camera position
int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x;
int cameraPosZ = (int)MC_RENDER.getCameraExactPosition().z;
// offset the camera position by negative 1 width when below zero to fix off-by-one errors in the negative direction
if (cameraPosX < 0) { cameraPosX -= cloudParams.widthInBlocks; }
if (cameraPosZ < 0) { cameraPosZ -= cloudParams.widthInBlocks; }
// determine how many cloud instances away from the origin we are
int cloudInstanceOffsetCountX = (cameraPosX / cloudParams.widthInBlocks);
int cloudInstanceOffsetCountZ = (cameraPosZ / cloudParams.widthInBlocks);
// calculate the new offset
float instanceOffsetX = (cloudInstanceOffsetCountX * cloudParams.widthInBlocks);
float instanceOffsetZ = (cloudInstanceOffsetCountZ * cloudParams.widthInBlocks);
float newMinPosX =
cloudParams.deltaOffsetX
+ (cloudParams.instanceOffsetX * cloudParams.widthInBlocks)
+ instanceOffsetX + cloudParams.halfWidthInBlocks;
float newMinPosY = this.level.getLevelWrapper().getMaxHeight() + 200;
float newMinPosZ = cloudParams.deltaOffsetZ
+ (cloudParams.instanceOffsetZ * cloudParams.widthInBlocks)
+ instanceOffsetZ + cloudParams.halfWidthInBlocks;
boolean cullCloud = this.shouldCloudBeCulled(
newMinPosX, newMinPosY, newMinPosZ,
cloudParams
);
if(cullCloud)
{
boxGroup.setActive(false);
}
//===========================//
// update color and position //
//===========================//
// if debug colors are enabled don't change them
if (!DEBUG_BORDER_COLORS
// don't modify cloud groups that aren't active
&& boxGroup.isActive())
{
// cloud color changes based on the time of day and weather so we need to get it from the level
Color newCloudColor = clientLevelWrapper.getCloudColor(renderParam.partialTicks);
// all boxes should have the same color, so we can get their current color
// via the first box
DhApiRenderableBox firstBox = boxGroup.get(0);
Color currentBoxColor = firstBox.color;
// update the boxes if their color should be changed
if (!newCloudColor.equals(currentBoxColor))
{
// Note: cloud instances may share boxes
// because of that this method may only need to be called once per all clouds
for (DhApiRenderableBox box : boxGroup)
{
box.color = newCloudColor;
}
}
// trigger an update if this cloud section has a different color
if (!cloudParams.previousColor.equals(newCloudColor))
{
cloudParams.previousColor = newCloudColor;
boxGroup.triggerBoxChange();
}
}
boxGroup.setOriginBlockPos(new DhApiVec3d(newMinPosX, newMinPosY, newMinPosZ));
}
private boolean shouldCloudBeCulled(
float minPosX, float minPosY, float minPosZ,
CloudParams cloudParams)
{
//========================//
// skip center 3x3 clouds //
//========================//
// always render the center 3x3 clouds, otherwise we may see
// an un-rendered border
if (cloudParams.instanceOffsetX >= -1 && cloudParams.instanceOffsetX <= 1
&& cloudParams.instanceOffsetZ >= -1 && cloudParams.instanceOffsetZ <= 1)
{
return false;
}
//==============//
// culling prep //
//==============//
// we need all 4 corners since we want to draw any clouds that
// could potentially be within render distance
this.cullingCorners[0].x = minPosX;
this.cullingCorners[0].y = minPosY;
this.cullingCorners[0].z = minPosZ;
this.cullingCorners[1].x = minPosX;
this.cullingCorners[1].y = minPosY;
this.cullingCorners[1].z = minPosZ + cloudParams.widthInBlocks;
this.cullingCorners[2].x = minPosX + cloudParams.widthInBlocks;
this.cullingCorners[2].y = minPosY;
this.cullingCorners[2].z = minPosZ;
this.cullingCorners[3].x = minPosX + cloudParams.widthInBlocks;
this.cullingCorners[3].y = minPosY;
this.cullingCorners[3].z = minPosZ + cloudParams.widthInBlocks;
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
Vec3f cameraLookAtVector = MC_RENDER.getLookAtVector();
cameraLookAtVector.normalize();
double renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()
// * 1.5 is so we have a little extra buffer where clouds will render further than
// necessary to prevent seeing the cloud border
* LodUtil.CHUNK_WIDTH * 1.5;
//===================//
// check each corner //
//===================//
boolean allOutsideRenderDistance = true;
boolean allBehindCamera = true;
for (Vec3d corner : this.cullingCorners)
{
// Check if the corner is within the render distance
// (ignoring height, since LODs also ignore height)
Vec3d cornerNoHeight = new Vec3d(corner);
cornerNoHeight.y = 0;
Vec3d cameraPosNoHeight = new Vec3d(cameraPos);
cameraPosNoHeight.y = 0;
double cornerDistance = cornerNoHeight.getDistance(cameraPosNoHeight);
if (cornerDistance <= renderDistance)
{
allOutsideRenderDistance = false;
}
// Check if the corner is in front of the camera (dot product > 0 means in front)
Vec3f toCorner = new Vec3f(
(float) (corner.x - cameraPos.x),
(float) (corner.y - cameraPos.y),
(float) (corner.z - cameraPos.z));
toCorner.normalize();
if (cameraLookAtVector.dotProduct(toCorner) > 0)
{
allBehindCamera = false;
}
}
// Cull if all corners are either behind the camera or outside the render distance
return allOutsideRenderDistance || allBehindCamera;
}
//endregion
//==================//
// texture handling //
//==================//
//region
private static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException
{
final ClassLoader loader = CloudRenderHandler.class.getClassLoader();
boolean[][] whitePixels = null;
try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH))
{
if (imageInputStream == null)
{
throw new FileNotFoundException("Unable to find cloud texture at resource path: ["+CLOUD_RESOURCE_TEXTURE_PATH+"].");
}
BufferedImage image = ImageIO.read(imageInputStream);
int width = image.getWidth();
int height = image.getHeight();
whitePixels = new boolean[width][height];
for (int x = 0; x < width; x ++)
{
for (int z = 0; z < width; z ++)
{
Color color = new Color(image.getRGB(x,z));
whitePixels[x][z] = color.equals(Color.WHITE);
}
}
}
return whitePixels;
}
//endregion
//================//
// helper classes //
//================//
//region
private static class CloudParams
{
public final int textureWidth;
public final int widthInBlocks;
public final int halfWidthInBlocks;
public final int instanceOffsetX;
public final int instanceOffsetZ;
/** how far this cloud group has moved in the X direction based on time */
public float deltaOffsetX = 0;
/** how far this cloud group has moved in the Z direction based on time */
public float deltaOffsetZ = 0;
public long lastFrameTime = System.currentTimeMillis();
/** used so we can trigger a VBO update when necessary */
public Color previousColor = Color.WHITE;
// constructor //
public CloudParams(int textureWidth, int instanceOffsetX, int instanceOffsetZ)
{
this.textureWidth = textureWidth;
this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH);
this.halfWidthInBlocks = this.widthInBlocks / 2;
this.instanceOffsetX = instanceOffsetX;
this.instanceOffsetZ = instanceOffsetZ;
}
}
//endregion
}
@@ -1,735 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLElementBuffer;
import com.seibel.distanthorizons.core.render.renderer.RenderParams;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.IMcGenericRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL33;
import org.lwjgl.system.MemoryUtil;
import java.awt.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Handles rendering generic groups of {@link DhApiRenderableBox}.
*
* @see IDhApiCustomRenderRegister
* @see DhApiRenderableBox
*/
public class GenericObjectRenderer implements IMcGenericRenderer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final ISodiumAccessor SODIUM = ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final DhApiRenderableBoxGroupShading DEFAULT_SHADING = DhApiRenderableBoxGroupShading.getUnshaded();
/**
* Can be used to troubleshoot the renderer.
* If enabled several debug objects will render around (0,150,0).
*/
public static final boolean RENDER_DEBUG_OBJECTS = false;
// rendering setup
private boolean init = false;
private IDhApiGenericObjectShaderProgram instancedShaderProgram;
private IDhApiGenericObjectShaderProgram directShaderProgram;
//private GLVertexBuffer boxVertexBuffer;
private GLElementBuffer boxIndexBuffer;
private boolean instancedRenderingAvailable;
private boolean vertexAttribDivisorSupported;
private boolean instancedArraysSupported;
private final ConcurrentHashMap<Long, RenderableBoxGroup> boxGroupById = new ConcurrentHashMap<>();
/** A box from 0,0,0 to 1,1,1 */
private static final float[] BOX_VERTICES = {
//region
// Pos x y z
// min X, vertical face
0, 0, 0,
1, 0, 0,
1, 1, 0,
0, 1, 0,
// max X, vertical face
0, 1, 1,
1, 1, 1,
1, 0, 1,
0, 0, 1,
// min Z, vertical face
0, 0, 1,
0, 0, 0,
0, 1, 0,
0, 1, 1,
// max Z, vertical face
1, 0, 1,
1, 1, 1,
1, 1, 0,
1, 0, 0,
// min Y, horizontal face
0, 0, 1,
1, 0, 1,
1, 0, 0,
0, 0, 0,
// max Y, horizontal face
0, 1, 1,
1, 1, 1,
1, 1, 0,
0, 1, 0,
//endregion
};
private static final int[] BOX_INDICES = {
//region
// min X, vertical face
2, 1, 0,
0, 3, 2,
// max X, vertical face
6, 5, 4,
4, 7, 6,
// min Z, vertical face
10, 9, 8,
8, 11, 10,
// max Z, vertical face
14, 13, 12,
12, 15, 14,
// min Y, horizontal face
18, 17, 16,
16, 19, 18,
// max Y, horizontal face
20, 21, 22,
22, 23, 20,
//endregion
};
//=============//
// constructor //
//=============//
//region
public GenericObjectRenderer() { }
public void init()
{
if (this.init)
{
return;
}
this.init = true;
//===================================//
// is instanced rendering available? //
//===================================//
this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported;
this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported;
boolean isMac = (EPlatform.get() == EPlatform.MACOS);
this.instancedRenderingAvailable = (this.vertexAttribDivisorSupported || this.instancedArraysSupported) && !isMac;
if (!this.instancedRenderingAvailable)
{
LOGGER.warn("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering will be slow and some effects may be disabled.");
}
//======================//
// startup the renderer //
//======================//
this.instancedShaderProgram = new GenericObjectShaderProgram(true);
this.directShaderProgram = new GenericObjectShaderProgram(false);
this.createBuffers();
if (RENDER_DEBUG_OBJECTS)
{
this.addGenericDebugObjects();
}
}
private void createBuffers()
{
// box vertices
ByteBuffer boxVerticesBuffer = MemoryUtil.memAlloc(BOX_VERTICES.length * Float.BYTES);
boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES);
boxVerticesBuffer.rewind();
//this.boxVertexBuffer = new GLVertexBuffer(false);
//this.boxVertexBuffer.bind();
//this.boxVertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES);
MemoryUtil.memFree(boxVerticesBuffer);
// box vertex indexes
ByteBuffer solidIndexBuffer = MemoryUtil.memAlloc(BOX_INDICES.length * Integer.BYTES);
solidIndexBuffer.asIntBuffer().put(BOX_INDICES);
solidIndexBuffer.rewind();
this.boxIndexBuffer = new GLElementBuffer(false);
this.boxIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
this.boxIndexBuffer.bind();
MemoryUtil.memFree(solidIndexBuffer);
}
private void addGenericDebugObjects()
{
GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE;
// single giant box
IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox(
ModInfo.NAME + ":CyanChunkBox",
new DhApiRenderableBox(
new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16),
new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125),
EDhApiBlockMaterial.WATER)
);
singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.add(singleGiantBoxGroup);
// single slender box
IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox(
ModInfo.NAME + ":GreenBeacon",
new DhApiRenderableBox(
new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32),
new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125),
EDhApiBlockMaterial.ILLUMINATED)
);
singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.add(singleTallBoxGroup);
// absolute box group
ArrayList<DhApiRenderableBox> absBoxList = new ArrayList<>();
for (int i = 0; i < 18; i++)
{
absBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25),
new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()),
EDhApiBlockMaterial.LAVA
)
);
}
IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList);
this.add(absolutePosBoxGroup);
// relative box group
ArrayList<DhApiRenderableBox> relBoxList = new ArrayList<>();
for (int i = 0; i < 8; i+=2)
{
relBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1),
new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()),
EDhApiBlockMaterial.METAL
)
);
}
IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup(
ModInfo.NAME + ":MovingMagentaGroup",
new DhApiVec3d(24, 140, 24),
relBoxList);
relativePosBoxGroup.setPreRenderFunc((event) ->
{
DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos();
pos.x += event.partialTicks / 2;
pos.x %= 32;
relativePosBoxGroup.setOriginBlockPos(pos);
});
this.add(relativePosBoxGroup);
// massive relative box group
ArrayList<DhApiRenderableBox> massRelBoxList = new ArrayList<>();
for (int x = 0; x < 50*2; x+=2)
{
for (int z = 0; z < 50*2; z+=2)
{
massRelBoxList.add(new DhApiRenderableBox(
new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z),
new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()),
EDhApiBlockMaterial.TERRACOTTA
)
);
}
}
IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup(
ModInfo.NAME + ":MassRedGroup",
new DhApiVec3d(-25, 140, 0),
massRelBoxList);
massRelativePosBoxGroup.setPreRenderFunc((event) ->
{
DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos();
blockPos.y += event.partialTicks / 4;
if (blockPos.y > 150f)
{
blockPos.y = 140f;
Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED;
massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; });
massRelativePosBoxGroup.triggerBoxChange();
}
massRelativePosBoxGroup.setOriginBlockPos(blockPos);
});
this.add(massRelativePosBoxGroup);
}
//endregion
//==============//
// registration //
//==============//
//region
@Override
public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException
{
if (!(iBoxGroup instanceof RenderableBoxGroup))
{
throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"].");
}
RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup;
long id = boxGroup.getId();
if (this.boxGroupById.containsKey(id))
{
throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present.");
}
this.boxGroupById.put(id, boxGroup);
}
@Override
public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); }
public void clear() { this.boxGroupById.clear(); }
//endregion
//===========//
// rendering //
//===========//
//region
/**
* @param renderingWithSsao
* if true that means this render call is happening before the SSAO pass
* and any objects rendered in this pass will have SSAO applied to them.
*/
@Override
public void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao)
{
// render setup //
profiler.push("setup");
this.init();
boolean useInstancedRendering = this.instancedRenderingAvailable
&& Config.Client.Advanced.Graphics.GenericRendering.enableInstancedRendering.get();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam);
boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get();
if (renderWireframe)
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
GLMC.disableFaceCulling();
}
else
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
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);
IDhApiGenericObjectShaderProgram shaderProgram = useInstancedRendering ? this.instancedShaderProgram : this.directShaderProgram;
IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class);
if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
{
shaderProgram = shaderProgramOverride;
}
shaderProgram.bind(renderEventParam);
//shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId());
this.boxIndexBuffer.bind();
Vec3d camPos = MC_RENDER.getCameraExactPosition();
// rendering //
Collection<RenderableBoxGroup> boxList = this.boxGroupById.values();
for (RenderableBoxGroup boxGroup : boxList)
{
// validation //
// shouldn't happen, but just in case
if (boxGroup == null)
{
continue;
}
// skip boxes that shouldn't render this pass
if (boxGroup.ssaoEnabled != renderingWithSsao)
{
continue;
}
profiler.popPush("render prep");
boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired
// ignore inactive groups
if (!boxGroup.active)
{
continue;
}
// allow API users to cancel this object's rendering
boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup));
if (cancelRendering)
{
continue;
}
// update instanced data if needed
if (useInstancedRendering)
{
boxGroup.tryUpdateInstancedDataAsync();
// skip groups that haven't been uploaded yet
if (boxGroup.vertexBufferContainer.getState() != NativeGlGenericObjectVertexContainer.EState.RENDER)
{
continue;
}
}
// render //
profiler.popPush("rendering");
profiler.push(boxGroup.getResourceLocationNamespace());
profiler.push(boxGroup.getResourceLocationPath());
if (useInstancedRendering)
{
this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos, profiler);
}
else
{
this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos, profiler);
}
profiler.pop(); // resource path
profiler.pop(); // resource namespace
boxGroup.postRender(renderEventParam);
}
//==========//
// clean up //
//==========//
profiler.popPush("cleanup");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam);
if (renderWireframe)
{
// default back to GL_FILL since all other rendering uses it
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GLMC.enableFaceCulling();
}
shaderProgram.unbind();
profiler.pop();
}
//endregion
//=====================//
// instanced rendering //
//=====================//
//region
private void renderBoxGroupInstanced(
IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam,
RenderableBoxGroup boxGroup, Vec3d camPos,
IProfilerWrapper profiler)
{
// update instance data //
profiler.push("vertex setup");
DhApiRenderableBoxGroupShading shading = boxGroup.shading;
if (shading == null)
{
shading = DEFAULT_SHADING;
}
shaderProgram.fillIndirectUniformData(
renderEventParam,
shading, boxGroup,
camPos);
// Bind instance data //
profiler.popPush("binding");
NativeGlGenericObjectVertexContainer container = (NativeGlGenericObjectVertexContainer)(boxGroup.vertexBufferContainer);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.color);
GL32.glEnableVertexAttribArray(1);
GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0);
this.vertexAttribDivisor(1, 1);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.scale);
GL32.glEnableVertexAttribArray(2);
this.vertexAttribDivisor(2, 1);
GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.chunkPos);
GL32.glEnableVertexAttribArray(3);
this.vertexAttribDivisor(3, 1);
GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.subChunkPos);
GL32.glEnableVertexAttribArray(4);
this.vertexAttribDivisor(4, 1);
GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.material);
GL32.glEnableVertexAttribArray(5);
this.vertexAttribDivisor(5, 1);
GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0);
// Draw instanced
profiler.popPush("render");
if (container.uploadedBoxCount > 0)
{
GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, container.uploadedBoxCount);
}
// Clean up
profiler.popPush("cleanup");
GL32.glDisableVertexAttribArray(1);
GL32.glDisableVertexAttribArray(2);
GL32.glDisableVertexAttribArray(3);
GL32.glDisableVertexAttribArray(4);
GL32.glDisableVertexAttribArray(5);
profiler.pop();
}
/**
* Clean way to handle both {@link GL33#glVertexAttribDivisor} and {@link ARBInstancedArrays#glVertexAttribDivisorARB}
* based on which one is supported.
*/
private void vertexAttribDivisor(int index, int divisor)
{
if (this.vertexAttribDivisorSupported)
{
GL33.glVertexAttribDivisor(index, divisor);
}
else if(this.instancedArraysSupported)
{
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
}
else
{
throw new IllegalStateException("Instanced rendering isn't supported by this machine. Direct rendering should have been used instead.");
}
}
//endregion
//==================//
// direct rendering //
//==================//
//region
private void renderBoxGroupDirect(
IDhApiGenericObjectShaderProgram shaderProgram,
DhApiRenderParam renderEventParam,
RenderableBoxGroup boxGroup, Vec3d camPos,
IProfilerWrapper profiler)
{
profiler.popPush("shared uniforms");
DhApiRenderableBoxGroupShading shading = boxGroup.shading;
if (shading == null)
{
shading = DhApiRenderableBoxGroupShading.getUnshaded();
}
shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos);
for (int i = 0; i < boxGroup.size(); i++)
{
try
{
DhApiRenderableBox box = boxGroup.get(i);
if (box != null)
{
profiler.popPush("direct uniforms");
shaderProgram.fillDirectUniformData(renderEventParam, boxGroup, box, camPos);
profiler.popPush("render");
GL32.glDrawElements(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0);
}
}
catch (IndexOutOfBoundsException e)
{
// Concurrency issue, the list was modified while rendering
// this can probably be ignored.
// However, if it does become a problem we can add locks to the box group.
break;
}
}
profiler.pop();
}
//endregion
//=========//
// getters //
//=========//
//region
/** @throws IllegalStateException if {@link #init()} function hasn't been called yet */
public boolean getInstancedRenderingAvailable() throws IllegalStateException
{
if (!this.init)
{
throw new IllegalStateException("GL initialization hasn't been completed.");
}
return this.instancedRenderingAvailable;
}
//endregion
//=========//
// F3 menu //
//=========//
//region
public String getVboRenderDebugMenuString()
{
// get counts
int totalGroupCount = this.boxGroupById.size();
int totalBoxCount = 0;
int activeGroupCount = 0;
int activeBoxCount = 0;
for (long key : this.boxGroupById.keySet())
{
RenderableBoxGroup renderGroup = this.boxGroupById.get(key);
if (renderGroup.active)
{
activeGroupCount++;
activeBoxCount += renderGroup.size();
}
totalBoxCount += renderGroup.size();
}
return "Generic Obj #: " + F3Screen.NUMBER_FORMAT.format(activeGroupCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalGroupCount) + ", " +
"Cube #: " + F3Screen.NUMBER_FORMAT.format(activeBoxCount) + "/" + F3Screen.NUMBER_FORMAT.format(totalBoxCount);
}
//endregion
}
@@ -1,231 +0,0 @@
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
public class GenericObjectShaderProgram extends ShaderProgram implements IDhApiGenericObjectShaderProgram
{
public static final String VERTEX_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/vert.vert";
public static final String VERTEX_SHADER_DIRECT_PATH = "shaders/genericObject/direct/vert.vert";
public static final String FRAGMENT_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/frag.frag";
public static final String FRAGMENT_SHADER_DIRECT_PATH = "shaders/genericObject/direct/frag.frag";
public final AbstractVertexAttribute va;
// shader uniforms
private final int directShaderTransformUniform;
private final int directShaderColorUniform;
private final int instancedShaderOffsetChunkUniform;
private final int instancedShaderOffsetSubChunkUniform;
private final int instancedShaderCameraChunkPosUniform;
private final int instancedShaderCameraSubChunkPosUniform;
private final int instancedShaderProjectionModelViewMatrixUniform;
private final int lightMapUniform;
private final int skyLightUniform;
private final int blockLightUniform;
private final int northShadingUniform;
private final int southShadingUniform;
private final int eastShadingUniform;
private final int westShadingUniform;
private final int topShadingUniform;
private final int bottomShadingUniform;
//=============//
// constructor //
//=============//
public GenericObjectShaderProgram(boolean useInstancedRendering)
{
super(
useInstancedRendering ? VERTEX_SHADER_INSTANCED_PATH : VERTEX_SHADER_DIRECT_PATH,
useInstancedRendering ? FRAGMENT_SHADER_INSTANCED_PATH : FRAGMENT_SHADER_DIRECT_PATH,
"vPosition"
);
this.va = AbstractVertexAttribute.create();
this.va.bind();
// Pos
this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false));
this.va.completeAndCheck(Float.BYTES * 3);
this.directShaderTransformUniform = this.tryGetUniformLocation("uTransform");
this.directShaderColorUniform = this.tryGetUniformLocation("uColor");
this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation("uOffsetChunk");
this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation("uOffsetSubChunk");
this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation("uCameraPosChunk");
this.instancedShaderCameraSubChunkPosUniform = this.tryGetUniformLocation("uCameraPosSubChunk");
this.instancedShaderProjectionModelViewMatrixUniform = this.tryGetUniformLocation("uProjectionMvm");
this.lightMapUniform = this.getUniformLocation("uLightMap");
this.skyLightUniform = this.getUniformLocation("uSkyLight");
this.blockLightUniform = this.getUniformLocation("uBlockLight");
this.northShadingUniform = this.getUniformLocation("uNorthShading");
this.southShadingUniform = this.getUniformLocation("uSouthShading");
this.eastShadingUniform = this.getUniformLocation("uEastShading");
this.westShadingUniform = this.getUniformLocation("uWestShading");
this.topShadingUniform = this.getUniformLocation("uTopShading");
this.bottomShadingUniform = this.getUniformLocation("uBottomShading");
}
//=========//
// methods //
//=========//
@Override
public void bind(DhApiRenderParam renderEventParam)
{
super.bind();
this.va.bind();
}
@Override
public void unbind()
{
super.unbind();
this.va.unbind();
}
@Override
public void free()
{
this.va.free();
super.free();
}
@Override
public void bindVertexBuffer(int vbo) { this.va.bindBufferToAllBindingPoints(vbo); }
@Override
public void fillIndirectUniformData(
DhApiRenderParam renderParameters,
DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
DhApiVec3d camPos
)
{
Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix);
super.bind();
this.setUniform(this.instancedShaderOffsetChunkUniform,
new DhApiVec3i(
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
));
this.setUniform(this.instancedShaderOffsetSubChunkUniform,
new Vec3f(
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
));
this.setUniform(this.instancedShaderCameraChunkPosUniform,
new DhApiVec3i(
LodUtil.getChunkPosFromDouble(camPos.x),
LodUtil.getChunkPosFromDouble(camPos.y),
LodUtil.getChunkPosFromDouble(camPos.z)
));
this.setUniform(this.instancedShaderCameraSubChunkPosUniform,
new Vec3f(
LodUtil.getSubChunkPosFromDouble(camPos.x),
LodUtil.getSubChunkPosFromDouble(camPos.y),
LodUtil.getSubChunkPosFromDouble(camPos.z)
));
this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, projectionMvmMatrix);
this.setUniform(this.lightMapUniform, ILightMapWrapper.BOUND_INDEX);
this.setUniform(this.skyLightUniform, boxGroup.getSkyLight());
this.setUniform(this.blockLightUniform, boxGroup.getBlockLight());
this.setUniform(this.northShadingUniform, shading.north);
this.setUniform(this.southShadingUniform, shading.south);
this.setUniform(this.eastShadingUniform, shading.east);
this.setUniform(this.westShadingUniform, shading.west);
this.setUniform(this.topShadingUniform, shading.top);
this.setUniform(this.bottomShadingUniform, shading.bottom);
}
@Override
public void fillSharedDirectUniformData(
DhApiRenderParam renderParameters,
DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
DhApiVec3d camPos)
{
this.setUniform(this.lightMapUniform, ILightMapWrapper.BOUND_INDEX);
this.setUniform(this.skyLightUniform, boxGroup.getSkyLight());
this.setUniform(this.blockLightUniform, boxGroup.getBlockLight());
this.setUniform(this.northShadingUniform, shading.north);
this.setUniform(this.southShadingUniform, shading.south);
this.setUniform(this.eastShadingUniform, shading.east);
this.setUniform(this.westShadingUniform, shading.west);
this.setUniform(this.topShadingUniform, shading.top);
this.setUniform(this.bottomShadingUniform, shading.bottom);
}
public void fillDirectUniformData(
DhApiRenderParam renderParameters,
IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box,
DhApiVec3d camPos)
{
Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix);
Mat4f boxTransform = Mat4f.createTranslateMatrix(
(float) (box.minPos.x + boxGroup.getOriginBlockPos().x - camPos.x),
(float) (box.minPos.y + boxGroup.getOriginBlockPos().y - camPos.y),
(float) (box.minPos.z + boxGroup.getOriginBlockPos().z - camPos.z));
boxTransform.multiply(Mat4f.createScaleMatrix(
(float) (box.maxPos.x - box.minPos.x),
(float) (box.maxPos.y - box.minPos.y),
(float) (box.maxPos.z - box.minPos.z)));
projectionMvmMatrix.multiply(boxTransform);
this.setUniform(this.directShaderTransformUniform, projectionMvmMatrix);
this.setUniform(this.directShaderColorUniform, box.color);
}
@Override
public int getId() { return this.id; }
/** The base DH render program should always render */
@Override
public boolean overrideThisFrame() { return true; }
}
@@ -1,78 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.List;
import java.util.*;
/**
* Handles creating {@link DhApiRenderableBox}.
*
* @see IDhApiCustomRenderRegister
* @see DhApiRenderableBox
*/
public class GenericRenderObjectFactory implements IDhApiCustomRenderObjectFactory
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final GenericRenderObjectFactory INSTANCE = new GenericRenderObjectFactory();
//=============//
// constructor //
//=============//
private GenericRenderObjectFactory() { }
//================//
// group creation //
//================//
@Override
public IDhApiRenderableBoxGroup createForSingleBox(String resourceLocation, DhApiRenderableBox box)
{
ArrayList<DhApiRenderableBox> list = new ArrayList<>();
list.add(box);
return this.createAbsolutePositionedGroup(resourceLocation, list);
}
@Override
public IDhApiRenderableBoxGroup createRelativePositionedGroup(String resourceLocation, DhApiVec3d originBlockPos, List<DhApiRenderableBox> boxList)
{ return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(originBlockPos.x, originBlockPos.y, originBlockPos.z), boxList, true); }
@Override
public IDhApiRenderableBoxGroup createAbsolutePositionedGroup(String resourceLocation, List<DhApiRenderableBox> boxList)
{ return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(0, 0, 0), boxList, false); }
}
@@ -1,40 +0,0 @@
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import java.util.List;
public interface IGenericObjectVertexBufferContainer extends AutoCloseable
{
void uploadDataToGpu();
void updateVertexData(List<DhApiRenderableBox> uploadBoxList);
EState getState();
void setState(EState state);
@Override
void close();
//================//
// helper classes //
//================//
//region
enum EState
{
NEW,
UPDATING_DATA,
READY_TO_UPLOAD,
RENDER,
ERROR,
}
//endregion
}
@@ -1,180 +0,0 @@
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
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.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.lwjgl.opengl.GL32;
import java.awt.*;
import java.util.List;
/**
* For use by {@link RenderableBoxGroup}
*
* @see RenderableBoxGroup
*/
public class NativeGlGenericObjectVertexContainer implements IGenericObjectVertexBufferContainer
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
public int chunkPos = 0;
public int subChunkPos = 0;
public int scale = 0;
public int color = 0;
public int material = 0;
public int[] chunkPosData = new int[0];
public float[] subChunkPosData = new float[0];
public float[] scalingData = new float[0];
public float[] colorData = new float[0];
public int[] materialData = new int[0];
public int uploadedBoxCount = 0;
private EState state = EState.NEW;
@Override
public EState getState() { return this.state; }
@Override
public void setState(EState state) { this.state = state; }
//===========================//
// render building/uploading //
//===========================//
//region
/** needs to be done on the render thread */
public void tryRunRenderThreadSetup()
{
if (this.chunkPos == 0)
{
this.chunkPos = GLMC.glGenBuffers();
this.subChunkPos = GLMC.glGenBuffers();
this.scale = GLMC.glGenBuffers();
this.color = GLMC.glGenBuffers();
this.material = GLMC.glGenBuffers();
}
}
public void updateVertexData(List<DhApiRenderableBox> uploadBoxList)
{
int boxCount = uploadBoxList.size();
// recreate the data arrays if their size is different
if (this.uploadedBoxCount != boxCount)
{
this.uploadedBoxCount = boxCount;
this.chunkPosData = new int[boxCount * 3]; // 3 elements XYZ
this.subChunkPosData = new float[boxCount * 3]; // 3 elements XYZ
this.scalingData = new float[boxCount * 3]; // 3 elements XYZ
this.colorData = new float[boxCount * 4]; // 4 elements, RGBA
this.materialData = new int[boxCount];
}
// transformation / scaling //
for (int i = 0; i < boxCount; i++)
{
DhApiRenderableBox box = uploadBoxList.get(i);
int dataIndex = i * 3;
this.chunkPosData[dataIndex] = LodUtil.getChunkPosFromDouble(box.minPos.x);
this.chunkPosData[dataIndex + 1] = LodUtil.getChunkPosFromDouble(box.minPos.y);
this.chunkPosData[dataIndex + 2] = LodUtil.getChunkPosFromDouble(box.minPos.z);
this.subChunkPosData[dataIndex] = LodUtil.getSubChunkPosFromDouble(box.minPos.x);
this.subChunkPosData[dataIndex + 1] = LodUtil.getSubChunkPosFromDouble(box.minPos.y);
this.subChunkPosData[dataIndex + 2] = LodUtil.getSubChunkPosFromDouble(box.minPos.z);
this.scalingData[dataIndex] = (float) (box.maxPos.x - box.minPos.x);
this.scalingData[dataIndex + 1] = (float) (box.maxPos.y - box.minPos.y);
this.scalingData[dataIndex + 2] = (float) (box.maxPos.z - box.minPos.z);
}
// colors/materials //
for (int i = 0; i < boxCount; i++)
{
DhApiRenderableBox box = uploadBoxList.get(i);
Color color = box.color;
int colorIndex = i * 4;
this.colorData[colorIndex] = color.getRed() / 255.0f;
this.colorData[colorIndex + 1] = color.getGreen() / 255.0f;
this.colorData[colorIndex + 2] = color.getBlue() / 255.0f;
this.colorData[colorIndex + 3] = color.getAlpha() / 255.0f;
this.materialData[i] = box.material;
}
this.state = NativeGlGenericObjectVertexContainer.EState.READY_TO_UPLOAD;
}
public void uploadDataToGpu()
{
// Upload transformation matrices
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.chunkPos);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.chunkPosData, GL32.GL_DYNAMIC_DRAW);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.subChunkPos);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.subChunkPosData, GL32.GL_DYNAMIC_DRAW);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.scale);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.scalingData, GL32.GL_DYNAMIC_DRAW);
// Upload colors
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.color);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.colorData, GL32.GL_DYNAMIC_DRAW);
// Upload materials
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.material);
GL32.glBufferData(GL32.GL_ARRAY_BUFFER, this.materialData, GL32.GL_DYNAMIC_DRAW);
this.state = EState.RENDER;
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
tryDeleteBuffer(this.chunkPos);
tryDeleteBuffer(this.subChunkPos);
tryDeleteBuffer(this.scale);
tryDeleteBuffer(this.color);
tryDeleteBuffer(this.material);
}
private static void tryDeleteBuffer(int bufferId)
{
// usually unnecessary, but just in case
if (bufferId != 0)
{
GLMC.glDeleteBuffers(bufferId);
}
}
//endregion
}
@@ -1,362 +0,0 @@
package com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
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.glObject.GLProxy;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.jetbrains.annotations.Nullable;
import java.io.Closeable;
import java.util.*;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
public class RenderableBoxGroup
extends AbstractList<DhApiRenderableBox>
implements IDhApiRenderableBoxGroup, Closeable
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
public static final AtomicInteger NEXT_ID_ATOMIC_INT = new AtomicInteger(0);
public final long id;
public final String resourceLocationNamespace;
public final String resourceLocationPath;
/** If false the boxes will be positioned relative to the level's origin */
public final boolean positionBoxesRelativeToGroupOrigin;
private final List<DhApiRenderableBox> boxList;
/** backup list which allows for uploading the boxes even it the main list is being modified on a different thread. */
private final List<DhApiRenderableBox> uploadBoxList;
private final DhApiVec3d originBlockPos;
public boolean active = true;
public boolean ssaoEnabled = true;
private boolean vertexDataDirty = true;
public byte skyLight = LodUtil.MAX_MC_LIGHT;
public byte blockLight = LodUtil.MIN_MC_LIGHT;
public DhApiRenderableBoxGroupShading shading = DhApiRenderableBoxGroupShading.getDefaultShaded();
@Nullable
public Consumer<DhApiRenderParam> beforeRenderFunc;
public Consumer<DhApiRenderParam> afterRenderFunc;
// instance data
public IGenericObjectVertexBufferContainer vertexBufferContainer = WRAPPER_FACTORY.createInstancedVboContainer();
/** double buffering for thread safety and to prevent locking the render thread during update */
private IGenericObjectVertexBufferContainer altVertexBufferContainer = WRAPPER_FACTORY.createInstancedVboContainer();
//=================//
// getters/setters //
//=================//
//region
@Override
public long getId() { return this.id; }
@Override
public String getResourceLocationNamespace() { return this.resourceLocationNamespace; }
@Override
public String getResourceLocationPath() { return this.resourceLocationPath; }
@Override
public void setOriginBlockPos(DhApiVec3d pos)
{
this.originBlockPos.x = pos.x;
this.originBlockPos.y = pos.y;
this.originBlockPos.z = pos.z;
}
@Override
public DhApiVec3d getOriginBlockPos() { return new DhApiVec3d(this.originBlockPos.x, this.originBlockPos.y, this.originBlockPos.z); }
@Override
public void setSkyLight(int skyLight)
{
if (skyLight < LodUtil.MIN_MC_LIGHT
|| skyLight > LodUtil.MAX_MC_LIGHT)
{
throw new IllegalArgumentException("Sky light ["+skyLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive).");
}
this.skyLight = (byte)skyLight;
}
@Override
public int getSkyLight() { return this.skyLight; }
@Override
public void setBlockLight(int blockLight)
{
if (blockLight < LodUtil.MIN_MC_LIGHT
|| blockLight > LodUtil.MAX_MC_LIGHT)
{
throw new IllegalArgumentException("Block light ["+blockLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive).");
}
this.blockLight = (byte)blockLight;
}
@Override
public int getBlockLight() { return this.blockLight; }
@Override
public void setPreRenderFunc(Consumer<DhApiRenderParam> func) { this.beforeRenderFunc = func; }
@Override
public void setPostRenderFunc(Consumer<DhApiRenderParam> func) { this.afterRenderFunc = func; }
@Override
public void setActive(boolean active) { this.active = active; }
@Override
public boolean isActive() { return this.active; }
@Override
public void setSsaoEnabled(boolean ssaoEnabled) { this.ssaoEnabled = ssaoEnabled; }
@Override
public boolean isSsaoEnabled() { return this.ssaoEnabled; }
@Override
public void setShading(DhApiRenderableBoxGroupShading shading) { this.shading = shading; }
@Override
public DhApiRenderableBoxGroupShading getShading() { return this.shading; }
//endregion
//=============//
// constructor //
//=============//
//region
public RenderableBoxGroup(
String resourceLocation,
DhApiVec3d originBlockPos, List<DhApiRenderableBox> boxList,
boolean positionBoxesRelativeToGroupOrigin) throws IllegalArgumentException
{
String[] splitResourceLocation = resourceLocation.split(":");
if (splitResourceLocation.length != 2)
{
throw new IllegalArgumentException("Resource Location must be a string that's separated by a single colon, for example: [DistantHorizons:Beacons], your namespace ["+resourceLocation+"], contains ["+(splitResourceLocation.length-1)+"] colons.");
}
this.resourceLocationNamespace = splitResourceLocation[0];
this.resourceLocationPath = splitResourceLocation[1];
this.id = NEXT_ID_ATOMIC_INT.getAndIncrement();
this.boxList = Collections.synchronizedList(new ArrayList<>(boxList));
this.uploadBoxList = Collections.synchronizedList(new ArrayList<>(boxList));
this.originBlockPos = originBlockPos;
this.positionBoxesRelativeToGroupOrigin = positionBoxesRelativeToGroupOrigin;
}
//endregion
//=================//
// render building //
//=================//
//region
@Override
public void triggerBoxChange() { this.vertexDataDirty = true; }
/**
* Does nothing if the vertex data is already up-to-date
* and is meaningless if using direct rendering.
*/
public void tryUpdateInstancedDataAsync()
{
// if the alt container is done, swap it in
if (this.altVertexBufferContainer.getState() == NativeGlGenericObjectVertexContainer.EState.READY_TO_UPLOAD)
{
this.altVertexBufferContainer.uploadDataToGpu();
// swap VBO references for rendering
IGenericObjectVertexBufferContainer temp = this.vertexBufferContainer;
this.vertexBufferContainer = this.altVertexBufferContainer;
this.altVertexBufferContainer = temp;
this.vertexDataDirty = false;
return;
}
// if the vertex data is already up to date, do nothing
if (!this.vertexDataDirty)
{
return;
}
PriorityTaskPicker.Executor executor = ThreadPoolUtil.getRenderLoadingExecutor();
if (executor == null || executor.isTerminated())
{
return;
}
// if the alternate container is already updating, don't double-queue it
if (this.altVertexBufferContainer.getState() == NativeGlGenericObjectVertexContainer.EState.UPDATING_DATA)
{
return;
}
this.altVertexBufferContainer.setState(NativeGlGenericObjectVertexContainer.EState.UPDATING_DATA);
//this.altInstancedVbos.tryRunRenderThreadSetup();
// copy over the box list so we can upload without concurrent modification issues
this.uploadBoxList.clear();
synchronized (this.uploadBoxList)
{
this.uploadBoxList.addAll(this.boxList);
}
try
{
executor.runTask(() ->
{
try
{
this.altVertexBufferContainer.updateVertexData(this.uploadBoxList);
}
catch (Exception e)
{
LOGGER.error("Unexpected error updating instanced VBO data for: ["+this+"], error: ["+e.getMessage()+"].", e);
this.altVertexBufferContainer.setState(NativeGlGenericObjectVertexContainer.EState.ERROR);
}
});
}
catch (RejectedExecutionException ignore)
{
// the executor was shut down, it should be back up shortly and able to accept new jobs
this.altVertexBufferContainer.setState(NativeGlGenericObjectVertexContainer.EState.NEW);
}
}
//endregion
//===============//
// render events //
//===============//
//region
/**
* This is called before every frame, even if {@link this#isActive()} returns false. <br>
* {@link this#isActive()} can be changed at this point before the object is rendered to the frame.
*/
public void preRender(DhApiRenderParam renderEventParam)
{
if (this.beforeRenderFunc != null)
{
this.beforeRenderFunc.accept(renderEventParam);
}
}
/**
* Called after rendering is completed. <br>
* Can be used to handle any necessary cleanup.
*/
public void postRender(DhApiRenderParam renderEventParam)
{
if (this.afterRenderFunc != null)
{
this.afterRenderFunc.accept(renderEventParam);
}
}
//endregion
//================//
// List Overrides //
//================//
//region
@Override
public boolean add(DhApiRenderableBox box) { return this.boxList.add(box); }
@Override
public DhApiRenderableBox get(int index) { return this.boxList.get(index); }
@Override
public int size() { return this.boxList.size(); }
@Override
public boolean removeIf(Predicate<? super DhApiRenderableBox> filter) { return this.boxList.removeIf(filter); }
@Override
public boolean remove(Object obj) { return this.boxList.remove(obj); }
@Override
public DhApiRenderableBox remove(int index) { return this.boxList.remove(index); }
@Override
public void replaceAll(UnaryOperator<DhApiRenderableBox> operator) { this.boxList.replaceAll(operator); }
@Override
public void sort(Comparator<? super DhApiRenderableBox> comparator) { this.boxList.sort(comparator); }
@Override
public void forEach(Consumer<? super DhApiRenderableBox> action) { this.boxList.forEach(action); }
@Override
public Spliterator<DhApiRenderableBox> spliterator() { return this.boxList.spliterator(); }
@Override
public Stream<DhApiRenderableBox> stream() { return this.boxList.stream(); }
@Override
public Stream<DhApiRenderableBox> parallelStream() { return this.boxList.parallelStream(); }
@Override
public void clear() { this.boxList.clear(); }
//endregion
//================//
// base overrides //
//================//
//region
@Override
public String toString() { return "["+this.resourceLocationNamespace+":"+this.resourceLocationPath+"] ID:["+this.id+"], pos:[("+this.originBlockPos.x+", "+this.originBlockPos.y+", "+this.originBlockPos.z+")], size:["+this.size()+"], active:["+this.active+"]"; }
@Override
public void close()
{
GLProxy.queueRunningOnRenderThread(() ->
{
this.vertexBufferContainer.close();
this.altVertexBufferContainer.close();
});
}
//endregion
}
@@ -1,77 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
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() {}
}
@@ -1,198 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
// 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
}
@@ -1,164 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,118 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.VanillaFadeRenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.lwjgl.opengl.GL32;
/**
* Draws the Fade texture onto Minecraft's FrameBuffer. <br><br>
*
* See Also: <br>
* {@link VanillaFadeRenderer} - Parent to this shader. <br>
* {@link VanillaFadeShader} - draws the Fade texture. <br>
*/
public class FadeApplyShader extends AbstractShaderRenderer
{
public static FadeApplyShader INSTANCE = new FadeApplyShader();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,118 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.FogRenderer;
import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.lwjgl.opengl.GL32;
/**
* Draws the Fog texture onto DH's FrameBuffer. <br><br>
*
* See Also: <br>
* {@link FogRenderer} - Parent to this shader. <br>
* {@link FogShader} - draws the Fog texture. <br>
*/
public class FogApplyShader extends AbstractShaderRenderer
{
public static FogApplyShader INSTANCE = new FogApplyShader();
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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);
}
}
@@ -1,286 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
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.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
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.IMinecraftGLWrapper;
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,144 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
import com.seibel.distanthorizons.core.render.renderer.SSAORenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.lwjgl.opengl.GL32;
/**
* Draws the SSAO texture onto DH's FrameBuffer. <br><br>
*
* See Also: <br>
* {@link SSAORenderer} - Parent to this shader. <br>
* {@link SSAOShader} - draws the SSAO texture. <br>
*/
public class SSAOApplyShader extends AbstractShaderRenderer
{
public static SSAOApplyShader INSTANCE = new SSAOApplyShader();
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,140 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
import com.seibel.distanthorizons.core.render.renderer.SSAORenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.lwjgl.opengl.GL32;
/**
* Draws the SSAO to a texture. <br><br>
*
* See Also: <br>
* {@link SSAORenderer} - Parent to this shader. <br>
* {@link SSAOApplyShader} - draws the SSAO texture to DH's FrameBuffer. <br>
*/
public class SSAOShader extends AbstractShaderRenderer
{
public static SSAOShader INSTANCE = new SSAOShader();
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,197 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.BlazeLodRenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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 IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
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();
}
}
@@ -1,181 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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,
* <p>
* 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
{
private final ImmutableList<LodVertexFormatElement> elements;
private final IntList offsets = new IntArrayList();
private final int byteSize;
public LodVertexFormat(ImmutableList<LodVertexFormatElement> 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<LodVertexFormatElement> 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();
}
/* not currently needed setupBufferState()
public void setupBufferState(long p_227892_1_)
{
if (!RenderSystem.isOnRenderThread())
{
RenderSystem.recordRenderCall(() ->
{
this.setupBufferState(p_227892_1_);
});
}
else
{
int i = this.getVertexSize();
List<LodVertexFormatElement> list = this.getElements();
for (int j = 0; j < list.size(); ++j)
{
list.get(j).setupBufferState(p_227892_1_ + this.offsets.getInt(j), i);
}
}
}
*/
/* not currently needed clearBufferState()
public void clearBufferState()
{
if (!RenderSystem.isOnRenderThread())
{
RenderSystem.recordRenderCall(this::clearBufferState);
}
else
{
for (LodVertexFormatElement LodVertexFormatElement : this.getElements())
{
LodVertexFormatElement.clearBufferState();
}
}
}
*/
/* not currently needed has-Position/Normal/Color/UV
public boolean hasPosition()
{
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.POSITION);
}
public boolean hasNormal()
{
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.NORMAL);
}
public boolean hasColor()
{
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.COLOR);
}
public boolean hasUV(int which)
{
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.UV && e.getIndex() == which);
}
*/
}
@@ -1,168 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.vertexFormat;
import org.lwjgl.opengl.GL32;
/**
* This object is used to build LodVertexFormats.
* <p>
* A (almost) exact copy of Minecraft's
* VertexFormatElement class. <br>
* 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;
}
}
}
@@ -1,50 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.render.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.<LodVertexFormatElement>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());
}
@@ -1,126 +0,0 @@
/*
* 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 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 License for more details.
*
* You should have received a copy of the GNU Lesser General License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.wrapperInterfaces.minecraft;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
import org.lwjgl.opengl.GL32;
import java.util.ArrayList;
import java.util.UUID;
/**
* Used to sync GL state changes between DH and MC.
* This is specifically important for other mods that change MC's rendering like Iris.
*/
public interface IMinecraftGLWrapper extends IBindable
{
// scissor //
/** @see GL32#GL_SCISSOR_TEST */
void enableScissorTest();
/** @see GL32#GL_SCISSOR_TEST */
void disableScissorTest();
// stencil //
///** @see GL32#GL_SCISSOR_TEST */
//void enableScissorTest() { GlStateManager._enableScissorTest(); }
///** @see GL32#GL_SCISSOR_TEST */
//void disableScissorTest() { GlStateManager._disableScissorTest(); }
// depth //
/** @see GL32#GL_DEPTH_TEST */
void enableDepthTest();
/** @see GL32#GL_DEPTH_TEST */
void disableDepthTest();
/** @see GL32#glDepthFunc(int) */
void glDepthFunc(int func);
/** @see GL32#glDepthMask(boolean) */
void enableDepthMask();
/** @see GL32#glDepthMask(boolean) */
void disableDepthMask();
// blending //
/** @see GL32#GL_BLEND */
void enableBlend();
/** @see GL32#GL_BLEND */
void disableBlend();
/** @see GL32#glBlendFunc */
void glBlendFunc(int sfactor, int dfactor);
/** @see GL32#glBlendFuncSeparate */
void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha);
// frame buffers //
/** @see GL32#glBindFramebuffer */
void glBindFramebuffer(int target, int framebuffer);
// buffers //
/** @see GL32#glGenBuffers() */
int glGenBuffers();
/** @see GL32#glDeleteBuffers(int) */
void glDeleteBuffers(int buffer);
// culling //
/** @see GL32#GL_CULL_FACE */
void enableFaceCulling();
/** @see GL32#GL_CULL_FACE */
void disableFaceCulling();
// textures //
/** @see GL32#glGenTextures() */
int glGenTextures();
/** @see GL32#glDeleteTextures(int) */
void glDeleteTextures(int texture);
/** @see GL32#glActiveTexture(int) */
void glActiveTexture(int textureId);
/**
* Only works for textures bound via this system. <br>
* Returns the bound {@link GL32#GL_TEXTURE_BINDING_2D}
*/
int getActiveTexture();
/**
* Always binds to {@link GL32#GL_TEXTURE_2D}
* @see GL32#glBindTexture(int, int)
*/
void glBindTexture(int texture);
}