diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java index 1d13d8871..d07872204 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java @@ -30,7 +30,7 @@ public abstract class GlObject { this.handle = INVALID_HANDLE; } - public final void delete() { + public void delete() { if (!isHandleValid()) { throw new IllegalStateException("Handle already deleted."); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java index a7da0f4ef..35d3c1e26 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java @@ -10,7 +10,7 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; -import com.jozufozu.flywheel.core.BufferedModel; +import com.jozufozu.flywheel.core.model.BufferedModel; import com.jozufozu.flywheel.util.AttribUtil; public class Instancer { @@ -47,13 +47,13 @@ public class Instancer { vao.bind(); // bind the model's vbo to our vao - model.bindBuffer(); - model.getFormat().vertexAttribPointers(0); - model.unbindBuffer(); + model.setupState(); + + AttribUtil.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount()); - // enable all the attribute arrays in our vao. we only need to do this once - AttribUtil.enableArrays(model.getAttributeCount() + this.instanceFormat.getAttributeCount()); vao.unbind(); + + model.clearState(); } public void render() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java index 398e9491d..39aa0ce31 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderMaterial.java @@ -15,11 +15,13 @@ import com.google.common.cache.CacheBuilder; import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.core.BufferedModel; import com.jozufozu.flywheel.core.PartialModel; +import com.jozufozu.flywheel.core.model.BufferedModel; +import com.jozufozu.flywheel.core.model.IndexedModel; import com.jozufozu.flywheel.core.shader.IProgramCallback; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.util.BufferBuilderReader; +import com.jozufozu.flywheel.util.QuadConverter; import com.jozufozu.flywheel.util.RenderUtil; import com.jozufozu.flywheel.util.VirtualEmptyModelData; import com.mojang.blaze3d.matrix.MatrixStack; @@ -128,25 +130,26 @@ public class RenderMaterial

{ VertexFormat format = spec.getModelFormat(); int vertexCount = reader.getVertexCount(); - ByteBuffer to = ByteBuffer.allocate(vertexCount * format.getStride()); - to.order(ByteOrder.nativeOrder()); + ByteBuffer vertices = ByteBuffer.allocate(vertexCount * format.getStride()); + vertices.order(ByteOrder.nativeOrder()); for (int i = 0; i < vertexCount; i++) { - to.putFloat(reader.getX(i)); - to.putFloat(reader.getY(i)); - to.putFloat(reader.getZ(i)); + vertices.putFloat(reader.getX(i)); + vertices.putFloat(reader.getY(i)); + vertices.putFloat(reader.getZ(i)); - to.put(reader.getNX(i)); - to.put(reader.getNY(i)); - to.put(reader.getNZ(i)); + vertices.put(reader.getNX(i)); + vertices.put(reader.getNY(i)); + vertices.put(reader.getNZ(i)); - to.putFloat(reader.getU(i)); - to.putFloat(reader.getV(i)); + vertices.putFloat(reader.getU(i)); + vertices.putFloat(reader.getV(i)); } - to.rewind(); + vertices.rewind(); - BufferedModel bufferedModel = new BufferedModel(GlPrimitive.QUADS, format, to, vertexCount); + BufferedModel bufferedModel = new IndexedModel(GlPrimitive.TRIANGLES, format, vertices, vertexCount, QuadConverter.getInstance().getEboForNQuads(vertexCount / 4)); + //BufferedModel bufferedModel = new BufferedModel(GlPrimitive.QUADS, format, vertices, vertexCount); return new Instancer<>(bufferedModel, renderer, spec.getInstanceFormat(), spec.getInstanceFactory()); } diff --git a/src/main/java/com/jozufozu/flywheel/core/BufferedArrayModel.java b/src/main/java/com/jozufozu/flywheel/core/BufferedArrayModel.java deleted file mode 100644 index fcfff623a..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/BufferedArrayModel.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.jozufozu.flywheel.core; - -import java.nio.ByteBuffer; - -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL20; - -import com.jozufozu.flywheel.backend.gl.GlPrimitive; -import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.util.AttribUtil; - -public class BufferedArrayModel extends BufferedModel { - - protected GlVertexArray vao; - - public BufferedArrayModel(GlPrimitive primitiveMode, VertexFormat format, ByteBuffer data, int vertices) { - super(primitiveMode, format, data, vertices); - - vao = new GlVertexArray(); - - vao.bind(); - - // bind the model's vbo to our vao - vbo.bind(); - getFormat().vertexAttribPointers(0); - - // enable all the attribute arrays in our vao. we only need to do this once - AttribUtil.enableArrays(getAttributeCount()); - vbo.unbind(); - vao.unbind(); - } - - public void draw() { - if (vertexCount <= 0 || deleted) return; - - vao.bind(); - - GL20.glDrawArrays(GL11.GL_QUADS, 0, vertexCount); - - vao.unbind(); - } - - @Override - public void delete() { - super.delete(); - vao.delete(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/IndexedModel.java b/src/main/java/com/jozufozu/flywheel/core/IndexedModel.java deleted file mode 100644 index ff7f08945..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/IndexedModel.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.jozufozu.flywheel.core; - -import java.nio.ByteBuffer; - -import org.lwjgl.opengl.GL20; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.GlNumericType; -import com.jozufozu.flywheel.backend.gl.GlPrimitive; -import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; -import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; -import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; -import com.jozufozu.flywheel.util.AttribUtil; - -public class IndexedModel extends BufferedModel { - - protected int elementCount; - protected GlNumericType eboIndexType; - protected GlBuffer ebo; - - public IndexedModel(GlPrimitive primitiveMode, VertexFormat modelFormat, ByteBuffer buf, int vertices, ByteBuffer indices, int elementCount, GlNumericType indexType) { - super(primitiveMode, modelFormat, buf, vertices); - - this.ebo = new GlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER); - this.eboIndexType = indexType; - this.elementCount = elementCount; - - int indicesSize = elementCount * indexType.getByteWidth(); - - ebo.bind(); - - ebo.alloc(indicesSize); - ebo.getBuffer(0, indicesSize) - .put(indices) - .flush(); - - ebo.unbind(); - } - - public void draw() { - ebo.bind(); - vbo.bind(); - - AttribUtil.enableArrays(getAttributeCount()); - format.vertexAttribPointers(0); - - GL20.glDrawElements(primitiveMode.glEnum, elementCount, eboIndexType.getGlEnum(), 0); - - AttribUtil.disableArrays(getAttributeCount()); - - ebo.unbind(); - vbo.unbind(); - } - - @Override - public void drawInstances(int instanceCount) { - if (vertexCount <= 0 || deleted) return; - - ebo.bind(); - Backend.compat.drawInstanced.drawElementsInstanced(primitiveMode, 0, eboIndexType, 0, instanceCount); - ebo.unbind(); - } - - @Override - public void delete() { - super.delete(); - ebo.delete(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ArrayModelRenderer.java b/src/main/java/com/jozufozu/flywheel/core/model/ArrayModelRenderer.java new file mode 100644 index 000000000..d63907fda --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/ArrayModelRenderer.java @@ -0,0 +1,28 @@ +package com.jozufozu.flywheel.core.model; + +import com.jozufozu.flywheel.backend.gl.GlVertexArray; + +public class ArrayModelRenderer extends ModelRenderer { + + protected GlVertexArray vao; + + public ArrayModelRenderer(BufferedModel model) { + super(model); + vao = new GlVertexArray(); + + vao.bind(); + model.setupState(); + vao.unbind(); + model.clearState(); + } + + public void draw() { + if (!model.valid()) return; + + vao.bind(); + + model.drawCall(); + + vao.unbind(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BufferedModel.java similarity index 88% rename from src/main/java/com/jozufozu/flywheel/core/BufferedModel.java rename to src/main/java/com/jozufozu/flywheel/core/model/BufferedModel.java index bccd14cec..1b415da33 100644 --- a/src/main/java/com/jozufozu/flywheel/core/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BufferedModel.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.core; +package com.jozufozu.flywheel.core.model; import static org.lwjgl.opengl.GL20.glDrawArrays; @@ -55,29 +55,33 @@ public class BufferedModel { vbo.unbind(); } + public boolean valid() { + return vertexCount > 0 && !deleted; + } + /** - * Renders this model, checking first if there is anything to render. + * The VBO/VAO should be bound externally. */ - public void draw() { - if (vertexCount <= 0 || deleted) return; - + public void setupState() { vbo.bind(); - AttribUtil.enableArrays(getAttributeCount()); format.vertexAttribPointers(0); + } - glDrawArrays(primitiveMode.glEnum, 0, vertexCount); - + public void clearState() { AttribUtil.disableArrays(getAttributeCount()); - vbo.unbind(); } + public void drawCall() { + glDrawArrays(primitiveMode.glEnum, 0, vertexCount); + } + /** * Draws many instances of this model, assuming the appropriate state is already bound. */ public void drawInstances(int instanceCount) { - if (vertexCount <= 0 || deleted) return; + if (!valid()) return; Backend.compat.drawInstanced.drawArraysInstanced(primitiveMode, 0, vertexCount, instanceCount); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ElementBuffer.java b/src/main/java/com/jozufozu/flywheel/core/model/ElementBuffer.java new file mode 100644 index 000000000..f9dedeca1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/ElementBuffer.java @@ -0,0 +1,30 @@ +package com.jozufozu.flywheel.core.model; + +import java.nio.ByteBuffer; + +import com.jozufozu.flywheel.backend.gl.GlNumericType; +import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; +import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; + +public class ElementBuffer extends GlBuffer { + + public final int elementCount; + public final GlNumericType eboIndexType; + + public ElementBuffer(ByteBuffer indices, int elementCount, GlNumericType indexType) { + super(GlBufferType.ELEMENT_ARRAY_BUFFER); + this.eboIndexType = indexType; + this.elementCount = elementCount; + + int indicesSize = elementCount * indexType.getByteWidth(); + + bind(); + + alloc(indicesSize); + getBuffer(0, indicesSize) + .put(indices) + .flush(); + + unbind(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/IndexedModel.java b/src/main/java/com/jozufozu/flywheel/core/model/IndexedModel.java new file mode 100644 index 000000000..e88025d6b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/IndexedModel.java @@ -0,0 +1,50 @@ +package com.jozufozu.flywheel.core.model; + +import java.nio.ByteBuffer; + +import org.lwjgl.opengl.GL20; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.GlPrimitive; +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; + +public class IndexedModel extends BufferedModel { + + protected ElementBuffer ebo; + + public IndexedModel(GlPrimitive primitiveMode, VertexFormat modelFormat, ByteBuffer buf, int vertices, ElementBuffer ebo) { + super(primitiveMode, modelFormat, buf, vertices); + + this.ebo = ebo; + } + + @Override + public void setupState() { + super.setupState(); + ebo.bind(); + } + + @Override + public void clearState() { + super.clearState(); + ebo.unbind(); + } + + @Override + public void drawCall() { + GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0); + } + + @Override + public void drawInstances(int instanceCount) { + if (vertexCount <= 0 || deleted) return; + + Backend.compat.drawInstanced.drawElementsInstanced(primitiveMode, ebo.elementCount, ebo.eboIndexType, 0, instanceCount); + } + + @Override + public void delete() { + super.delete(); + ebo.delete(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelRenderer.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelRenderer.java new file mode 100644 index 000000000..97ed214cb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelRenderer.java @@ -0,0 +1,25 @@ +package com.jozufozu.flywheel.core.model; + +public class ModelRenderer { + + protected BufferedModel model; + + public ModelRenderer(BufferedModel model) { + this.model = model; + } + + /** + * Renders this model, checking first if there is anything to render. + */ + public void draw() { + if (!model.valid()) return; + + model.setupState(); + model.drawCall(); + model.clearState(); + } + + public void delete() { + model.delete(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/util/QuadConverter.java b/src/main/java/com/jozufozu/flywheel/util/QuadConverter.java new file mode 100644 index 000000000..4340c99cd --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/QuadConverter.java @@ -0,0 +1,88 @@ +package com.jozufozu.flywheel.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import com.jozufozu.flywheel.backend.gl.GlNumericType; +import com.jozufozu.flywheel.core.model.ElementBuffer; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minecraft.util.math.MathHelper; + +public class QuadConverter { + + private static QuadConverter INSTANCE = new QuadConverter(); + + public static QuadConverter getInstance() { + return INSTANCE; + } + + private final Int2ObjectMap quads2Tris; + + public QuadConverter() { + quads2Tris = new Int2ObjectOpenHashMap<>(); + } + + public ElementBuffer getEboForNQuads(int quads) { + quads2Tris.values().removeIf(CachedEbo::noReferences); + + CachedEbo ebo = quads2Tris.computeIfAbsent(quads, quadCount -> { + int triangleCount = quadCount * 2; + int indexCount = triangleCount * 3; + + GlNumericType type; + + int bitWidth = MathHelper.log2(indexCount); + if (bitWidth <= 8) { + type = GlNumericType.UBYTE; + } else if (bitWidth <= 16) { + type = GlNumericType.USHORT; + } else { + type = GlNumericType.UINT; + } + ByteBuffer indices = ByteBuffer.allocate(indexCount * type.getByteWidth()); + indices.order(ByteOrder.nativeOrder()); + + for (int i = 0; i < quadCount; i++) { + int qStart = 4 * i; + // triangle 1 + type.castAndBuffer(indices, qStart); + type.castAndBuffer(indices, qStart + 1); + type.castAndBuffer(indices, qStart + 2); + // triangle 2 + type.castAndBuffer(indices, qStart); + type.castAndBuffer(indices, qStart + 2); + type.castAndBuffer(indices, qStart + 3); + } + + indices.flip(); + + return new CachedEbo(indices, indexCount, type); + }); + + ebo.refCount++; + + return ebo; + } + + private class CachedEbo extends ElementBuffer { + int refCount = 1; + + public CachedEbo(ByteBuffer indices, int elementCount, GlNumericType indexType) { + super(indices, elementCount, indexType); + } + + @Override + public void delete() { + refCount--; + + if (refCount == 0) + super.delete(); + } + + public boolean noReferences() { + return refCount == 0; + } + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java index 572474654..70c1db87c 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java @@ -10,15 +10,17 @@ import java.util.Map; import javax.annotation.Nullable; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.instancing.IInstanceRendered; -import com.jozufozu.flywheel.core.BufferedModel; -import com.jozufozu.flywheel.core.IndexedModel; +import com.jozufozu.flywheel.core.model.ArrayModelRenderer; +import com.jozufozu.flywheel.core.model.BufferedModel; +import com.jozufozu.flywheel.core.model.IndexedModel; +import com.jozufozu.flywheel.core.model.ModelRenderer; import com.jozufozu.flywheel.light.GridAlignedBB; import com.jozufozu.flywheel.util.BufferBuilderReader; +import com.jozufozu.flywheel.util.QuadConverter; import com.mojang.blaze3d.matrix.MatrixStack; import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; @@ -48,7 +50,7 @@ public class RenderedContraption extends ContraptionWorldHolder { private final ContraptionLighter lighter; public final ContraptionKineticRenderer kinetics; - private final Map renderLayers = new HashMap<>(); + private final Map renderLayers = new HashMap<>(); private Matrix4f model; private AxisAlignedBB lightBox; @@ -70,7 +72,7 @@ public class RenderedContraption extends ContraptionWorldHolder { } public void doRenderLayer(RenderType layer, ContraptionProgram shader) { - BufferedModel structure = renderLayers.get(layer); + ModelRenderer structure = renderLayers.get(layer); if (structure != null) { setup(shader); structure.draw(); @@ -111,7 +113,7 @@ public class RenderedContraption extends ContraptionWorldHolder { } void invalidate() { - for (BufferedModel buffer : renderLayers.values()) { + for (ModelRenderer buffer : renderLayers.values()) { buffer.delete(); } renderLayers.clear(); @@ -122,7 +124,7 @@ public class RenderedContraption extends ContraptionWorldHolder { } private void buildLayers() { - for (BufferedModel buffer : renderLayers.values()) { + for (ModelRenderer buffer : renderLayers.values()) { buffer.delete(); } @@ -132,7 +134,13 @@ public class RenderedContraption extends ContraptionWorldHolder { for (RenderType layer : blockLayers) { BufferedModel layerModel = buildStructureModel(renderWorld, contraption, layer); - if (layerModel != null) renderLayers.put(layer, layerModel); + + if (layerModel != null) { + if (Backend.compat.vertexArrayObjectsSupported()) + renderLayers.put(layer, new ArrayModelRenderer(layerModel)); + else + renderLayers.put(layer, new ModelRenderer(layerModel)); + } } } @@ -195,43 +203,6 @@ public class RenderedContraption extends ContraptionWorldHolder { vertices.rewind(); -//// if (Backend.compat.vertexArrayObjectsSupported()) -//// return new BufferedArrayModel(GlPrimitive.QUADS, format, vertices, vertexCount); -//// else -// return new BufferedModel(GlPrimitive.QUADS, format, vertices, vertexCount); - - int quadCount = vertexCount / 4; - int triangleCount = vertexCount / 2; - int indexCount = triangleCount * 3; - - GlNumericType type; - - int bitWidth = MathHelper.log2(indexCount); - if (bitWidth <= 8) { - type = GlNumericType.UBYTE; - } else if (bitWidth <= 16) { - type = GlNumericType.USHORT; - } else { - type = GlNumericType.UINT; - } - - ByteBuffer indices = ByteBuffer.allocate(indexCount * type.getByteWidth()); - indices.order(ByteOrder.nativeOrder()); - - for (int i = 0; i < quadCount; i++) { - int qStart = 4 * i; - // triangle 1 - type.castAndBuffer(indices, qStart); - type.castAndBuffer(indices, qStart + 1); - type.castAndBuffer(indices, qStart + 2); - // triangle 2 - type.castAndBuffer(indices, qStart); - type.castAndBuffer(indices, qStart + 2); - type.castAndBuffer(indices, qStart + 3); - } - - indices.flip(); - - return new IndexedModel(GlPrimitive.TRIANGLES, format, vertices, vertexCount, indices, indexCount, type); + return new IndexedModel(GlPrimitive.TRIANGLES, format, vertices, vertexCount, QuadConverter.getInstance().getEboForNQuads(vertexCount / 4)); } }