diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java index 22092e6ca..d24867f8e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java @@ -97,6 +97,14 @@ public class VecBuffer { return this; } + public VecBuffer putColor(byte r, byte g, byte b, byte a) { + internal.put(r); + internal.put(g); + internal.put(b); + internal.put(a); + return this; + } + public VecBuffer putVec3(float x, float y, float z) { internal.putFloat(x); internal.putFloat(y); 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 61bd13723..132cbd9f2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java @@ -11,9 +11,9 @@ 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.material.MaterialSpec; -import com.jozufozu.flywheel.backend.model.BufferedModel; +import com.jozufozu.flywheel.backend.model.IBufferedModel; +import com.jozufozu.flywheel.backend.model.IndexedModel; import com.jozufozu.flywheel.core.model.IModel; -import com.jozufozu.flywheel.core.model.ModelUtil; import com.jozufozu.flywheel.util.AttribUtil; /** @@ -37,7 +37,7 @@ import com.jozufozu.flywheel.util.AttribUtil; public class Instancer { protected final Supplier gen; - protected BufferedModel model; + protected IBufferedModel model; protected final VertexFormat instanceFormat; protected final IInstanceFactory factory; @@ -92,7 +92,7 @@ public class Instancer { } private void init() { - model = ModelUtil.getIndexedModel(gen.get()); + model = new IndexedModel(gen.get()); initialized = true; if (model.getVertexCount() <= 0) 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 5347e74d1..17bcff621 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java @@ -1,23 +1,23 @@ package com.jozufozu.flywheel.backend.model; +import java.util.function.Supplier; + import com.jozufozu.flywheel.backend.gl.GlVertexArray; +import com.jozufozu.flywheel.core.model.IModel; +import com.jozufozu.flywheel.util.AttribUtil; public class ArrayModelRenderer extends ModelRenderer { protected GlVertexArray vao; - public ArrayModelRenderer(BufferedModel model) { + public ArrayModelRenderer(Supplier model) { super(model); - vao = new GlVertexArray(); - - vao.bind(); - model.setupState(); - vao.unbind(); - model.clearState(); } + @Override public void draw() { - if (!model.valid()) return; + if (!isInitialized()) init(); + if (!isValid()) return; vao.bind(); @@ -25,4 +25,31 @@ public class ArrayModelRenderer extends ModelRenderer { vao.unbind(); } + + private boolean isValid() { + return model != null && model.valid(); + } + + @Override + protected void init() { + initialized = true; + IModel model = modelSupplier.get(); + + if (model.vertexCount() <= 0) return; + + this.model = new IndexedModel(model); + + vao = new GlVertexArray(); + + vao.bind(); + + // bind the model's vbo to our vao + this.model.setupState(); + + AttribUtil.enableArrays(this.model.getAttributeCount()); + + vao.unbind(); + + this.model.clearState(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java index 3c775cfbb..5808c177e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java @@ -9,46 +9,45 @@ 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.backend.gl.buffer.MappedBuffer; +import com.jozufozu.flywheel.core.model.IModel; import com.jozufozu.flywheel.util.AttribUtil; -public class BufferedModel { +public class BufferedModel implements IBufferedModel { + protected final IModel model; protected final GlPrimitive primitiveMode; - protected final ByteBuffer data; - protected final VertexFormat format; - protected final int vertexCount; protected GlBuffer vbo; protected boolean deleted; - public BufferedModel(GlPrimitive primitiveMode, VertexFormat format, ByteBuffer data, int vertices) { + public BufferedModel(GlPrimitive primitiveMode, IModel model) { + this.model = model; this.primitiveMode = primitiveMode; - this.data = data; - this.format = format; - this.vertexCount = vertices; vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); vbo.bind(); // allocate the buffer on the gpu - vbo.alloc(this.data.capacity()); + vbo.alloc(model.size()); // mirror it in system memory so we can write to it, and upload our model. - vbo.getBuffer(0, this.data.capacity()) - .put(this.data) - .flush(); + MappedBuffer buffer = vbo.getBuffer(0, model.size()); + model.buffer(buffer); + buffer.flush(); + vbo.unbind(); } public VertexFormat getFormat() { - return format; + return model.format(); } public int getVertexCount() { - return vertexCount; + return model.vertexCount(); } public boolean valid() { - return vertexCount > 0 && !deleted; + return getVertexCount() > 0 && !deleted; } /** @@ -57,7 +56,7 @@ public class BufferedModel { public void setupState() { vbo.bind(); AttribUtil.enableArrays(getAttributeCount()); - format.vertexAttribPointers(0); + getFormat().vertexAttribPointers(0); } public void clearState() { @@ -66,7 +65,7 @@ public class BufferedModel { } public void drawCall() { - glDrawArrays(primitiveMode.glEnum, 0, vertexCount); + glDrawArrays(primitiveMode.glEnum, 0, getVertexCount()); } /** @@ -75,7 +74,7 @@ public class BufferedModel { public void drawInstances(int instanceCount) { if (!valid()) return; - Backend.getInstance().compat.drawInstanced.drawArraysInstanced(primitiveMode, 0, vertexCount, instanceCount); + Backend.getInstance().compat.drawInstanced.drawArraysInstanced(primitiveMode, 0, getVertexCount(), instanceCount); } public void delete() { @@ -84,10 +83,5 @@ public class BufferedModel { deleted = true; vbo.delete(); } - - public int getAttributeCount() { - return format.getAttributeCount(); - } - } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java new file mode 100644 index 000000000..4ac73c572 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java @@ -0,0 +1,32 @@ +package com.jozufozu.flywheel.backend.model; + +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; + +public interface IBufferedModel { + + VertexFormat getFormat(); + + int getVertexCount(); + + boolean valid(); + + /** + * The VBO/VAO should be bound externally. + */ + void setupState(); + + void clearState(); + + void drawCall(); + + /** + * Draws many instances of this model, assuming the appropriate state is already bound. + */ + void drawInstances(int instanceCount); + + void delete(); + + default int getAttributeCount() { + return getFormat().getAttributeCount(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java index 628491766..9ae5c8c47 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/IndexedModel.java @@ -8,6 +8,7 @@ import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.core.model.IModel; /** * An indexed triangle model. Just what the driver ordered. @@ -18,15 +19,10 @@ public class IndexedModel extends BufferedModel { protected ElementBuffer ebo; - public IndexedModel(VertexFormat modelFormat, ByteBuffer buf, int vertices, ElementBuffer ebo) { - super(GlPrimitive.TRIANGLES, modelFormat, buf, vertices); + public IndexedModel(IModel model) { + super(GlPrimitive.TRIANGLES, model); - this.ebo = ebo; - } - - public static IndexedModel fromSequentialQuads(VertexFormat modelFormat, ByteBuffer quads, int vertices) { - return new IndexedModel(modelFormat, quads, vertices, QuadConverter.getInstance() - .quads2Tris(vertices / 4)); + this.ebo = model.createEBO(); } @Override @@ -48,13 +44,8 @@ public class IndexedModel extends BufferedModel { @Override public void drawInstances(int instanceCount) { - if (vertexCount <= 0 || deleted) return; + if (!valid()) return; Backend.getInstance().compat.drawInstanced.drawElementsInstanced(primitiveMode, ebo.elementCount, ebo.eboIndexType, 0, instanceCount); } - - @Override - public void delete() { - super.delete(); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java index fd1efd896..e0a2e97b9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelRenderer.java @@ -1,17 +1,26 @@ package com.jozufozu.flywheel.backend.model; +import java.util.function.Supplier; + +import com.jozufozu.flywheel.core.model.IModel; + public class ModelRenderer { - protected BufferedModel model; + protected Supplier modelSupplier; + protected IBufferedModel model; - public ModelRenderer(BufferedModel model) { - this.model = model; + protected boolean initialized; + + public ModelRenderer(Supplier modelSupplier) { + this.modelSupplier = modelSupplier; } /** * Renders this model, checking first if there is anything to render. */ public void draw() { + + if (!isInitialized()) init(); if (!model.valid()) return; model.setupState(); @@ -19,7 +28,21 @@ public class ModelRenderer { model.clearState(); } + protected void init() { + initialized = true; + IModel model = modelSupplier.get(); + + if (model.vertexCount() <= 0) return; + + this.model = new IndexedModel(model); + } + + public boolean isInitialized() { + return initialized; + } + public void delete() { - model.delete(); + if (model != null) + model.delete(); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/Formats.java b/src/main/java/com/jozufozu/flywheel/core/Formats.java index e7cca829a..6632c2117 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Formats.java +++ b/src/main/java/com/jozufozu/flywheel/core/Formats.java @@ -9,12 +9,21 @@ public class Formats { .addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, CommonAttributes.UV) .build(); + public static final VertexFormat COLORED_LIT_MODEL = VertexFormat.builder() + .addAttributes(CommonAttributes.VEC3, + CommonAttributes.NORMAL, + CommonAttributes.UV, + CommonAttributes.RGBA, + CommonAttributes.LIGHT) + .build(); + public static final VertexFormat TRANSFORMED = litInstance().addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3) .build(); + public static final VertexFormat ORIENTED = litInstance().addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION) .build(); - public static VertexFormat.Builder litInstance() { + public static VertexFormat.Builder litInstance() { return VertexFormat.builder() .addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java index 631c87da1..8ddb4aed2 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java @@ -23,6 +23,9 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; +/** + * A model of a single block. + */ public class BlockModel implements IModel { private static final MatrixStack IDENTITY = new MatrixStack(); @@ -69,12 +72,6 @@ public class BlockModel implements IModel { } } - @Override - public ElementBuffer createEBO() { - return QuadConverter.getInstance() - .quads2Tris(vertexCount() / 4); - } - public static BufferBuilder getBufferBuilder(IBakedModel model, BlockState referenceState, MatrixStack ms) { Minecraft mc = Minecraft.getInstance(); BlockRendererDispatcher dispatcher = mc.getBlockRenderer(); diff --git a/src/main/java/com/jozufozu/flywheel/core/model/IModel.java b/src/main/java/com/jozufozu/flywheel/core/model/IModel.java index 4c3e9d027..7bc41a9ae 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/IModel.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/IModel.java @@ -3,9 +3,27 @@ package com.jozufozu.flywheel.core.model; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.backend.model.ElementBuffer; +import com.jozufozu.flywheel.core.QuadConverter; /** * A model that can be rendered by flywheel. + * + *

+ * It is expected that the following assertion will not fail: + *

+ * + *
{@code
+ * IModel model = ...;
+ * VecBuffer into = ...;
+ *
+ * int initial = VecBuffer.unwrap().position();
+ *
+ * model.buffer(into);
+ *
+ * int final = VecBuffer.unwrap().position();
+ *
+ * assert model.size() == final - initial;
+ * }
*/ public interface IModel { @@ -14,12 +32,34 @@ public interface IModel { */ void buffer(VecBuffer buffer); + /** + * @return The number of vertices the model has. + */ int vertexCount(); + /** + * @return The format of this model's vertices + */ VertexFormat format(); - ElementBuffer createEBO(); + /** + * Create an element buffer object that indexes the vertices of this model. + * + *

+ * Very often models in minecraft are made up of sequential quads, which is a very predictable pattern. + * The default implementation accommodates this, however this can be overridden to change the behavior and + * support more complex models. + *

+ * @return an element buffer object indexing this model's vertices. + */ + default ElementBuffer createEBO() { + return QuadConverter.getInstance() + .quads2Tris(vertexCount() / 4); + } + /** + * The size in bytes that this model's data takes up. + */ default int size() { return vertexCount() * format().getStride(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java index fff356878..6e81c7d2e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java @@ -43,10 +43,4 @@ public class ModelPart implements IModel { public VertexFormat format() { return Formats.UNLIT_MODEL; } - - @Override - public ElementBuffer createEBO() { - return QuadConverter.getInstance() - .quads2Tris(vertices / 4); - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java index 3039e2218..52b69403d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -1,21 +1,62 @@ package com.jozufozu.flywheel.core.model; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import static org.lwjgl.opengl.GL11.GL_QUADS; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; -import com.jozufozu.flywheel.backend.model.IndexedModel; + +import java.util.Collection; +import java.util.Random; + +import com.mojang.blaze3d.matrix.MatrixStack; + +import net.minecraft.block.BlockRenderType; +import net.minecraft.block.BlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BlockModelRenderer; +import net.minecraft.client.renderer.BlockModelShapes; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.RenderTypeLookup; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockDisplayReader; +import net.minecraft.world.gen.feature.template.Template; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.model.data.EmptyModelData; +import net.minecraftforge.common.util.Lazy; public class ModelUtil { - public static IndexedModel getIndexedModel(IModel blockModel) { - ByteBuffer vertices = ByteBuffer.allocate(blockModel.size()); - vertices.order(ByteOrder.nativeOrder()); + private static final Lazy MODEL_RENDERER = Lazy.of(() -> new BlockModelRenderer(Minecraft.getInstance().getBlockColors())); + private static final Lazy BLOCK_MODELS = Lazy.of(() -> Minecraft.getInstance().getModelManager().getBlockModelShaper()); - blockModel.buffer(new VecBuffer(vertices)); + public static BufferBuilder getBufferBuilderFromTemplate(IBlockDisplayReader renderWorld, RenderType layer, Collection blocks) { + MatrixStack ms = new MatrixStack(); + Random random = new Random(); + BufferBuilder builder = new BufferBuilder(DefaultVertexFormats.BLOCK.getIntegerSize()); + builder.begin(GL_QUADS, DefaultVertexFormats.BLOCK); - ((Buffer) vertices).rewind(); + ForgeHooksClient.setRenderLayer(layer); + BlockModelRenderer.enableCaching(); + for (Template.BlockInfo info : blocks) { + BlockState state = info.state; - return new IndexedModel(blockModel.format(), vertices, blockModel.vertexCount(), blockModel.createEBO()); + if (state.getRenderShape() != BlockRenderType.MODEL) + continue; + if (!RenderTypeLookup.canRenderInLayer(state, layer)) + continue; + + BlockPos pos = info.pos; + + ms.pushPose(); + ms.translate(pos.getX(), pos.getY(), pos.getZ()); + MODEL_RENDERER.get().renderModel(renderWorld, BLOCK_MODELS.get().getBlockModel(state), state, pos, ms, builder, true, + random, 42, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE); + ms.popPose(); + } + BlockModelRenderer.clearCache(); + ForgeHooksClient.setRenderLayer(null); + + builder.end(); + return builder; } } diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java new file mode 100644 index 000000000..fa2001d0d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java @@ -0,0 +1,53 @@ +package com.jozufozu.flywheel.core.model; + +import java.util.Collection; + +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.util.BufferBuilderReader; + +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.world.IBlockDisplayReader; +import net.minecraft.world.gen.feature.template.Template; + +public class WorldModel implements IModel { + + private final BufferBuilderReader reader; + + public WorldModel(IBlockDisplayReader renderWorld, RenderType layer, Collection blocks) { + reader = new BufferBuilderReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks)); + } + + @Override + public void buffer(VecBuffer vertices) { + for (int i = 0; i < vertexCount(); i++) { + vertices.putVec3(reader.getX(i), reader.getY(i), reader.getZ(i)); + + vertices.putVec3(reader.getNX(i), reader.getNY(i), reader.getNZ(i)); + + vertices.putVec2(reader.getU(i), reader.getV(i)); + + vertices.putColor(reader.getR(i), reader.getG(i), reader.getB(i), reader.getA(i)); + + int light = reader.getLight(i); + + byte block = (byte) (LightTexture.block(light) << 4); + byte sky = (byte) (LightTexture.sky(light) << 4); + + vertices.putVec2(block, sky); + } + } + + @Override + public int vertexCount() { + return reader.getVertexCount(); + } + + @Override + public VertexFormat format() { + return Formats.COLORED_LIT_MODEL; + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java index 79028b953..3b891d683 100644 --- a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java +++ b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java @@ -2,7 +2,31 @@ package com.jozufozu.flywheel.util; public class RenderMath { + /** + * Convert a signed, normalized floating point value into a normalized byte. + */ public static byte nb(float f) { return (byte) (f * 127); } + + /** + * Convert a signed byte into a normalized float. + */ + public static float f(byte b) { + return b / 127f; + } + + /** + * Convert an unsigned byte into a normalized float. + */ + public static float uf(byte b) { + return (float) (Byte.toUnsignedInt(b)) / 255f; + } + + /** + * Convert an unsigned, normalized float into an unsigned normalized byte. + */ + public static byte unb(float f) { + return (byte) Math.floor(f * 255); + } }