Add instanced and direct genericObject rendering

This commit is contained in:
James Seibel
2024-06-29 10:29:30 -06:00
parent 62e5183c54
commit f2bba7f3df
9 changed files with 211 additions and 75 deletions
@@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.objects.GLMessage;
import com.seibel.distanthorizons.core.util.objects.GLMessageOutputStream;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.apache.logging.log4j.LogManager;
@@ -40,8 +39,6 @@ import org.lwjgl.opengl.GLUtil;
import java.io.PrintStream;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
@@ -76,7 +73,9 @@ public class GLProxy
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 vertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3
public boolean instancedArraysSupported = false;
public boolean vertexAttribDivisorSupported = false; // OpenGL 3.3 or newer
private final EDhApiGpuUploadMethod preferredUploadMethod;
@@ -133,21 +132,26 @@ public class GLProxy
// get GPU capabilities //
//======================//
// 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
// 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
// get specific capabilities
// 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)
{
GL_LOGGER.warn("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods.");
GL_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 (vendor.contains("NVIDIA") || vendor.contains("GEFORCE"))
@@ -154,7 +154,7 @@ public class ShaderProgram
* @return Location of the Uniform
* @throws RuntimeException if uniform not found
*/
public int getUniformLocation(CharSequence name)
public int getUniformLocation(CharSequence name) throws RuntimeException
{
int i = GL32.glGetUniformLocation(id, name);
if (i == -1)
@@ -48,7 +48,7 @@ public abstract class AbstractVertexAttribute
public static AbstractVertexAttribute create()
{
if (GLProxy.getInstance().VertexAttributeBufferBindingSupported)
if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
{
return new VertexAttributePostGL43();
}
@@ -29,6 +29,8 @@ import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLElementBuffer;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
@@ -42,6 +44,8 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL33;
@@ -65,23 +69,33 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
{
public static GenericObjectRenderer INSTANCE = new GenericObjectRenderer();
public static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(GenericObjectRenderer.class), () -> EDhApiLoggerMode.LOG_ALL_TO_CHAT);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final ConfigBasedSpamLogger SPAM_LOGGER = new ConfigBasedSpamLogger(LogManager.getLogger(TestRenderer.class), () -> EDhApiLoggerMode.LOG_ALL_TO_CHAT, 1);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
// rendering setup
private ShaderProgram basicUnlitShader;
private boolean init = false;
private ShaderProgram shader;
private GLVertexBuffer vertexBuffer;
private GLElementBuffer solidIndexBuffer;
private AbstractVertexAttribute va;
private boolean init = false;
private boolean useInstancedRendering;
private boolean vertexAttribDivisorSupported;
private boolean instancedArraysSupported;
// instance data
private int instanceTransformVBO;
private int instanceColorVBO;
// shader uniforms
private int transformUniformLocation;
private int directShaderColorUniformLocation;
// TODO may need to be double buffered to prevent rendering lag
private final Long2ReferenceOpenHashMap<DhApiRenderableBoxGroup> boxGroupById = new Long2ReferenceOpenHashMap<>();
@@ -141,35 +155,50 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
}
this.init = true;
this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported;
this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported;
this.useInstancedRendering = this.vertexAttribDivisorSupported || this.instancedArraysSupported;
if (!this.useInstancedRendering)
{
LOGGER.info("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering may be slow.");
}
this.va = AbstractVertexAttribute.create();
this.va.bind();
// Pos
this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false));
this.va.completeAndCheck(Float.BYTES * 3);
this.basicUnlitShader = new ShaderProgram("shaders/genericObject/vert.vert", "shaders/genericObject/frag.frag",
this.shader = new ShaderProgram(
this.useInstancedRendering ? "shaders/genericObject/instanced/vert.vert" : "shaders/genericObject/direct/vert.vert",
this.useInstancedRendering ? "shaders/genericObject/instanced/frag.frag" : "shaders/genericObject/direct/frag.frag",
"fragColor", new String[]{"vPosition"});
this.transformUniformLocation = this.shader.tryGetUniformLocation("transform");
this.directShaderColorUniformLocation = this.shader.tryGetUniformLocation("uColor");
this.createBuffers();
// testing //
//// single giant cube
//IDhApiRenderableBoxGroup singleGiantCubeGroup = DhApi.Delayed.renderRegister.createForSingleBox(
// new DhApiRenderableBox(
// new Vec3f(0f,0f,0f), new Vec3f(16f,190f,16f),
// new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125))
// );
//DhApi.Delayed.renderRegister.add(singleGiantCubeGroup);
//
//// single slender cube
//singleGiantCubeGroup = DhApi.Delayed.renderRegister.createForSingleBox(
// new DhApiRenderableBox(
// new Vec3f(16f,0f,31f), new Vec3f(17f,2000f,32f),
// new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125))
//);
//DhApi.Delayed.renderRegister.add(singleGiantCubeGroup);
// single giant cube
IDhApiRenderableBoxGroup singleGiantCubeGroup = DhApi.Delayed.renderRegister.createForSingleBox(
new DhApiRenderableBox(
new Vec3f(0f,0f,0f), new Vec3f(16f,190f,16f),
new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125))
);
DhApi.Delayed.renderRegister.add(singleGiantCubeGroup);
// single slender cube
singleGiantCubeGroup = DhApi.Delayed.renderRegister.createForSingleBox(
new DhApiRenderableBox(
new Vec3f(16f,0f,31f), new Vec3f(17f,2000f,32f),
new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125))
);
DhApi.Delayed.renderRegister.add(singleGiantCubeGroup);
// absolute cube group
ArrayList<DhApiRenderableBox> absCubeList = new ArrayList<>();
@@ -182,26 +211,26 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
IDhApiRenderableBoxGroup absolutePosCubeGroup = DhApi.Delayed.renderRegister.createAbsolutePositionedGroup(absCubeList);
DhApi.Delayed.renderRegister.add(absolutePosCubeGroup);
//// relative cube group
//ArrayList<DhApiRenderableBox> relCubeList = new ArrayList<>();
//for (int i = 0; i < 8; i+=2)
//{
// relCubeList.add(new DhApiRenderableBox(
// new Vec3f(0f,0f+i,0f), new Vec3f(1f,1f+i,1f),
// new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue())));
//}
//IDhApiRenderableBoxGroup relativePosCubeGroup = DhApi.Delayed.renderRegister.createRelativePositionedGroup(
// 24f, 140f, 24f,
// relCubeList);
//AtomicInteger frameCount = new AtomicInteger(0);
//relativePosCubeGroup.setPreRenderFunc((event) ->
//{
// float x = relativePosCubeGroup.getOriginBlockX();
// x += event.partialTicks / 2;
// x %= 32;
// relativePosCubeGroup.setOriginBlockPos(x, relativePosCubeGroup.getOriginBlockY(), relativePosCubeGroup.getOriginBlockZ());
//});
//DhApi.Delayed.renderRegister.add(relativePosCubeGroup);
// relative cube group
ArrayList<DhApiRenderableBox> relCubeList = new ArrayList<>();
for (int i = 0; i < 8; i+=2)
{
relCubeList.add(new DhApiRenderableBox(
new Vec3f(0f,0f+i,0f), new Vec3f(1f,1f+i,1f),
new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue())));
}
IDhApiRenderableBoxGroup relativePosCubeGroup = DhApi.Delayed.renderRegister.createRelativePositionedGroup(
24f, 140f, 24f,
relCubeList);
AtomicInteger frameCount = new AtomicInteger(0);
relativePosCubeGroup.setPreRenderFunc((event) ->
{
float x = relativePosCubeGroup.getOriginBlockX();
x += event.partialTicks / 2;
x %= 32;
relativePosCubeGroup.setOriginBlockPos(x, relativePosCubeGroup.getOriginBlockY(), relativePosCubeGroup.getOriginBlockZ());
});
DhApi.Delayed.renderRegister.add(relativePosCubeGroup);
}
private void createBuffers()
@@ -318,6 +347,8 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
public void render(DhApiRenderParam renderEventParam)
{
// render setup //
GLState glState = new GLState();
this.init();
@@ -328,38 +359,57 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
GL32.glBlendEquation(GL32.GL_FUNC_ADD);
GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
this.basicUnlitShader.bind();
this.shader.bind();
this.va.bind();
this.va.bindBufferToAllBindingPoints(this.vertexBuffer.getId());
this.solidIndexBuffer.bind();
LongSet keys = boxGroupById.keySet();
for (long key : keys)
{
DhApiRenderableBoxGroup cubeGroup = boxGroupById.get(key);
this.renderCubeGroup(cubeGroup, renderEventParam);
}
this.basicUnlitShader.unbind();
glState.restore();
}
private void renderCubeGroup(DhApiRenderableBoxGroup cubeGroup, DhApiRenderParam renderEventParam)
{
Mat4f transformMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
transformMatrix.multiply(renderEventParam.dhModelViewMatrix);
Vec3d camPos = MC_RENDER.getCameraExactPosition();
Vec3f camPosFloat = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z);
Vec3d camPosDouble = MC_RENDER.getCameraExactPosition();
Vec3f camPos = new Vec3f((float) camPosDouble.x, (float) camPosDouble.y, (float) camPosDouble.z);
// rendering //
LongSet keys = boxGroupById.keySet();
for (long key : keys)
{
DhApiRenderableBoxGroup boxGroup = boxGroupById.get(key);
if (this.useInstancedRendering)
{
this.renderBoxGroupInstanced(boxGroup, renderEventParam, transformMatrix, camPos);
}
else
{
this.renderBoxGroupDirect(boxGroup, renderEventParam, transformMatrix, camPos);
}
}
// clean up //
this.shader.unbind();
glState.restore();
}
//=====================//
// instanced rendering //
//=====================//
private void renderBoxGroupInstanced(DhApiRenderableBoxGroup cubeGroup,
DhApiRenderParam renderEventParam, Mat4f transformMatrix, Vec3f camPos)
{
// update instance data //
cubeGroup.preRender(renderEventParam);
int boxCount = updateInstanceBuffers(camPosFloat, transformMatrix); // Update instance data
int boxCount = updateInstanceBuffers(camPos, transformMatrix); // Update instance data
@@ -368,24 +418,23 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceColorVBO);
GL32.glEnableVertexAttribArray(1);
GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0);
GL33.glVertexAttribDivisor(1, 1);
vertexAttribDivisor(1, 1);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceTransformVBO);
GL32.glEnableVertexAttribArray(2);
GL33.glVertexAttribDivisor(2, 1);
vertexAttribDivisor(2, 1);
GL32.glVertexAttribPointer(2, 4, GL32.GL_FLOAT, false, 16 * Float.BYTES, 0);
GL32.glEnableVertexAttribArray(3);
GL33.glVertexAttribDivisor(3, 1);
vertexAttribDivisor(3, 1);
GL32.glVertexAttribPointer(3, 4, GL32.GL_FLOAT, false, 16 * Float.BYTES, 16);
GL32.glEnableVertexAttribArray(4);
GL33.glVertexAttribDivisor(4, 1);
vertexAttribDivisor(4, 1);
GL32.glVertexAttribPointer(4, 4, GL32.GL_FLOAT, false, 16 * Float.BYTES, 32);
GL32.glEnableVertexAttribArray(5);
GL33.glVertexAttribDivisor(5, 1);
vertexAttribDivisor(5, 1);
GL32.glVertexAttribPointer(5, 4, GL32.GL_FLOAT, false, 16 * Float.BYTES, 48);
// Draw instanced
GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, SOLID_BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, boxCount);
@@ -397,6 +446,26 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
GL32.glDisableVertexAttribArray(4);
GL32.glDisableVertexAttribArray(5);
}
/**
* 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.");
}
}
private int updateInstanceBuffers(Vec3f camPos, Mat4f transformationMatrix)
{
int boxCount = 0;
@@ -481,6 +550,50 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
//==================//
// direct rendering //
//==================//
private void renderBoxGroupDirect(DhApiRenderableBoxGroup cubeGroup,
DhApiRenderParam renderEventParam, Mat4f transformMatrix, Vec3f camPos)
{
cubeGroup.preRender(renderEventParam);
for (DhApiRenderableBox box : cubeGroup.cubeList)
{
renderBox(cubeGroup, box, transformMatrix, camPos);
}
}
private void renderBox(
DhApiRenderableBoxGroup cubeGroup, DhApiRenderableBox cube,
Mat4f transformationMatrix, Vec3f camPos)
{
float originOffsetX = 0;
float originOffsetY = 0;
float originOffsetZ = 0;
if (cubeGroup.positionCubesRelativeToGroupOrigin)
{
originOffsetX = cubeGroup.originBlockX;
originOffsetY = cubeGroup.originBlockY;
originOffsetZ = cubeGroup.originBlockZ;
}
Mat4f boxTransform = Mat4f.createTranslateMatrix(
cube.minPos.x + originOffsetX - camPos.x,
cube.minPos.y + originOffsetY - camPos.y,
cube.minPos.z + originOffsetZ - camPos.z);
boxTransform.multiply(Mat4f.createScaleMatrix(
cube.maxPos.x - cube.minPos.x,
cube.maxPos.y - cube.minPos.y,
cube.maxPos.z - cube.minPos.z));
Mat4f transformMatrix = transformationMatrix.copy();
transformMatrix.multiply(boxTransform);
this.shader.setUniform(this.shader.getUniformLocation("transform"), transformMatrix);
this.shader.setUniform(this.shader.getUniformLocation("uColor"), cube.color);
GL32.glDrawElements(GL32.GL_TRIANGLES , SOLID_BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0);
}
//================//
@@ -102,7 +102,7 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr
// TODO: Add better use of the LODFormat thing
int vertexByteCount = LodUtil.LOD_VERTEX_FORMAT.getByteSize();
if (GLProxy.getInstance().VertexAttributeBufferBindingSupported)
if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute
else
vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute
@@ -0,0 +1,9 @@
#version 150 core
uniform vec4 uColor;
out vec4 fragColor;
void main()
{
fragColor = uColor;
}
@@ -0,0 +1,10 @@
#version 150 core
uniform mat4 transform;
in vec3 vPosition;
void main()
{
gl_Position = transform * vec4(vPosition, 1.0);
}