From be60eae9af580011e97752bb88ca1226fc05b21a Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Wed, 11 May 2022 16:32:27 -0700 Subject: [PATCH] Simplifications - Inline a handful of interfaces that had limited use - Removed IndexedModel/VBOModel as a first step in refactoring model uploads - InstancedModels/GPUInstancers support multiple VAOs - Fix padding issue --- .../instancing/instancing/GPUInstancer.java | 18 ++- .../instancing/InstancedMaterial.java | 19 +-- .../instancing/instancing/InstancedModel.java | 100 +++++++++------ .../instancing/InstancingEngine.java | 32 ++--- .../instancing/instancing/Renderable.java | 2 + .../backend/model/ArrayModelRenderer.java | 39 ++---- .../flywheel/backend/model/BufferedModel.java | 40 ------ .../backend/model/FallbackAllocator.java | 15 --- .../flywheel/backend/model/IndexedModel.java | 97 --------------- .../flywheel/backend/model/MeshAllocator.java | 20 --- .../flywheel/backend/model/MeshPool.java | 60 ++++----- .../flywheel/backend/model/VBOModel.java | 86 ------------- .../flywheel/core/BasicModelSupplier.java | 14 +-- .../flywheel/core/hardcoded/ModelPart.java | 2 +- .../flywheel/core/layout/BufferLayout.java | 17 ++- .../flywheel/core/layout/CommonItems.java | 1 - .../flywheel/core/layout/LayoutItem.java | 2 - .../flywheel/core/layout/MatrixItem.java | 5 - .../flywheel/core/layout/Padding.java | 19 --- .../flywheel/core/layout/PrimitiveItem.java | 5 - .../jozufozu/flywheel/core/model/Mesh.java | 8 +- .../flywheel/core/model/ModelSupplier.java | 6 +- .../model/SeparatedWorldModelBuilder.java | 117 ++++++++++++++++++ .../core/model/WorldModelBuilder.java | 4 +- .../flywheel/core/vertex/BlockVertex.java | 4 +- 25 files changed, 271 insertions(+), 461 deletions(-) delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/FallbackAllocator.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/MeshAllocator.java delete mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java delete mode 100644 src/main/java/com/jozufozu/flywheel/core/layout/Padding.java create mode 100644 src/main/java/com/jozufozu/flywheel/core/model/SeparatedWorldModelBuilder.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index 99c7c8b74..d30d9183f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -1,5 +1,8 @@ package com.jozufozu.flywheel.backend.instancing.instancing; +import java.util.HashSet; +import java.util.Set; + import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.Flywheel; @@ -46,13 +49,18 @@ public class GPUInstancer extends AbstractInstancer { return !anyToUpdate && !anyToRemove && glInstanceCount == 0; } + private final Set boundTo = new HashSet<>(); + void renderSetup(GlVertexArray vao) { if (anyToRemove) { removeDeletedInstances(); } vbo.bind(); - if (!realloc(vao)) { + + if (!realloc()) { + + boundTo.clear(); if (anyToRemove) { clearBufferTail(); @@ -65,6 +73,10 @@ public class GPUInstancer extends AbstractInstancer { glInstanceCount = data.size(); } + if (boundTo.add(vao)) { + bindInstanceAttributes(vao); + } + vbo.unbind(); anyToRemove = anyToUpdate = false; @@ -110,7 +122,7 @@ public class GPUInstancer extends AbstractInstancer { } } - private boolean realloc(GlVertexArray vao) { + private boolean realloc() { int size = this.data.size(); int stride = instanceFormat.getStride(); int requiredSize = size * stride; @@ -127,8 +139,6 @@ public class GPUInstancer extends AbstractInstancer { glInstanceCount = size; - bindInstanceAttributes(vao); - return true; } return false; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java index 3e3b3e3a8..700ff4aca 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java @@ -1,7 +1,6 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -12,7 +11,7 @@ import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Material; import com.jozufozu.flywheel.api.struct.Instanced; -import com.jozufozu.flywheel.backend.model.MeshAllocator; +import com.jozufozu.flywheel.backend.model.MeshPool; import com.jozufozu.flywheel.core.model.ModelSupplier; import net.minecraft.client.renderer.RenderType; @@ -54,12 +53,6 @@ public class InstancedMaterial implements Material { .sum(); } - public boolean nothingToRender() { - return models.size() > 0 && models.values() - .stream() - .allMatch(InstancedModel::isEmpty); - } - public void delete() { models.values().forEach(InstancedModel::delete); models.clear(); @@ -76,7 +69,7 @@ public class InstancedMaterial implements Material { .forEach(GPUInstancer::clear); } - public void init(MeshAllocator allocator) { + public void init(MeshPool allocator) { for (var instanced : uninitialized) { var map = instanced.init(allocator); @@ -86,14 +79,6 @@ public class InstancedMaterial implements Material { uninitialized.clear(); } - public void renderIn(RenderType layer) { - renderables.get(layer).forEach(Renderable::render); - } - - public boolean anythingToRender(RenderType type) { - return renderables.get(type).size() > 0; - } - private InstancedModel createInstancer(ModelSupplier model) { var instancer = new InstancedModel<>(new GPUInstancer<>(type), model); uninitialized.add(instancer); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java index 845f7e4cb..bd45f77a3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java @@ -2,61 +2,88 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.Map; -import javax.annotation.Nullable; - import com.google.common.collect.ImmutableMap; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.backend.model.BufferedModel; -import com.jozufozu.flywheel.backend.model.MeshAllocator; +import com.jozufozu.flywheel.backend.model.MeshPool; +import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.ModelSupplier; +import com.jozufozu.flywheel.util.Pair; import net.minecraft.client.renderer.RenderType; public class InstancedModel { - GPUInstancer instancer; - ModelSupplier model; - - @Nullable - private BufferedModel bufferedMesh; - @Nullable - private GlVertexArray vao; + final GPUInstancer instancer; + final ModelSupplier model; + private Map layers; public InstancedModel(GPUInstancer instancer, ModelSupplier model) { this.instancer = instancer; this.model = model; } - public Map init(MeshAllocator allocator) { + public Map init(MeshPool allocator) { instancer.init(); - vao = new GlVertexArray(); + layers = model.get() + .entrySet() + .stream() + .map(entry -> Pair.of(entry.getKey(), new Layer(allocator, entry.getKey(), entry.getValue()))) + .collect(ImmutableMap.toImmutableMap(Pair::first, Pair::second)); - bufferedMesh = allocator.alloc(model.get(), vao); - instancer.attributeBaseIndex = bufferedMesh.getAttributeCount(); - vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount()); - return ImmutableMap.of(RenderType.solid(), this::render); + return layers; } - public void render() { - if (invalid()) return; + private class Layer implements Renderable { - vao.bind(); + final RenderType type; + MeshPool.BufferedMesh bufferedMesh; + GlVertexArray vao; - instancer.renderSetup(vao); - - if (instancer.glInstanceCount > 0) { - bufferedMesh.drawInstances(instancer.glInstanceCount); + private Layer(MeshPool allocator, RenderType type, Mesh mesh) { + this.type = type; + vao = new GlVertexArray(); + bufferedMesh = allocator.alloc(mesh, vao); + instancer.attributeBaseIndex = bufferedMesh.getAttributeCount(); + vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount()); } - // persistent mapping sync point - instancer.vbo.doneForThisFrame(); - } + @Override + public void render() { + if (invalid()) return; - private boolean invalid() { - return instancer.vbo == null || bufferedMesh == null || vao == null; + vao.bind(); + + instancer.renderSetup(vao); + + if (instancer.glInstanceCount > 0) { + bufferedMesh.drawInstances(instancer.glInstanceCount); + } + + // persistent mapping sync point + instancer.vbo.doneForThisFrame(); + } + + @Override + public boolean shouldRemove() { + return invalid(); + } + + private boolean invalid() { + return instancer.vbo == null || bufferedMesh == null || vao == null; + } + + public void delete() { + if (invalid()) return; + + vao.delete(); + bufferedMesh.delete(); + + vao = null; + bufferedMesh = null; + } } public GPUInstancer getInstancer() { @@ -71,19 +98,14 @@ public class InstancedModel { return model.getVertexCount() * instancer.glInstanceCount; } - public boolean isEmpty() { - return instancer.isEmpty(); - } - public void delete() { - if (invalid()) return; + if (instancer.vbo == null) return; - vao.delete(); - bufferedMesh.delete(); instancer.vbo.delete(); - - vao = null; - bufferedMesh = null; instancer.vbo = null; + + for (var layer : layers.values()) { + layer.delete(); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 6af4b7add..865cc4d18 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -11,11 +11,8 @@ import javax.annotation.Nonnull; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.TaskEngine; -import com.jozufozu.flywheel.backend.model.FallbackAllocator; -import com.jozufozu.flywheel.backend.model.MeshAllocator; import com.jozufozu.flywheel.backend.model.MeshPool; import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.RenderContext; @@ -41,7 +38,7 @@ public class InstancingEngine

implements Engine { protected BlockPos originCoordinate = BlockPos.ZERO; protected final ProgramCompiler

context; - private MeshAllocator allocator; + private MeshPool allocator; protected final Map, InstancedMaterial> materials = new HashMap<>(); @@ -110,7 +107,10 @@ public class InstancingEngine

implements Engine { for (Map.Entry, InstancedMaterial> entry : materials.entrySet()) { InstancedMaterial material = entry.getValue(); - if (material.anythingToRender(type)) { + var toRender = material.renderables.get(type); + toRender.removeIf(Renderable::shouldRemove); + + if (!toRender.isEmpty()) { Instanced instanceType = entry.getKey(); setup(type, camX, camY, camZ, viewProjection, instanceType.getProgramSpec()); @@ -118,7 +118,7 @@ public class InstancingEngine

implements Engine { instanceCount += material.getInstanceCount(); vertexCount += material.getVertexCount(); - material.renderIn(type); + toRender.forEach(Renderable::render); } } @@ -165,7 +165,7 @@ public class InstancingEngine

implements Engine { public void beginFrame(Camera info) { checkOriginDistance(info); - MeshAllocator allocator = getModelAllocator(); + MeshPool allocator = getModelAllocator(); for (InstancedMaterial material : materials.values()) { material.init(allocator); @@ -173,10 +173,7 @@ public class InstancingEngine

implements Engine { toRender.addAll(material.renderables.keySet()); } - if (allocator instanceof MeshPool pool) { - // ...and then flush the model arena in case anything was marked for upload - pool.flush(); - } + allocator.flush(); } @@ -211,20 +208,17 @@ public class InstancingEngine

implements Engine { info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); } - private MeshAllocator getModelAllocator() { + private MeshPool getModelAllocator() { if (allocator == null) { allocator = createAllocator(); } return this.allocator; } - private static MeshAllocator createAllocator() { - if (GlCompat.getInstance() - .onAMDWindows()) { - return FallbackAllocator.INSTANCE; - } else { - return new MeshPool(Formats.POS_TEX_NORMAL); - } + private static MeshPool createAllocator() { + + // FIXME: Windows AMD Drivers don't like ..BaseVertex + return new MeshPool(Formats.POS_TEX_NORMAL); } @FunctionalInterface diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/Renderable.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/Renderable.java index f984b6493..4ac104a4d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/Renderable.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/Renderable.java @@ -3,4 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.instancing; public interface Renderable { void render(); + + boolean shouldRemove(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java index c4d47bdbd..79809edf8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java @@ -1,52 +1,31 @@ package com.jozufozu.flywheel.backend.model; import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.model.BlockMesh; public class ArrayModelRenderer { - private final Mesh mesh; - protected GlVertexArray vao; - protected BufferedModel vbo; - protected boolean initialized; + protected final GlVertexArray vao; + protected final MeshPool.BufferedMesh mesh; - public ArrayModelRenderer(Mesh mesh) { - this.mesh = mesh; + public ArrayModelRenderer(BlockMesh mesh, MeshPool meshPool) { + this.vao = new GlVertexArray(); + this.mesh = meshPool.alloc(mesh, this.vao); } /** * Renders this model, checking first if there is anything to render. */ public void draw() { - if (!initialized) init(); - if (!isValid()) return; + if (mesh.isDeleted()) return; vao.bind(); - vbo.drawCall(); - } - - protected void init() { - initialized = true; - - if (mesh.empty()) return; - - this.vbo = new IndexedModel(mesh); - - vao = new GlVertexArray(); - - // bind the model's vbo to our vao - this.vbo.setupState(vao); - - GlVertexArray.unbind(); + mesh.drawCall(); } public void delete() { - if (vbo != null) - vbo.delete(); + mesh.delete(); } - protected boolean isValid() { - return vbo != null && vbo.valid(); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java deleted file mode 100644 index 5dc2e3107..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.jozufozu.flywheel.backend.model; - -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.core.layout.BufferLayout; - -public interface BufferedModel { - - VertexType getType(); - - int getVertexCount(); - - /** - * The VAO must be bound externally. - */ - void setupState(GlVertexArray vao); - - void drawCall(); - - /** - * Draws many instances of this model, assuming the appropriate state is already bound. - */ - void drawInstances(int instanceCount); - - boolean isDeleted(); - - void delete(); - - default BufferLayout getLayout() { - return getType().getLayout(); - } - - default boolean valid() { - return getVertexCount() > 0 && !isDeleted(); - } - - default int getAttributeCount() { - return getType().getLayout().getAttributeCount(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/FallbackAllocator.java b/src/main/java/com/jozufozu/flywheel/backend/model/FallbackAllocator.java deleted file mode 100644 index 1f276016a..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/model/FallbackAllocator.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jozufozu.flywheel.backend.model; - -import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.core.model.Mesh; - -public enum FallbackAllocator implements MeshAllocator { - INSTANCE; - - @Override - public BufferedModel alloc(Mesh mesh, GlVertexArray vao) { - IndexedModel out = new IndexedModel(mesh); - out.setupState(vao); - return out; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java deleted file mode 100644 index d2129ad09..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.jozufozu.flywheel.backend.model; - -import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GL31; - -import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.GlPrimitive; -import com.jozufozu.flywheel.backend.gl.GlVertexArray; -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.backend.gl.buffer.MappedGlBuffer; -import com.jozufozu.flywheel.core.model.Mesh; - -/** - * An indexed triangle model. Just what the driver ordered. - * - *
This should be favored over a normal BufferedModel. - */ -public class IndexedModel implements BufferedModel { - - protected final Mesh mesh; - protected final GlPrimitive primitiveMode; - protected ElementBuffer ebo; - protected GlBuffer vbo; - protected boolean deleted; - - public IndexedModel(Mesh mesh) { - this.mesh = mesh; - this.primitiveMode = GlPrimitive.TRIANGLES; - - vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); - - vbo.bind(); - // allocate the buffer on the gpu - vbo.ensureCapacity(mesh.size()); - - // mirror it in system memory, so we can write to it, and upload our model. - try (MappedBuffer buffer = vbo.getBuffer()) { - mesh.writeInto(buffer.unwrap()); - } catch (Exception e) { - Flywheel.LOGGER.error(String.format("Error uploading model '%s':", mesh.name()), e); - } - - vbo.unbind(); - - this.ebo = mesh.createEBO(); - } - - /** - * The VBO/VAO should be bound externally. - */ - public void setupState(GlVertexArray vao) { - vbo.bind(); - vao.enableArrays(getAttributeCount()); - vao.bindAttributes(0, getType().getLayout()); - } - - @Override - public void drawCall() { - ebo.bind(); - GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0); - } - - /** - * Draws many instances of this model, assuming the appropriate state is already bound. - */ - @Override - public void drawInstances(int instanceCount) { - if (!valid()) return; - - ebo.bind(); - - GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount); - } - - public boolean isDeleted() { - return deleted; - } - - @Override - public VertexType getType() { - return mesh.getType(); - } - - public int getVertexCount() { - return mesh.vertexCount(); - } - - public void delete() { - if (deleted) return; - - deleted = true; - vbo.delete(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/MeshAllocator.java b/src/main/java/com/jozufozu/flywheel/backend/model/MeshAllocator.java deleted file mode 100644 index 371b80fbf..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/model/MeshAllocator.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jozufozu.flywheel.backend.model; - -import com.jozufozu.flywheel.backend.gl.GlVertexArray; -import com.jozufozu.flywheel.core.model.Mesh; - -public interface MeshAllocator { - /** - * Allocate a model. - * - * @param mesh The model to allocate. - * @param vao The vertex array object to attach the model to. - * @return A handle to the allocated model. - */ - BufferedModel alloc(Mesh mesh, GlVertexArray vao); - - @FunctionalInterface - interface Callback { - void onAlloc(BufferedModel arenaModel); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java index 70fb5acbd..0bcaf66b1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java @@ -16,13 +16,13 @@ import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; import com.jozufozu.flywheel.core.model.Mesh; -public class MeshPool implements MeshAllocator { +public class MeshPool { protected final VertexType vertexType; - private final List models = new ArrayList<>(); + private final List models = new ArrayList<>(); - private final List pendingUpload = new ArrayList<>(); + private final List pendingUpload = new ArrayList<>(); private final GlBuffer vbo; @@ -53,10 +53,9 @@ public class MeshPool implements MeshAllocator { * @param vao The vertex array object to attach the model to. * @return A handle to the allocated model. */ - @Override - public PooledModel alloc(Mesh mesh, GlVertexArray vao) { - PooledModel bufferedModel = new PooledModel(vao, mesh, vertices); - vertices += mesh.vertexCount(); + public BufferedMesh alloc(Mesh mesh, GlVertexArray vao) { + BufferedMesh bufferedModel = new BufferedMesh(vao, mesh, vertices); + vertices += mesh.getVertexCount(); models.add(bufferedModel); pendingUpload.add(bufferedModel); @@ -84,17 +83,17 @@ public class MeshPool implements MeshAllocator { private void processDeletions() { // remove deleted models - models.removeIf(PooledModel::isDeleted); + models.removeIf(BufferedMesh::isDeleted); // re-evaluate first vertex for each model int vertices = 0; - for (PooledModel model : models) { + for (BufferedMesh model : models) { if (model.first != vertices) pendingUpload.add(model); model.first = vertices; - vertices += model.mesh.vertexCount(); + vertices += model.mesh.getVertexCount(); } this.vertices = vertices; @@ -115,12 +114,12 @@ public class MeshPool implements MeshAllocator { VertexWriter writer = vertexType.createWriter(buffer.unwrap()); int vertices = 0; - for (PooledModel model : models) { + for (BufferedMesh model : models) { model.first = vertices; model.buffer(writer); - vertices += model.mesh.vertexCount(); + vertices += model.mesh.getVertexCount(); } } catch (Exception e) { @@ -131,7 +130,7 @@ public class MeshPool implements MeshAllocator { private void uploadPending() { try (MappedBuffer buffer = vbo.getBuffer()) { VertexWriter writer = vertexType.createWriter(buffer.unwrap()); - for (PooledModel model : pendingUpload) { + for (BufferedMesh model : pendingUpload) { model.buffer(writer); } pendingUpload.clear(); @@ -148,7 +147,7 @@ public class MeshPool implements MeshAllocator { vbo.delete(); } - public class PooledModel implements BufferedModel { + public class BufferedMesh { private final ElementBuffer ebo; private final GlVertexArray vao; @@ -158,38 +157,20 @@ public class MeshPool implements MeshAllocator { private boolean deleted; - public PooledModel(GlVertexArray vao, Mesh mesh, int first) { + public BufferedMesh(GlVertexArray vao, Mesh mesh, int first) { this.vao = vao; this.mesh = mesh; this.first = first; ebo = mesh.createEBO(); } - @Override - public VertexType getType() { - return MeshPool.this.vertexType; - } - - @Override - public int getVertexCount() { - return mesh.vertexCount(); - } - - @Override - public void setupState(GlVertexArray vao) { - vbo.bind(); - vao.enableArrays(getAttributeCount()); - vao.bindAttributes(0, vertexType.getLayout()); - } - - @Override public void drawCall() { + ebo.bind(); GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, first); } - @Override public void drawInstances(int instanceCount) { - if (!valid()) return; + if (mesh.getVertexCount() <= 0 || isDeleted()) return; ebo.bind(); @@ -198,12 +179,10 @@ public class MeshPool implements MeshAllocator { GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount, first); } - @Override public boolean isDeleted() { return deleted; } - @Override public void delete() { setDirty(); anyToRemove = true; @@ -214,7 +193,12 @@ public class MeshPool implements MeshAllocator { writer.seekToVertex(first); writer.writeVertexList(mesh.getReader()); - setupState(vao); + vao.enableArrays(getAttributeCount()); + vao.bindAttributes(0, vertexType.getLayout()); + } + + public int getAttributeCount() { + return MeshPool.this.vertexType.getLayout().getAttributeCount(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java deleted file mode 100644 index 100fbf9d7..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/model/VBOModel.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.jozufozu.flywheel.backend.model; - -import static org.lwjgl.opengl.GL11.glDrawArrays; - -import org.lwjgl.opengl.GL31; - -import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.api.vertex.VertexType; -import com.jozufozu.flywheel.backend.gl.GlPrimitive; -import com.jozufozu.flywheel.backend.gl.GlVertexArray; -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.backend.gl.buffer.MappedGlBuffer; -import com.jozufozu.flywheel.core.model.Mesh; - -public class VBOModel implements BufferedModel { - - protected final Mesh mesh; - protected final GlPrimitive primitiveMode; - protected GlBuffer vbo; - protected boolean deleted; - - public VBOModel(GlPrimitive primitiveMode, Mesh mesh) { - this.mesh = mesh; - this.primitiveMode = primitiveMode; - - vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); - - vbo.bind(); - // allocate the buffer on the gpu - vbo.ensureCapacity(mesh.size()); - - // mirror it in system memory, so we can write to it, and upload our model. - try (MappedBuffer buffer = vbo.getBuffer()) { - mesh.writeInto(buffer.unwrap()); - } catch (Exception e) { - Flywheel.LOGGER.error(String.format("Error uploading model '%s':", mesh.name()), e); - } - - vbo.unbind(); - } - - public boolean isDeleted() { - return deleted; - } - - @Override - public VertexType getType() { - return mesh.getType(); - } - - public int getVertexCount() { - return mesh.vertexCount(); - } - - /** - * The VBO/VAO should be bound externally. - */ - public void setupState(GlVertexArray vao) { - vbo.bind(); - vao.enableArrays(getAttributeCount()); - vao.bindAttributes(0, getLayout()); - } - - public void drawCall() { - glDrawArrays(primitiveMode.glEnum, 0, mesh.vertexCount()); - } - - /** - * Draws many instances of this model, assuming the appropriate state is already bound. - */ - public void drawInstances(int instanceCount) { - if (!valid()) return; - - GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, mesh.vertexCount(), instanceCount); - } - - public void delete() { - if (deleted) return; - - deleted = true; - vbo.delete(); - } -} - diff --git a/src/main/java/com/jozufozu/flywheel/core/BasicModelSupplier.java b/src/main/java/com/jozufozu/flywheel/core/BasicModelSupplier.java index b7c66e0cb..cd606b35a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/BasicModelSupplier.java +++ b/src/main/java/com/jozufozu/flywheel/core/BasicModelSupplier.java @@ -1,7 +1,10 @@ package com.jozufozu.flywheel.core; +import java.util.Map; + import javax.annotation.Nonnull; +import com.google.common.collect.ImmutableMap; import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.model.ModelSupplier; import com.jozufozu.flywheel.util.Lazy; @@ -33,17 +36,12 @@ public class BasicModelSupplier implements ModelSupplier { } @Override - public Mesh get() { - return supplier.get(); - } - - @Nonnull - public RenderType getRenderType() { - return renderType; + public Map get() { + return ImmutableMap.of(renderType, supplier.get()); } public int getVertexCount() { - return supplier.map(Mesh::vertexCount) + return supplier.map(Mesh::getVertexCount) .orElse(0); } diff --git a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java index e946c3a33..a70646760 100644 --- a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java @@ -45,7 +45,7 @@ public class ModelPart implements Mesh { } @Override - public int vertexCount() { + public int getVertexCount() { return vertices; } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java index 3e8ae1a0e..5673f0126 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java @@ -23,17 +23,16 @@ public class BufferLayout { private final int stride; - public BufferLayout(List layoutItems) { + public BufferLayout(List layoutItems, int padding) { ImmutableList.Builder attributes = ImmutableList.builder(); - stride = calculateStride(layoutItems); - for (LayoutItem item : layoutItems) { item.provideAttributes(attributes::add); } this.attributes = attributes.build(); + this.stride = calculateStride(this.attributes) + padding; } public Collection getAttributes() { @@ -52,9 +51,9 @@ public class BufferLayout { return new Builder(); } - private static int calculateStride(List layoutItems) { + private static int calculateStride(List layoutItems) { int stride = 0; - for (LayoutItem spec : layoutItems) { + for (VertexAttribute spec : layoutItems) { stride += spec.getByteWidth(); } return stride; @@ -62,6 +61,7 @@ public class BufferLayout { public static class Builder { private final ImmutableList.Builder allItems; + private int padding; public Builder() { allItems = ImmutableList.builder(); @@ -72,8 +72,13 @@ public class BufferLayout { return this; } + public Builder withPadding(int padding) { + this.padding = padding; + return this; + } + public BufferLayout build() { - return new BufferLayout(allItems.build()); + return new BufferLayout(allItems.build(), padding); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java b/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java index 5cae77a0f..32c59b3b4 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java @@ -19,7 +19,6 @@ public class CommonItems { public static final PrimitiveItem LIGHT_SHORT = new PrimitiveItem(GlNumericType.USHORT, 2, true); public static final PrimitiveItem NORMALIZED_BYTE = new PrimitiveItem(GlNumericType.BYTE, 1, true); - public static final LayoutItem PADDING_BYTE = new Padding(1); public static final MatrixItem MAT3 = new MatrixItem(3, 3); public static final MatrixItem MAT4 = new MatrixItem(4, 4); diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java index 69d2ed978..6e9217425 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java @@ -8,6 +8,4 @@ public interface LayoutItem { void provideAttributes(Consumer consumer); - int getByteWidth(); - } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItem.java index fbcd53a53..62682d43b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItem.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItem.java @@ -14,9 +14,4 @@ public record MatrixItem(int rows, int cols) implements LayoutItem { } } - @Override - public int getByteWidth() { - return GlNumericType.FLOAT.getByteWidth() * rows * cols; - } - } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/Padding.java b/src/main/java/com/jozufozu/flywheel/core/layout/Padding.java deleted file mode 100644 index 033752cfc..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/layout/Padding.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jozufozu.flywheel.core.layout; - -import java.util.function.Consumer; - -import com.jozufozu.flywheel.backend.gl.VertexAttribute; - -record Padding(int bytes) implements LayoutItem { - - @Override - public void provideAttributes(Consumer consumer) { - - } - - @Override - public int getByteWidth() { - return bytes; - } - -} diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java index 323ac2c6f..f6218238c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java @@ -22,9 +22,4 @@ public class PrimitiveItem implements LayoutItem { consumer.accept(attribute); } - @Override - public int getByteWidth() { - return attribute.getByteWidth(); - } - } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java b/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java index f0b843f7d..18faef22c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/Mesh.java @@ -39,7 +39,7 @@ public interface Mesh { /** * @return The number of vertices the model has. */ - default int vertexCount() { + default int getVertexCount() { return getReader().getVertexCount(); } @@ -59,14 +59,14 @@ public interface Mesh { */ default ElementBuffer createEBO() { return QuadConverter.getInstance() - .quads2Tris(vertexCount() / 4); + .quads2Tris(getVertexCount() / 4); } /** * The size in bytes that this model's data takes up. */ default int size() { - return getType().byteOffset(vertexCount()); + return getType().byteOffset(getVertexCount()); } /** @@ -74,7 +74,7 @@ public interface Mesh { * @return true if there are no vertices. */ default boolean empty() { - return vertexCount() == 0; + return getVertexCount() == 0; } default void writeInto(ByteBuffer buffer) { diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelSupplier.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelSupplier.java index 82ea5d76c..111a126ce 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelSupplier.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelSupplier.java @@ -1,8 +1,12 @@ package com.jozufozu.flywheel.core.model; +import java.util.Map; + +import net.minecraft.client.renderer.RenderType; + public interface ModelSupplier { - Mesh get(); + Map get(); int getVertexCount(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/SeparatedWorldModelBuilder.java b/src/main/java/com/jozufozu/flywheel/core/model/SeparatedWorldModelBuilder.java new file mode 100644 index 000000000..7a9443f19 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/SeparatedWorldModelBuilder.java @@ -0,0 +1,117 @@ +package com.jozufozu.flywheel.core.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.function.Function; + +import com.google.common.collect.ImmutableMap; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; + +import net.minecraft.client.renderer.ItemBlockRenderTypes; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.ModelBlockRenderer; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.model.data.EmptyModelData; +import net.minecraftforge.client.model.data.IModelData; + +public class SeparatedWorldModelBuilder { + + private PoseStack poseStack = new PoseStack(); + private Map modelData = Collections.emptyMap(); + private BlockAndTintGetter renderWorld = VirtualEmptyBlockGetter.INSTANCE; + private Collection blocks = Collections.emptyList(); + + public Map getMeshes() { + Map builders = new HashMap<>(); + + ModelBlockRenderer modelRenderer = ModelUtil.VANILLA_RENDERER.getModelRenderer(); + + buffer(modelRenderer, new Random(), type -> builders.computeIfAbsent(type, $ -> { + var out = new BufferBuilder(512); + + out.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); + + return out; + })); + + return builders.entrySet() + .stream() + .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, e -> { + var b = e.getValue(); + + b.end(); + + return new BlockMesh(Formats.BLOCK.createReader(b), ""); + })); + } + + public void buffer(ModelBlockRenderer modelRenderer, Random random, Function consumer) { + ModelBlockRenderer.enableCaching(); + for (StructureTemplate.StructureBlockInfo info : this.blocks) { + var state = info.state; + + if (state.getRenderShape() != RenderShape.MODEL) continue; + + var pos = info.pos; + var seed = state.getSeed(pos); + var data = this.modelData.getOrDefault(pos, EmptyModelData.INSTANCE); + var blockModel = ModelUtil.VANILLA_RENDERER.getBlockModel(state); + + this.poseStack.pushPose(); + this.poseStack.translate(pos.getX(), pos.getY(), pos.getZ()); + + for (RenderType type : RenderType.chunkBufferLayers()) { + if (!ItemBlockRenderTypes.canRenderInLayer(state, type)) { + continue; + } + + var vertexConsumer = consumer.apply(type); + + if (vertexConsumer == null) { + continue; + } + + ForgeHooksClient.setRenderType(type); + + modelRenderer.tesselateBlock(this.renderWorld, blockModel, state, pos, poseStack, vertexConsumer, true, random, seed, OverlayTexture.NO_OVERLAY, data); + } + this.poseStack.popPose(); + } + ForgeHooksClient.setRenderType(null); + ModelBlockRenderer.clearCache(); + } + + public SeparatedWorldModelBuilder withRenderWorld(BlockAndTintGetter renderWorld) { + this.renderWorld = renderWorld; + return this; + } + + public SeparatedWorldModelBuilder withBlocks(Collection blocks) { + this.blocks = blocks; + return this; + } + + public SeparatedWorldModelBuilder withModelData(Map modelData) { + this.modelData = modelData; + return this; + } + + public SeparatedWorldModelBuilder withPoseStack(PoseStack poseStack) { + this.poseStack = poseStack; + return this; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java index 86e4da233..80f4f8470 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModelBuilder.java @@ -77,7 +77,7 @@ public final class WorldModelBuilder implements Bufferable { return this; } - public BlockMesh finish() { - return new BlockMesh(this, "name"); + public BlockMesh intoMesh(String name) { + return new BlockMesh(this, name); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java index d74dde894..a554d7f2c 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java @@ -18,8 +18,8 @@ public class BlockVertex implements VertexType { CommonItems.RGBA, CommonItems.UV, CommonItems.LIGHT_SHORT, - CommonItems.NORMAL, - CommonItems.PADDING_BYTE) + CommonItems.NORMAL) + .withPadding(1) .build(); @Override