package com.mojang.blaze3d.vertex; import com.mojang.blaze3d.buffers.BufferType; import com.mojang.blaze3d.buffers.BufferUsage; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.ByteBufferBuilder.Result; import com.mojang.blaze3d.vertex.MeshData.DrawState; import com.mojang.blaze3d.vertex.VertexFormat.IndexType; import com.mojang.blaze3d.vertex.VertexFormat.Mode; import java.nio.ByteBuffer; import java.util.function.Consumer; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.CompiledShaderProgram; import net.minecraft.client.renderer.RenderType; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; @Environment(EnvType.CLIENT) public class VertexBuffer implements AutoCloseable { private final BufferUsage usage; private final GpuBuffer vertexBuffer; @Nullable private GpuBuffer indexBuffer = null; private int arrayObjectId; @Nullable private VertexFormat format; @Nullable private RenderSystem.AutoStorageIndexBuffer sequentialIndices; private IndexType indexType; private int indexCount; private Mode mode; public VertexBuffer(BufferUsage usage) { this.usage = usage; RenderSystem.assertOnRenderThread(); this.vertexBuffer = new GpuBuffer(BufferType.VERTICES, usage, 0); this.arrayObjectId = GlStateManager._glGenVertexArrays(); } public static VertexBuffer uploadStatic(Mode mode, VertexFormat format, Consumer builder) { BufferBuilder bufferBuilder = Tesselator.getInstance().begin(mode, format); builder.accept(bufferBuilder); VertexBuffer vertexBuffer = new VertexBuffer(BufferUsage.STATIC_WRITE); vertexBuffer.bind(); vertexBuffer.upload(bufferBuilder.buildOrThrow()); unbind(); return vertexBuffer; } public void upload(MeshData meshData) { MeshData var2 = meshData; label40: { try { if (this.isInvalid()) { break label40; } RenderSystem.assertOnRenderThread(); DrawState drawState = meshData.drawState(); this.format = this.uploadVertexBuffer(drawState, meshData.vertexBuffer()); this.sequentialIndices = this.uploadIndexBuffer(drawState, meshData.indexBuffer()); this.indexCount = drawState.indexCount(); this.indexType = drawState.indexType(); this.mode = drawState.mode(); } catch (Throwable var6) { if (meshData != null) { try { var2.close(); } catch (Throwable var5) { var6.addSuppressed(var5); } } throw var6; } if (meshData != null) { meshData.close(); } return; } if (meshData != null) { meshData.close(); } } public void uploadIndexBuffer(Result result) { Result var2 = result; label46: { try { if (this.isInvalid()) { break label46; } RenderSystem.assertOnRenderThread(); if (this.indexBuffer != null) { this.indexBuffer.close(); } this.indexBuffer = new GpuBuffer(BufferType.INDICES, this.usage, result.byteBuffer()); this.sequentialIndices = null; } catch (Throwable var6) { if (result != null) { try { var2.close(); } catch (Throwable var5) { var6.addSuppressed(var5); } } throw var6; } if (result != null) { result.close(); } return; } if (result != null) { result.close(); } } private VertexFormat uploadVertexBuffer(DrawState drawState, @Nullable ByteBuffer buffer) { boolean bl = false; if (!drawState.format().equals(this.format)) { if (this.format != null) { this.format.clearBufferState(); } this.vertexBuffer.bind(); drawState.format().setupBufferState(); bl = true; } if (buffer != null) { if (!bl) { this.vertexBuffer.bind(); } this.vertexBuffer.resize(buffer.remaining()); this.vertexBuffer.write(buffer, 0); } return drawState.format(); } @Nullable private RenderSystem.AutoStorageIndexBuffer uploadIndexBuffer(DrawState drawState, @Nullable ByteBuffer buffer) { if (buffer != null) { if (this.indexBuffer != null) { this.indexBuffer.close(); } this.indexBuffer = new GpuBuffer(BufferType.INDICES, this.usage, buffer); return null; } else { RenderSystem.AutoStorageIndexBuffer autoStorageIndexBuffer = RenderSystem.getSequentialBuffer(drawState.mode()); if (autoStorageIndexBuffer != this.sequentialIndices || !autoStorageIndexBuffer.hasStorage(drawState.indexCount())) { autoStorageIndexBuffer.bind(drawState.indexCount()); } return autoStorageIndexBuffer; } } public void bind() { BufferUploader.invalidate(); GlStateManager._glBindVertexArray(this.arrayObjectId); } public static void unbind() { BufferUploader.invalidate(); GlStateManager._glBindVertexArray(0); } public void draw() { RenderSystem.drawElements(this.mode.asGLMode, this.indexCount, this.getIndexType().asGLType); } private IndexType getIndexType() { RenderSystem.AutoStorageIndexBuffer autoStorageIndexBuffer = this.sequentialIndices; return autoStorageIndexBuffer != null ? autoStorageIndexBuffer.type() : this.indexType; } public void drawWithShader(Matrix4f frustumMatrix, Matrix4f projectionMatrix, @Nullable CompiledShaderProgram shader) { if (shader != null) { RenderSystem.assertOnRenderThread(); shader.setDefaultUniforms(this.mode, frustumMatrix, projectionMatrix, Minecraft.getInstance().getWindow()); shader.apply(); this.draw(); shader.clear(); } } public void drawWithRenderType(RenderType renderType) { renderType.setupRenderState(); this.bind(); this.drawWithShader(RenderSystem.getModelViewMatrix(), RenderSystem.getProjectionMatrix(), RenderSystem.getShader()); unbind(); renderType.clearRenderState(); } public void close() { this.vertexBuffer.close(); if (this.indexBuffer != null) { this.indexBuffer.close(); this.indexBuffer = null; } if (this.arrayObjectId >= 0) { RenderSystem.glDeleteVertexArrays(this.arrayObjectId); this.arrayObjectId = -1; } } public VertexFormat getFormat() { return this.format; } public boolean isInvalid() { return this.arrayObjectId == -1; } }