prepare for fast actor rendering

This commit is contained in:
JozsefA 2021-01-21 14:14:20 -08:00
parent 697a7df59c
commit 11f9c7459c
11 changed files with 421 additions and 167 deletions

View file

@ -10,90 +10,39 @@ import java.nio.ByteBuffer;
import static com.simibubi.create.foundation.render.instancing.VertexAttribute.*;
public class ContraptionBuffer extends TemplateBuffer {
public class ContraptionBuffer extends GPUBuffer {
public static final VertexFormat FORMAT = new VertexFormat(InstanceBuffer.FORMAT, RGBA);
protected int vao, ebo, vbo;
public ContraptionBuffer(BufferBuilder buf) {
super(buf);
if (vertexCount > 0) setup();
}
public void delete() {
if (vertexCount > 0) {
RenderWork.enqueue(() -> {
GL15.glDeleteBuffers(vbo);
GL15.glDeleteBuffers(ebo);
GL30.glDeleteVertexArrays(vao);
});
}
@Override
protected void copyVertex(ByteBuffer to, int vertex) {
to.putFloat(getX(template, vertex));
to.putFloat(getY(template, vertex));
to.putFloat(getZ(template, vertex));
to.put(getNX(template, vertex));
to.put(getNY(template, vertex));
to.put(getNZ(template, vertex));
to.putFloat(getU(template, vertex));
to.putFloat(getV(template, vertex));
to.put(getR(template, vertex));
to.put(getG(template, vertex));
to.put(getB(template, vertex));
to.put(getA(template, vertex));
}
public void render() {
if (vertexCount == 0) return;
GL30.glBindVertexArray(vao);
for (int i = 0; i <= 3; i++) {
GL40.glEnableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
@Override
protected VertexFormat getModelFormat() {
return FORMAT;
}
@Override
protected void drawCall() {
GL40.glDrawElements(GL11.GL_QUADS, vertexCount, GL11.GL_UNSIGNED_SHORT, 0);
for (int i = 0; i <= FORMAT.getNumAttributes(); i++) {
GL40.glDisableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
private void setup() {
int stride = FORMAT.getStride();
int invariantSize = vertexCount * stride;
vao = GL30.glGenVertexArrays();
ebo = GlStateManager.genBuffers();
vbo = GlStateManager.genBuffers();
GL30.glBindVertexArray(vao);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
// allocate the buffer on the gpu
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
// mirror it in system memory so we can write to it
ByteBuffer constant = GL15.glMapBuffer(GL15.GL_ARRAY_BUFFER, GL15.GL_WRITE_ONLY);
for (int i = 0; i < vertexCount; i++) {
constant.putFloat(getX(template, i));
constant.putFloat(getY(template, i));
constant.putFloat(getZ(template, i));
constant.put(getNX(template, i));
constant.put(getNY(template, i));
constant.put(getNZ(template, i));
constant.putFloat(getU(template, i));
constant.putFloat(getV(template, i));
constant.put(getR(template, i));
constant.put(getG(template, i));
constant.put(getB(template, i));
constant.put(getA(template, i));
}
constant.rewind();
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
buildEBO(ebo);
FORMAT.informAttributes(0);
GlStateManager.bindBuffers(GL15.GL_ARRAY_BUFFER, 0);
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
// Deselect (bind to 0) the VAO
GL30.glBindVertexArray(0);
}
}

View file

@ -0,0 +1,107 @@
package com.simibubi.create.foundation.render;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.render.instancing.VertexFormat;
import net.minecraft.client.renderer.BufferBuilder;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL40;
import java.nio.ByteBuffer;
public abstract class GPUBuffer extends TemplateBuffer {
protected int vao, ebo, invariantVBO;
public GPUBuffer(BufferBuilder buf) {
super(buf);
if (vertexCount > 0) setup();
}
protected void setup() {
int stride = getModelFormat().getStride();
int invariantSize = vertexCount * stride;
vao = GL30.glGenVertexArrays();
ebo = GlStateManager.genBuffers();
invariantVBO = GlStateManager.genBuffers();
GL30.glBindVertexArray(vao);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, invariantVBO);
// allocate the buffer on the gpu
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
// mirror it in system memory so we can write to it
ByteBuffer constant = GL15.glMapBuffer(GL15.GL_ARRAY_BUFFER, GL15.GL_WRITE_ONLY);
for (int i = 0; i < vertexCount; i++) {
copyVertex(constant, i);
}
constant.rewind();
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
buildEBO(ebo);
getModelFormat().informAttributes(0);
GlStateManager.bindBuffers(GL15.GL_ARRAY_BUFFER, 0);
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
// Deselect (bind to 0) the VAO
GL30.glBindVertexArray(0);
}
protected abstract void copyVertex(ByteBuffer to, int index);
protected abstract VertexFormat getModelFormat();
protected int getTotalShaderAttributeCount() {
return getModelFormat().getShaderAttributeCount();
}
protected abstract void drawCall();
protected void preDrawTask() {
}
public void render() {
if (vao == 0) return;
GL30.glBindVertexArray(vao);
preDrawTask();
int numAttributes = getTotalShaderAttributeCount();
for (int i = 0; i <= numAttributes; i++) {
GL40.glEnableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
drawCall();
for (int i = 0; i <= numAttributes; i++) {
GL40.glDisableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
public void delete() {
if (vertexCount > 0) {
RenderWork.enqueue(this::deleteInternal);
}
}
protected void deleteInternal() {
GL15.glDeleteBuffers(invariantVBO);
GL15.glDeleteBuffers(ebo);
GL30.glDeleteVertexArrays(vao);
vao = 0;
ebo = 0;
invariantVBO = 0;
}
}

View file

@ -0,0 +1,30 @@
package com.simibubi.create.foundation.render.instancing;
import net.minecraft.client.renderer.BufferBuilder;
import org.lwjgl.opengl.*;
public abstract class DynamicInstanceBuffer<S extends InstanceData, D extends InstanceData> extends InstanceBuffer<S> {
protected int dynamicVBO;
protected int dynamicBufferSize = -1;
public DynamicInstanceBuffer(BufferBuilder buf) {
super(buf);
}
@Override
protected void setup() {
super.setup();
dynamicVBO = GL20.glGenBuffers();
}
protected abstract VertexFormat getDynamicFormat();
protected abstract D newDynamicPart();
@Override
protected int getTotalShaderAttributeCount() {
return super.getTotalShaderAttributeCount() + getDynamicFormat().getShaderAttributeCount();
}
}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.foundation.render.instancing;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.render.GPUBuffer;
import com.simibubi.create.foundation.render.RenderMath;
import com.simibubi.create.foundation.render.RenderWork;
import com.simibubi.create.foundation.render.TemplateBuffer;
@ -14,12 +15,13 @@ import java.util.function.Consumer;
import static com.simibubi.create.foundation.render.instancing.VertexAttribute.*;
public abstract class InstanceBuffer<D extends InstanceData> extends TemplateBuffer {
public abstract class InstanceBuffer<D extends InstanceData> extends GPUBuffer {
public static final VertexFormat FORMAT = new VertexFormat(POSITION, NORMAL, UV);
protected int vao, ebo, invariantVBO, instanceVBO, instanceCount;
protected int instanceVBO;
protected int instanceCount;
protected int bufferSize = -1;
protected int instanceBufferSize = -1;
protected final ArrayList<D> data = new ArrayList<>();
protected boolean rebuffer = false;
@ -27,51 +29,31 @@ public abstract class InstanceBuffer<D extends InstanceData> extends TemplateBuf
public InstanceBuffer(BufferBuilder buf) {
super(buf);
if (vertexCount > 0) setup();
}
private void setup() {
int stride = FORMAT.getStride();
int invariantSize = vertexCount * stride;
vao = GL30.glGenVertexArrays();
ebo = GlStateManager.genBuffers();
invariantVBO = GlStateManager.genBuffers();
@Override
protected void setup() {
super.setup();
instanceVBO = GlStateManager.genBuffers();
}
GL30.glBindVertexArray(vao);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, invariantVBO);
@Override
protected VertexFormat getModelFormat() {
return FORMAT;
}
// allocate the buffer on the gpu
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
@Override
protected void copyVertex(ByteBuffer constant, int i) {
constant.putFloat(getX(template, i));
constant.putFloat(getY(template, i));
constant.putFloat(getZ(template, i));
// mirror it in system memory so we can write to it
ByteBuffer constant = GL15.glMapBuffer(GL15.GL_ARRAY_BUFFER, GL15.GL_WRITE_ONLY);
constant.put(getNX(template, i));
constant.put(getNY(template, i));
constant.put(getNZ(template, i));
for (int i = 0; i < vertexCount; i++) {
constant.putFloat(getX(template, i));
constant.putFloat(getY(template, i));
constant.putFloat(getZ(template, i));
constant.put(getNX(template, i));
constant.put(getNY(template, i));
constant.put(getNZ(template, i));
constant.putFloat(getU(template, i));
constant.putFloat(getV(template, i));
}
constant.rewind();
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
buildEBO(ebo);
FORMAT.informAttributes(0);
GlStateManager.bindBuffers(GL15.GL_ARRAY_BUFFER, 0);
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
// Deselect (bind to 0) the VAO
GL30.glBindVertexArray(0);
constant.putFloat(getU(template, i));
constant.putFloat(getV(template, i));
}
protected abstract VertexFormat getInstanceFormat();
@ -93,20 +75,16 @@ public abstract class InstanceBuffer<D extends InstanceData> extends TemplateBuf
if (shouldBuild) rebuffer = true;
}
public void delete() {
if (vertexCount > 0) {
RenderWork.enqueue(() -> {
GL15.glDeleteBuffers(invariantVBO);
GL15.glDeleteBuffers(instanceVBO);
GL15.glDeleteBuffers(ebo);
GL30.glDeleteVertexArrays(vao);
vao = 0;
ebo = 0;
invariantVBO = 0;
instanceVBO = 0;
bufferSize = -1;
});
}
protected void deleteInternal() {
GL15.glDeleteBuffers(invariantVBO);
GL15.glDeleteBuffers(instanceVBO);
GL15.glDeleteBuffers(ebo);
GL30.glDeleteVertexArrays(vao);
vao = 0;
ebo = 0;
invariantVBO = 0;
instanceVBO = 0;
instanceBufferSize = -1;
}
protected abstract D newInstance();
@ -120,30 +98,16 @@ public abstract class InstanceBuffer<D extends InstanceData> extends TemplateBuf
data.add(instanceData);
}
public void render() {
if (vao == 0) return;
GL30.glBindVertexArray(vao);
finishBuffering();
int numAttributes = getInstanceFormat().getNumAttributes() + FORMAT.getNumAttributes();
for (int i = 0; i <= numAttributes; i++) {
GL40.glEnableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo);
GL40.glDrawElementsInstanced(GL11.GL_QUADS, vertexCount, GL11.GL_UNSIGNED_SHORT, 0, instanceCount);
for (int i = 0; i <= numAttributes; i++) {
GL40.glDisableVertexAttribArray(i);
}
GlStateManager.bindBuffers(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
protected int getTotalShaderAttributeCount() {
return getInstanceFormat().getShaderAttributeCount() + FORMAT.getShaderAttributeCount();
}
private void finishBuffering() {
@Override
protected void drawCall() {
GL40.glDrawElementsInstanced(GL11.GL_QUADS, vertexCount, GL11.GL_UNSIGNED_SHORT, 0, instanceCount);
}
protected void preDrawTask() {
if (!rebuffer || data.isEmpty()) return;
instanceCount = data.size();
@ -155,9 +119,9 @@ public abstract class InstanceBuffer<D extends InstanceData> extends TemplateBuf
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, instanceVBO);
// this changes enough that it's not worth reallocating the entire buffer every time.
if (instanceSize > bufferSize) {
if (instanceSize > instanceBufferSize) {
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, instanceSize, GL15.GL_STATIC_DRAW);
bufferSize = instanceSize;
instanceBufferSize = instanceSize;
}
ByteBuffer buffer = GL15.glMapBuffer(GL15.GL_ARRAY_BUFFER, GL15.GL_WRITE_ONLY);
@ -166,10 +130,10 @@ public abstract class InstanceBuffer<D extends InstanceData> extends TemplateBuf
buffer.rewind();
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
int staticAttributes = FORMAT.getNumAttributes();
int staticAttributes = FORMAT.getShaderAttributeCount();
instanceFormat.informAttributes(staticAttributes);
for (int i = 0; i < instanceFormat.getNumAttributes(); i++) {
for (int i = 0; i < instanceFormat.getShaderAttributeCount(); i++) {
GL33.glVertexAttribDivisor(i + staticAttributes, 1);
}

View file

@ -7,13 +7,13 @@ import java.nio.ByteBuffer;
import static com.simibubi.create.foundation.render.instancing.VertexAttribute.*;
public class RotatingData extends BasicData<RotatingData> {
public static VertexFormat FORMAT = new VertexFormat(BasicData.FORMAT, FLOAT, FLOAT, VEC3);
public static VertexFormat FORMAT = new VertexFormat(BasicData.FORMAT, FLOAT, FLOAT, NORMAL);
private float rotationalSpeed;
private float rotationOffset;
private float rotationAxisX;
private float rotationAxisY;
private float rotationAxisZ;
private byte rotationAxisX;
private byte rotationAxisY;
private byte rotationAxisZ;
public RotatingData setRotationalSpeed(float rotationalSpeed) {
this.rotationalSpeed = rotationalSpeed;
@ -26,16 +26,14 @@ public class RotatingData extends BasicData<RotatingData> {
}
public RotatingData setRotationAxis(Vector3f axis) {
this.rotationAxisX = axis.getX();
this.rotationAxisY = axis.getY();
this.rotationAxisZ = axis.getZ();
setRotationAxis(axis.getX(), axis.getY(), axis.getZ());
return this;
}
public RotatingData setRotationAxis(float rotationAxisX, float rotationAxisY, float rotationAxisZ) {
this.rotationAxisX = rotationAxisX;
this.rotationAxisY = rotationAxisY;
this.rotationAxisZ = rotationAxisZ;
this.rotationAxisX = (byte) (rotationAxisX * 127);
this.rotationAxisY = (byte) (rotationAxisY * 127);
this.rotationAxisZ = (byte) (rotationAxisZ * 127);
return this;
}

View file

@ -34,7 +34,7 @@ public class VertexFormat {
this.stride = stride;
}
public int getNumAttributes() {
public int getShaderAttributeCount() {
return numAttributes;
}

View file

@ -0,0 +1,35 @@
package com.simibubi.create.foundation.render.instancing.actors;
import com.simibubi.create.foundation.render.instancing.InstanceData;
import com.simibubi.create.foundation.render.instancing.VertexFormat;
import net.minecraft.client.renderer.Vector3f;
import java.nio.ByteBuffer;
import static com.simibubi.create.foundation.render.instancing.VertexAttribute.*;
import static com.simibubi.create.foundation.render.instancing.VertexAttribute.NORMAL;
public class DynamicRotatingActorData extends InstanceData {
public static VertexFormat FORMAT = new VertexFormat(NORMAL);
private byte relativeMotionX;
private byte relativeMotionY;
private byte relativeMotionZ;
public DynamicRotatingActorData setRelativeMotion(Vector3f axis) {
setRelativeMotion(axis.getX(), axis.getY(), axis.getZ());
return this;
}
public DynamicRotatingActorData setRelativeMotion(float relativeMotionX, float relativeMotionY, float relativeMotionZ) {
this.relativeMotionX = (byte) (relativeMotionX * 127);
this.relativeMotionY = (byte) (relativeMotionY * 127);
this.relativeMotionZ = (byte) (relativeMotionZ * 127);
return this;
}
@Override
public void write(ByteBuffer buf) {
putVec3(buf, relativeMotionX, relativeMotionY, relativeMotionZ);
}
}

View file

@ -0,0 +1,31 @@
package com.simibubi.create.foundation.render.instancing.actors;
import com.simibubi.create.foundation.render.instancing.DynamicInstanceBuffer;
import com.simibubi.create.foundation.render.instancing.VertexFormat;
import net.minecraft.client.renderer.BufferBuilder;
public class RotatingActorBuffer extends DynamicInstanceBuffer<StaticRotatingActorData, DynamicRotatingActorData> {
public RotatingActorBuffer(BufferBuilder buf) {
super(buf);
}
@Override
protected VertexFormat getDynamicFormat() {
return DynamicRotatingActorData.FORMAT;
}
@Override
protected DynamicRotatingActorData newDynamicPart() {
return new DynamicRotatingActorData();
}
@Override
protected VertexFormat getInstanceFormat() {
return StaticRotatingActorData.FORMAT;
}
@Override
protected StaticRotatingActorData newInstance() {
return new StaticRotatingActorData();
}
}

View file

@ -0,0 +1,71 @@
package com.simibubi.create.foundation.render.instancing.actors;
import com.simibubi.create.foundation.render.instancing.InstanceData;
import com.simibubi.create.foundation.render.instancing.RotatingData;
import com.simibubi.create.foundation.render.instancing.VertexFormat;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.util.math.BlockPos;
import java.nio.ByteBuffer;
import static com.simibubi.create.foundation.render.instancing.VertexAttribute.*;
public class StaticRotatingActorData extends InstanceData {
public static VertexFormat FORMAT = new VertexFormat(POSITION, FLOAT, NORMAL, NORMAL);
private float x;
private float y;
private float z;
private float rotationOffset;
private byte rotationAxisX;
private byte rotationAxisY;
private byte rotationAxisZ;
private byte localOrientationX;
private byte localOrientationY;
private byte localOrientationZ;
public StaticRotatingActorData setPosition(BlockPos pos) {
this.x = pos.getX();
this.y = pos.getY();
this.z = pos.getZ();
return this;
}
public StaticRotatingActorData setRotationOffset(float rotationOffset) {
this.rotationOffset = rotationOffset;
return this;
}
public StaticRotatingActorData setRotationAxis(Vector3f axis) {
setRotationAxis(axis.getX(), axis.getY(), axis.getZ());
return this;
}
public StaticRotatingActorData setRotationAxis(float rotationAxisX, float rotationAxisY, float rotationAxisZ) {
this.rotationAxisX = (byte) (rotationAxisX * 127);
this.rotationAxisY = (byte) (rotationAxisY * 127);
this.rotationAxisZ = (byte) (rotationAxisZ * 127);
return this;
}
public StaticRotatingActorData setLocalOrientation(Vector3f axis) {
setRotationAxis(axis.getX(), axis.getY(), axis.getZ());
return this;
}
public StaticRotatingActorData setLocalOrientation(float localOrientationX, float localOrientationY, float localOrientationZ) {
this.localOrientationX = (byte) (localOrientationX * 127);
this.localOrientationY = (byte) (localOrientationY * 127);
this.localOrientationZ = (byte) (localOrientationZ * 127);
return this;
}
@Override
public void write(ByteBuffer buf) {
putVec3(buf, x, y, z);
put(buf, rotationOffset);
putVec3(buf, rotationAxisX, rotationAxisY, rotationAxisZ);
putVec3(buf, localOrientationX, localOrientationY, localOrientationZ);
}
}

View file

@ -6,6 +6,7 @@ public enum Shader {
CONTRAPTION_STRUCTURE("shader/contraption.vert", "shader/contraption.frag"),
CONTRAPTION_ROTATING("shader/contraption_rotating.vert", "shader/contraption.frag"),
CONTRAPTION_BELT("shader/contraption_belt.vert", "shader/contraption.frag"),
//CONTRAPTION_ACTOR("shader/contraption_actor.vert", "shader/contraption.frag"),
;
public final String vert;

View file

@ -0,0 +1,68 @@
#version 330 core
#define PI 3.1415926538
// model data
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
// instance data
layout (location = 3) in vec3 instancePos;
layout (location = 4) in float rotationOffset;
layout (location = 5) in vec3 localRotationAxis;
layout (location = 6) in vec3 localOrientation;
// dynamic data
layout (location = 7) in vec3 relativeMotion;
out float Diffuse;
out vec2 TexCoords;
out vec4 Color;
out vec3 BoxCoord;
uniform vec3 lightBoxSize;
uniform vec3 lightBoxMin;
uniform mat4 model;
uniform float time;
uniform int ticks;
uniform mat4 projection;
uniform mat4 view;
uniform int debug;
mat4 rotate(vec3 axis, float angle) {
float s = sin(angle);
float c = cos(angle);
float oc = 1.0 - c;
return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.,
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.,
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.,
0., 0., 0., 1.);
}
mat4 kineticRotation() {
float degrees = rotationOffset + time * speed * -3./10.;
float angle = fract(degrees / 360.) * PI * 2.;
return rotate(normalize(localRotationAxis), angle);
}
float diffuse(vec3 normal) {
float x = normal.x;
float y = normal.y;
float z = normal.z;
return min(x * x * 0.6f + y * y * ((3f + y) / 4f) + z * z * 0.8f, 1f);
}
void main() {
mat4 kineticRotation = kineticRotation();
vec4 localPos = kineticRotation * vec4(aPos - 0.5, 1f) + vec4(instancePos + 0.5, 0);
vec4 worldPos = model * localPos;
BoxCoord = (worldPos.xyz - lightBoxMin) / lightBoxSize;
Diffuse = diffuse(normalize(model * kineticRotation * vec4(aNormal, 0.)).xyz);
Color = vec4(1.);
TexCoords = aTexCoords;
gl_Position = projection * view * worldPos;
}