From 4228011b848e99de64eb79c26598d81490c32bad Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 30 Jul 2022 12:04:28 +0100 Subject: [PATCH 1/3] Support Occulus shaders This is mostly copied from the work Toad and I did for CC:R. Instead of not writing to the depth buffer when rendering terminals, we now render terminal forgrounds with a small glPolygonOffset (or an emulation of it where not possible). This removes the need for custom render types, while still avoiding z-fighting between the terminal foreground and background. The VBO monitors backend now uses Iris's TextVertexSink API when available: Iris overwrites the vertex format for RenderType.text, and so we need to use this API to avoid rendering garbage. Performance is maybe a little worse than before (<3ms) and definitely worse than CC:R. Unfortunately we can't do our upload batching as that conflicts with Optifine's patches - instead we need to maintain two separate VBOs. This is a bit slower, but not so bad it's unworkable. --- build.gradle | 8 + .../computercraft/client/gui/GuiPrintout.java | 6 +- .../client/gui/widgets/WidgetTerminal.java | 4 +- .../client/render/ItemPocketRenderer.java | 14 +- .../client/render/ItemPrintoutRenderer.java | 6 +- .../client/render/PrintoutRenderer.java | 24 +-- .../client/render/RenderTypes.java | 55 +----- .../render/TileEntityMonitorRenderer.java | 84 ++++++--- .../text/DirectFixedWidthFontRenderer.java | 171 ++++++++++++------ .../render/text/FixedWidthFontRenderer.java | 70 ++++--- .../shared/integration/ShaderMod.java | 96 ++++++++++ .../peripheral/monitor/ClientMonitor.java | 22 ++- .../peripheral/monitor/MonitorRenderer.java | 4 +- 13 files changed, 359 insertions(+), 205 deletions(-) create mode 100644 src/main/java/dan200/computercraft/shared/integration/ShaderMod.java diff --git a/build.gradle b/build.gradle index 0825cf71d..278e3e9f6 100644 --- a/build.gradle +++ b/build.gradle @@ -129,6 +129,13 @@ repositories { name "SquidDev" url "https://squiddev.cc/maven" } + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + content { + includeGroup "maven.modrinth" + } + } } configurations { @@ -150,6 +157,7 @@ dependencies { extraModsCompileOnly fg.deobf("mezz.jei:jei-1.18.2:9.4.1.116:api") extraModsRuntimeOnly fg.deobf("mezz.jei:jei-1.18.2:9.4.1.116") + extraModsCompileOnly fg.deobf("maven.modrinth:oculus:1.18.2-1.2.5") shade 'org.squiddev:Cobalt:0.5.5' diff --git a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java index 5f378f76e..e9f390978 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java @@ -8,7 +8,6 @@ package dan200.computercraft.client.gui; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.math.Matrix4f; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.common.ContainerHeldItem; import dan200.computercraft.shared.media.items.ItemPrintout; @@ -99,9 +98,8 @@ public class GuiPrintout extends AbstractContainerScreen RenderSystem.enableDepthTest(); MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() ); - Matrix4f matrix = transform.last().pose(); - drawBorder( matrix, renderer, leftPos, topPos, getBlitOffset(), page, pages, book, FULL_BRIGHT_LIGHTMAP ); - drawText( matrix, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours ); + drawBorder( transform, renderer, leftPos, topPos, getBlitOffset(), page, pages, book, FULL_BRIGHT_LIGHTMAP ); + drawText( transform, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours ); renderer.endBatch(); } diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java index 17cb909c9..e09546f47 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java @@ -7,7 +7,6 @@ package dan200.computercraft.client.gui.widgets; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.math.Matrix4f; import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; @@ -316,11 +315,10 @@ public class WidgetTerminal extends AbstractWidget public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float partialTicks ) { if( !visible ) return; - Matrix4f matrix = transform.last().pose(); Terminal terminal = computer.getTerminal(); var bufferSource = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() ); - var emitter = FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) ); + var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, bufferSource.getBuffer( RenderTypes.TERMINAL ) ); if( terminal != null ) { diff --git a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java index 8e7ddd2ad..f78723025 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java @@ -96,23 +96,19 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer // Render the light int lightColour = ItemPocketComputer.getLightState( stack ); if( lightColour == -1 ) lightColour = Colour.BLACK.getHex(); - renderLight( matrix, bufferSource, lightColour, width, height ); + renderLight( transform, bufferSource, lightColour, width, height ); if( computer != null && terminal != null ) { FixedWidthFontRenderer.drawTerminal( - FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ) ), + FixedWidthFontRenderer.toVertexConsumer( transform, bufferSource.getBuffer( RenderTypes.TERMINAL ) ), MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN ); - FixedWidthFontRenderer.drawBlocker( - FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ) ), - 0, 0, width, height - ); } else { FixedWidthFontRenderer.drawEmptyTerminal( - FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) ), + FixedWidthFontRenderer.toVertexConsumer( transform, bufferSource.getBuffer( RenderTypes.TERMINAL ) ), 0, 0, width, height ); } @@ -131,14 +127,14 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer ComputerBorderRenderer.render( transform, render.getBuffer( ComputerBorderRenderer.getRenderType( texture ) ), 0, 0, 0, light, width, height, true, r, g, b ); } - private static void renderLight( Matrix4f transform, MultiBufferSource render, int colour, int width, int height ) + private static void renderLight( PoseStack transform, MultiBufferSource render, int colour, int width, int height ) { byte r = (byte) ((colour >>> 16) & 0xFF); byte g = (byte) ((colour >>> 8) & 0xFF); byte b = (byte) (colour & 0xFF); byte[] c = new byte[] { r, g, b, (byte) 255 }; - VertexConsumer buffer = render.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ); + VertexConsumer buffer = render.getBuffer( RenderTypes.TERMINAL ); FixedWidthFontRenderer.drawQuad( FixedWidthFontRenderer.toVertexConsumer( transform, buffer ), width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT, diff --git a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java index 9532c7d4f..92ab93d20 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java @@ -6,7 +6,6 @@ package dan200.computercraft.client.render; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Matrix4f; import com.mojang.math.Vector3f; import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.media.items.ItemPrintout; @@ -106,10 +105,9 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer transform.scale( scale, scale, scale ); transform.translate( (max - width) / 2.0, (max - height) / 2.0, 0.0 ); - Matrix4f matrix = transform.last().pose(); - drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book, light ); + drawBorder( transform, render, 0, 0, -0.01f, 0, pages, book, light ); drawText( - matrix, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, + transform, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) ); } diff --git a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java index 0c81615f9..fc2eeb5d0 100644 --- a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java @@ -5,6 +5,7 @@ */ package dan200.computercraft.client.render; +import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Matrix4f; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; @@ -54,7 +55,7 @@ public final class PrintoutRenderer private PrintoutRenderer() {} - public static void drawText( Matrix4f transform, MultiBufferSource bufferSource, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours ) + public static void drawText( PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours ) { var buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_TEXT ); var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer ); @@ -67,7 +68,7 @@ public final class PrintoutRenderer } } - public static void drawText( Matrix4f transform, MultiBufferSource bufferSource, int x, int y, int start, int light, String[] text, String[] colours ) + public static void drawText( PoseStack transform, MultiBufferSource bufferSource, int x, int y, int start, int light, String[] text, String[] colours ) { var buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_TEXT ); var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer ); @@ -81,8 +82,9 @@ public final class PrintoutRenderer } } - public static void drawBorder( Matrix4f transform, MultiBufferSource bufferSource, float x, float y, float z, int page, int pages, boolean isBook, int light ) + public static void drawBorder( PoseStack transform, MultiBufferSource bufferSource, float x, float y, float z, int page, int pages, boolean isBook, int light ) { + var matrix = transform.last().pose(); int leftPages = page; int rightPages = pages - page - 1; @@ -96,11 +98,11 @@ public final class PrintoutRenderer float right = x + X_SIZE + offset - 4; // Left and right border - drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light ); - drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light ); + drawTexture( matrix, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light ); + drawTexture( matrix, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light ); // Draw centre panel (just stretched texture, sorry). - drawTexture( transform, buffer, + drawTexture( matrix, buffer, x - offset, y, z - 0.02f, X_SIZE + offset * 2, Y_SIZE, COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE, light @@ -110,20 +112,20 @@ public final class PrintoutRenderer while( borderX < right ) { double thisWidth = Math.min( right - borderX, X_SIZE ); - drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE, light ); - drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE, light ); + drawTexture( matrix, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE, light ); + drawTexture( matrix, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE, light ); borderX += thisWidth; } } // Current page background: Z-offset is interleaved between the "zeroth" left/right page and the first // left/right page, so that the "bold" border can be drawn over the edge where appropriate. - drawTexture( transform, buffer, x, y, z - 1e-3f * 0.5f, X_FOLD_SIZE * 2, 0, X_SIZE, Y_SIZE, light ); + drawTexture( matrix, buffer, x, y, z - 1e-3f * 0.5f, X_FOLD_SIZE * 2, 0, X_SIZE, Y_SIZE, light ); // Left pages for( int n = 0; n <= leftPages; n++ ) { - drawTexture( transform, buffer, + drawTexture( matrix, buffer, x - offsetAt( n ), y, z - 1e-3f * n, // Use the left "bold" fold for the outermost page n == leftPages ? 0 : X_FOLD_SIZE, 0, @@ -134,7 +136,7 @@ public final class PrintoutRenderer // Right pages for( int n = 0; n <= rightPages; n++ ) { - drawTexture( transform, buffer, + drawTexture( matrix, buffer, x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3f * n, // Two folds, then the main page. Use the right "bold" fold for the outermost page. X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0, diff --git a/src/main/java/dan200/computercraft/client/render/RenderTypes.java b/src/main/java/dan200/computercraft/client/render/RenderTypes.java index 74bf89986..61c31e026 100644 --- a/src/main/java/dan200/computercraft/client/render/RenderTypes.java +++ b/src/main/java/dan200/computercraft/client/render/RenderTypes.java @@ -30,24 +30,9 @@ public class RenderTypes private static MonitorTextureBufferShader monitorTboShader; /** - * Renders a fullbright terminal without writing to the depth layer. This is used in combination with - * {@link #TERMINAL_BLOCKER} to ensure we can render a terminal without z-fighting. + * Renders a fullbright terminal. */ - public static final RenderType TERMINAL_WITHOUT_DEPTH = Types.TERMINAL_WITHOUT_DEPTH; - - /** - * A transparent texture which only writes to the depth layer. - */ - public static final RenderType TERMINAL_BLOCKER = Types.TERMINAL_BLOCKER; - - /** - * Renders a fullbright terminal which also writes to the depth layer. This is used when z-fighting isn't an issue - - * for instance rendering an empty terminal or inside a GUI. - * - * This is identical to vanilla's {@link RenderType#text}. Forge overrides one with a definition which sets - * sortOnUpload to true, which is entirely broken! - */ - public static final RenderType TERMINAL_WITH_DEPTH = Types.TERMINAL_WITH_DEPTH; + public static final RenderType TERMINAL = RenderType.text( FixedWidthFontRenderer.FONT ); /** * Renders a monitor with the TBO shader. @@ -57,7 +42,7 @@ public class RenderTypes public static final RenderType MONITOR_TBO = Types.MONITOR_TBO; /** - * A variant of {@link #TERMINAL_WITH_DEPTH} which uses the lightmap rather than rendering fullbright. + * A variant of {@link #TERMINAL} which uses the lightmap rather than rendering fullbright. */ public static final RenderType PRINTOUT_TEXT = RenderType.text( FixedWidthFontRenderer.FONT ); @@ -77,7 +62,7 @@ public class RenderTypes @Nonnull static ShaderInstance getTerminalShader() { - return GameRenderer.getPositionColorTexShader(); + return GameRenderer.getRendertypeTextShader(); } @SubscribeEvent @@ -99,8 +84,6 @@ public class RenderTypes FixedWidthFontRenderer.FONT, false, false // blur, minimap ); - private static final VertexFormat TERM_FORMAT = DefaultVertexFormat.POSITION_COLOR_TEX; - private static final ShaderStateShard TERM_SHADER = new ShaderStateShard( RenderTypes::getTerminalShader ); static final RenderType MONITOR_TBO = RenderType.create( "monitor_tbo", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.TRIANGLE_STRIP, 128, @@ -111,36 +94,6 @@ public class RenderTypes .createCompositeState( false ) ); - static final RenderType TERMINAL_WITHOUT_DEPTH = RenderType.create( - "terminal_without_depth", TERM_FORMAT, VertexFormat.Mode.QUADS, 1024, - false, false, // useDelegate, needsSorting - RenderType.CompositeState.builder() - .setTextureState( TERM_FONT_TEXTURE ) - .setShaderState( TERM_SHADER ) - .setLightmapState( LIGHTMAP ) - .setWriteMaskState( COLOR_WRITE ) - .createCompositeState( false ) - ); - - static final RenderType TERMINAL_BLOCKER = RenderType.create( - "terminal_blocker", DefaultVertexFormat.POSITION, VertexFormat.Mode.QUADS, 256, - false, false, // useDelegate, needsSorting - RenderType.CompositeState.builder() - .setShaderState( POSITION_SHADER ) - .setWriteMaskState( DEPTH_WRITE ) - .createCompositeState( false ) - ); - - static final RenderType TERMINAL_WITH_DEPTH = RenderType.create( - "terminal_with_depth", TERM_FORMAT, VertexFormat.Mode.QUADS, 1024, - false, false, // useDelegate, needsSorting - RenderType.CompositeState.builder() - .setTextureState( TERM_FONT_TEXTURE ) - .setShaderState( TERM_SHADER ) - .setLightmapState( LIGHTMAP ) - .createCompositeState( false ) - ); - private Types( String name, Runnable setup, Runnable destroy ) { super( name, setup, destroy ); diff --git a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java index 3660c6a04..6389247e4 100644 --- a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java @@ -12,6 +12,7 @@ import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; import com.mojang.math.Vector3f; import dan200.computercraft.ComputerCraft; @@ -19,11 +20,14 @@ import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import dan200.computercraft.client.util.DirectBuffers; +import dan200.computercraft.client.util.DirectVertexBuffer; import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.integration.ShaderMod; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import dan200.computercraft.shared.util.DirectionUtil; +import net.minecraft.Util; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; @@ -35,6 +39,7 @@ import org.lwjgl.opengl.GL31; import javax.annotation.Nonnull; import java.nio.ByteBuffer; +import java.util.function.Consumer; import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; @@ -46,6 +51,9 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer + DirectFixedWidthFontRenderer.drawTerminalBackground( sink, 0, 0, terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin ) ); - buffer.flip(); - - vbo.upload( termIndexes, RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format(), buffer ); + renderToBuffer( foregroundBuffer, size, sink -> { + DirectFixedWidthFontRenderer.drawTerminalForeground( sink, 0, 0, terminal, !monitor.isColour() ); + // If the cursor is visible, we append it to the end of our buffer. When rendering, we can either + // render n or n+1 quads and so toggle the cursor on and off. + DirectFixedWidthFontRenderer.drawCursor( sink, 0, 0, terminal, !monitor.isColour() ); + } ); } - RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState(); - vbo.drawWithShader( + // Our VBO doesn't transform its vertices with the provided pose stack, which means that the inverse view + // rotation matrix gives entirely wrong numbers for fog distances. We just set it to the identity which + // gives a good enough approximation. + Matrix3f oldInverseRotation = RenderSystem.getInverseViewRotationMatrix(); + RenderSystem.setInverseViewRotationMatrix( IDENTITY_NORMAL ); + + RenderTypes.TERMINAL.setupRenderState(); + + // Render background geometry + backgroundBuffer.drawWithShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader() ); + + // Render foreground geometry with glPolygonOffset enabled. + GL11.glPolygonOffset( -1.0f, -10.0f ); + GL11.glEnable( GL11.GL_POLYGON_OFFSET_FILL ); + foregroundBuffer.drawWithShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader(), // As mentioned in the above comment, render the extra cursor quad if it is visible this frame. Each // // quad has an index count of 6. - FixedWidthFontRenderer.isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() ? vbo.getIndexCount() + 6 : vbo.getIndexCount() + FixedWidthFontRenderer.isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() + ? foregroundBuffer.getIndexCount() + 6 : foregroundBuffer.getIndexCount() ); - RenderTypes.TERMINAL_WITHOUT_DEPTH.clearRenderState(); - // TERMINAL_WITHOUT_DEPTH doesn't write to the depth blocker, so write a blocker over the monitor. - BufferBuilder buffer = Tesselator.getInstance().getBuilder(); - buffer.begin( RenderTypes.TERMINAL_BLOCKER.mode(), RenderTypes.TERMINAL_BLOCKER.format() ); - FixedWidthFontRenderer.drawBlocker( - FixedWidthFontRenderer.toVertexConsumer( matrix, buffer ), - -xMargin, -yMargin, pixelWidth + xMargin * 2, pixelHeight + yMargin * 2 - ); - RenderTypes.TERMINAL_BLOCKER.end( buffer, 0, 0, 0 ); + // Clear state + GL11.glPolygonOffset( 0.0f, -0.0f ); + GL11.glDisable( GL11.GL_POLYGON_OFFSET_FILL ); + RenderTypes.TERMINAL.clearRenderState(); + + RenderSystem.setInverseViewRotationMatrix( oldInverseRotation ); break; } } } + private static void renderToBuffer( DirectVertexBuffer vbo, int size, Consumer draw ) + { + var sink = ShaderMod.INSTANCE.getQuadEmitter( size, TileEntityMonitorRenderer::getBuffer ); + var buffer = sink.buffer(); + + draw.accept( sink ); + buffer.flip(); + vbo.upload( buffer.limit() / sink.format().getVertexSize(), RenderTypes.TERMINAL.mode(), sink.format(), buffer ); + } + private static void tboVertex( VertexConsumer builder, Matrix4f matrix, float x, float y ) { // We encode position in the UV, as that's not transformed by the matrix. diff --git a/src/main/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java b/src/main/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java index d2dda9665..c8731bbf3 100644 --- a/src/main/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java @@ -8,6 +8,8 @@ package dan200.computercraft.client.render.text; import com.mojang.blaze3d.platform.MemoryTracker; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; +import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.util.Colour; @@ -18,17 +20,16 @@ import javax.annotation.Nonnull; import java.nio.ByteBuffer; import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*; -import static org.lwjgl.system.MemoryUtil.memPutByte; -import static org.lwjgl.system.MemoryUtil.memPutFloat; +import static org.lwjgl.system.MemoryUtil.*; /** - * An optimised copy of {@link FixedWidthFontRenderer} emitter emits directly to a {@link ByteBuffer} rather than + * An optimised copy of {@link FixedWidthFontRenderer} emitter emits directly to a {@link QuadEmitter} rather than * emitting to {@link VertexConsumer}. This allows us to emit vertices very quickly, when using the VBO renderer. * * There are some limitations here: *
    *
  • No transformation matrix (not needed for VBOs).
  • - *
  • Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX}.
  • + *
  • Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX_LIGHTMAP}.
  • *
  • The buffer MUST be allocated with {@link MemoryTracker}, and not through any other means.
  • *
* @@ -44,7 +45,7 @@ public final class DirectFixedWidthFontRenderer { } - private static void drawChar( ByteBuffer buffer, float x, float y, int index, byte[] colour ) + private static void drawChar( QuadEmitter emitter, float x, float y, int index, byte[] colour ) { // Short circuit to avoid the common case - the texture should be blank here after all. if( index == '\0' || index == ' ' ) return; @@ -56,30 +57,30 @@ public final class DirectFixedWidthFontRenderer int yStart = 1 + row * (FONT_HEIGHT + 2); quad( - buffer, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, colour, + emitter, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour, xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ); } - private static void drawQuad( ByteBuffer emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex ) + private static void drawQuad( QuadEmitter emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex ) { byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale ); - quad( emitter, x, y, x + width, y + height, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END ); + quad( emitter, x, y, x + width, y + height, 0f, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END ); } private static void drawBackground( - @Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, + @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize, float height ) { if( leftMarginSize > 0 ) { - drawQuad( buffer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) ); + drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) ); } if( rightMarginSize > 0 ) { - drawQuad( buffer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) ); + drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) ); } // Batch together runs of identical background cells. @@ -92,7 +93,7 @@ public final class DirectFixedWidthFontRenderer if( blockColour != '\0' ) { - drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour ); + drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour ); } blockColour = colourIndex; @@ -101,11 +102,11 @@ public final class DirectFixedWidthFontRenderer if( blockColour != '\0' ) { - drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour ); + drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour ); } } - private static void drawString( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale ) + public static void drawString( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale ) { for( int i = 0; i < text.length(); i++ ) { @@ -113,12 +114,29 @@ public final class DirectFixedWidthFontRenderer int index = text.charAt( i ); if( index > 255 ) index = '?'; - drawChar( buffer, x + i * FONT_WIDTH, y, index, colour ); + drawChar( emitter, x + i * FONT_WIDTH, y, index, colour ); + } + + } + + public static void drawTerminalForeground( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale ) + { + Palette palette = terminal.getPalette(); + int height = terminal.getHeight(); + + // The main text + for( int i = 0; i < height; i++ ) + { + float rowY = y + FONT_HEIGHT * i; + drawString( + emitter, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ), + palette, greyscale + ); } } - public static void drawTerminalWithoutCursor( - @Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale, + public static void drawTerminalBackground( + @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize ) { @@ -127,12 +145,12 @@ public final class DirectFixedWidthFontRenderer // Top and bottom margins drawBackground( - buffer, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale, + emitter, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale, leftMarginSize, rightMarginSize, topMarginSize ); drawBackground( - buffer, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, + emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, leftMarginSize, rightMarginSize, bottomMarginSize ); @@ -141,43 +159,68 @@ public final class DirectFixedWidthFontRenderer { float rowY = y + FONT_HEIGHT * i; drawBackground( - buffer, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale, + emitter, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT ); - drawString( - buffer, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ), - palette, greyscale - ); } } - public static void drawCursor( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale ) + public static void drawCursor( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale ) { if( isCursorVisible( terminal ) ) { byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale ); - drawChar( buffer, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour ); + drawChar( emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour ); } } public static int getVertexCount( Terminal terminal ) { - return (1 + (terminal.getHeight() + 2) * terminal.getWidth() * 2) * 4; + return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2; } - private static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, byte[] rgba, float u1, float v1, float u2, float v2 ) + private static void quad( QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2 ) + { + buffer.quad( x1, y1, x2, y2, z, rgba, u1, v1, u2, v2 ); + } + + public interface QuadEmitter + { + VertexFormat format(); + + ByteBuffer buffer(); + + void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2 ); + } + + public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter + { + @Override + public VertexFormat format() + { + return RenderTypes.TERMINAL.format(); + } + + @Override + public void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2 ) + { + DirectFixedWidthFontRenderer.quad( buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2 ); + } + } + + static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2 ) { // Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the // underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write. // This provides significant performance gains, at the cost of well, using Unsafe. - // Each vertex is 24 bytes, giving 96 bytes in total. Vertices are of the form (xyz:FFF)(rgba:BBBB)(uv:FF), - // which matches the POSITION_COLOR_TEX vertex format. + // Each vertex is 28 bytes, giving 112 bytes in total. Vertices are of the form (xyz:FFF)(rgba:BBBB)(uv1:FF)(uv2:SS), + // which matches the POSITION_COLOR_TEX_LIGHTMAP vertex format. int position = buffer.position(); long addr = MemoryUtil.memAddress( buffer ); // We're doing terrible unsafe hacks below, so let's be really sure that what we're doing is reasonable. - if( position < 0 || 96 > buffer.limit() - position ) throw new IndexOutOfBoundsException(); + if( position < 0 || 112 > buffer.limit() - position ) throw new IndexOutOfBoundsException(); // Require the pointer to be aligned to a 32-bit boundary. if( (addr & 3) != 0 ) throw new IllegalStateException( "Memory is not aligned" ); // Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances. @@ -185,46 +228,54 @@ public final class DirectFixedWidthFontRenderer memPutFloat( addr + 0, x1 ); memPutFloat( addr + 4, y1 ); - memPutFloat( addr + 8, 0 ); + memPutFloat( addr + 8, z ); memPutByte( addr + 12, rgba[0] ); memPutByte( addr + 13, rgba[1] ); memPutByte( addr + 14, rgba[2] ); memPutByte( addr + 15, (byte) 255 ); memPutFloat( addr + 16, u1 ); memPutFloat( addr + 20, v1 ); + memPutShort( addr + 24, (short) 0xF0 ); + memPutShort( addr + 26, (short) 0xF0 ); - memPutFloat( addr + 24, x1 ); - memPutFloat( addr + 28, y2 ); - memPutFloat( addr + 32, 0 ); - memPutByte( addr + 36, rgba[0] ); - memPutByte( addr + 37, rgba[1] ); - memPutByte( addr + 38, rgba[2] ); - memPutByte( addr + 39, (byte) 255 ); - memPutFloat( addr + 40, u1 ); - memPutFloat( addr + 44, v2 ); + memPutFloat( addr + 28, x1 ); + memPutFloat( addr + 32, y2 ); + memPutFloat( addr + 36, z ); + memPutByte( addr + 40, rgba[0] ); + memPutByte( addr + 41, rgba[1] ); + memPutByte( addr + 42, rgba[2] ); + memPutByte( addr + 43, (byte) 255 ); + memPutFloat( addr + 44, u1 ); + memPutFloat( addr + 48, v2 ); + memPutShort( addr + 52, (short) 0xF0 ); + memPutShort( addr + 54, (short) 0xF0 ); - memPutFloat( addr + 48, x2 ); - memPutFloat( addr + 52, y2 ); - memPutFloat( addr + 56, 0 ); - memPutByte( addr + 60, rgba[0] ); - memPutByte( addr + 61, rgba[1] ); - memPutByte( addr + 62, rgba[2] ); - memPutByte( addr + 63, (byte) 255 ); - memPutFloat( addr + 64, u2 ); - memPutFloat( addr + 68, v2 ); + memPutFloat( addr + 56, x2 ); + memPutFloat( addr + 60, y2 ); + memPutFloat( addr + 64, z ); + memPutByte( addr + 68, rgba[0] ); + memPutByte( addr + 69, rgba[1] ); + memPutByte( addr + 70, rgba[2] ); + memPutByte( addr + 71, (byte) 255 ); + memPutFloat( addr + 72, u2 ); + memPutFloat( addr + 76, v2 ); + memPutShort( addr + 80, (short) 0xF0 ); + memPutShort( addr + 82, (short) 0xF0 ); - memPutFloat( addr + 72, x2 ); - memPutFloat( addr + 76, y1 ); - memPutFloat( addr + 80, 0 ); - memPutByte( addr + 84, rgba[0] ); - memPutByte( addr + 85, rgba[1] ); - memPutByte( addr + 86, rgba[2] ); - memPutByte( addr + 87, (byte) 255 ); - memPutFloat( addr + 88, u2 ); - memPutFloat( addr + 92, v1 ); + memPutFloat( addr + 84, x2 ); + memPutFloat( addr + 88, y1 ); + memPutFloat( addr + 92, z ); + memPutByte( addr + 96, rgba[0] ); + memPutByte( addr + 97, rgba[1] ); + memPutByte( addr + 98, rgba[2] ); + memPutByte( addr + 99, (byte) 255 ); + memPutFloat( addr + 100, u2 ); + memPutFloat( addr + 104, v1 ); + memPutShort( addr + 108, (short) 0xF0 ); + memPutShort( addr + 110, (short) 0xF0 ); // Finally increment the position. - buffer.position( position + 96 ); + buffer.position( position + 112 ); // Well done for getting to the end of this method. I recommend you take a break and go look at cute puppies. } diff --git a/src/main/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java b/src/main/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java index c84c411ba..9dcf53685 100644 --- a/src/main/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java @@ -5,10 +5,11 @@ */ package dan200.computercraft.client.render.text; +import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Matrix4f; +import com.mojang.math.Vector3f; import dan200.computercraft.client.FrameInfo; -import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.util.Colour; @@ -26,13 +27,8 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMA *
    *
  • {@link #drawString}: Drawing basic text without a terminal (such as for printouts). Unlike the other methods, * this accepts a lightmap coordinate as, unlike terminals, printed pages render fullbright.
  • - *
  • {@link #drawTerminalWithoutCursor}/{@link #drawCursor}: Draw a terminal without a cursor and then draw the cursor - * separately. This is used by the monitor renderer to render the terminal to a VBO and draw the cursor dynamically. - *
  • *
  • {@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the * whole term.
  • - *
  • {@link #drawBlocker}: When rendering a terminal using {@link RenderTypes#TERMINAL_WITHOUT_DEPTH} you need to - * render an additional "depth blocker" on top of the monitor.
  • *
* * IMPORTANT: When making changes to this class, please check if you need to make the same changes to @@ -50,6 +46,7 @@ public final class FixedWidthFontRenderer static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; private static final byte[] BLACK = new byte[] { byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), (byte) 255 }; + private static final float Z_OFFSET = 1e-3f; private FixedWidthFontRenderer() { @@ -149,9 +146,24 @@ public final class FixedWidthFontRenderer } - public static void drawTerminalWithoutCursor( - @Nonnull QuadEmitter emitter, float x, float y, - @Nonnull Terminal terminal, boolean greyscale, + public static void drawTerminalForeground( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale ) + { + Palette palette = terminal.getPalette(); + int height = terminal.getHeight(); + + // The main text + for( int i = 0; i < height; i++ ) + { + float rowY = y + FONT_HEIGHT * i; + drawString( + emitter, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ), + palette, greyscale, FULL_BRIGHT_LIGHTMAP + ); + } + } + + public static void drawTerminalBackground( + @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize ) { @@ -172,15 +184,11 @@ public final class FixedWidthFontRenderer // The main text for( int i = 0; i < height; i++ ) { - float rowY = y + FixedWidthFontRenderer.FONT_HEIGHT * i; + float rowY = y + FONT_HEIGHT * i; drawBackground( emitter, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT, FULL_BRIGHT_LIGHTMAP ); - drawString( - emitter, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ), - palette, greyscale, FULL_BRIGHT_LIGHTMAP - ); } } @@ -208,8 +216,21 @@ public final class FixedWidthFontRenderer float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize ) { - drawTerminalWithoutCursor( emitter, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); + drawTerminalBackground( + emitter, x, y, terminal, greyscale, + topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize + ); + + // Render the foreground with a slight offset. By calling .translate() on the matrix itself, we're translating + // in screen space, rather than in model/view space. + // It's definitely not perfect, but better than z fighting! + var transformBackup = emitter.poseMatrix().copy(); + emitter.poseMatrix().translate( new Vector3f( 0, 0, Z_OFFSET ) ); + + drawTerminalForeground( emitter, x, y, terminal, greyscale ); drawCursor( emitter, x, y, terminal, greyscale ); + + emitter.poseMatrix().load( transformBackup ); } public static void drawEmptyTerminal( @Nonnull QuadEmitter emitter, float x, float y, float width, float height ) @@ -217,27 +238,24 @@ public final class FixedWidthFontRenderer drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP ); } - public static void drawBlocker( @Nonnull QuadEmitter emitter, float x, float y, float width, float height ) + public record QuadEmitter(Matrix4f poseMatrix, VertexConsumer consumer) { - drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP ); } - public record QuadEmitter(Matrix4f matrix4f, VertexConsumer consumer) {} - - public static QuadEmitter toVertexConsumer( Matrix4f matrix, VertexConsumer consumer ) + public static QuadEmitter toVertexConsumer( PoseStack transform, VertexConsumer consumer ) { - return new QuadEmitter( matrix, consumer ); + return new QuadEmitter( transform.last().pose(), consumer ); } private static void quad( QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light ) { - var matrix = c.matrix4f(); + var poseMatrix = c.poseMatrix(); var consumer = c.consumer(); byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3]; - consumer.vertex( matrix, x1, y1, z ).color( r, g, b, a ).uv( u1, v1 ).uv2( light ).endVertex(); - consumer.vertex( matrix, x1, y2, z ).color( r, g, b, a ).uv( u1, v2 ).uv2( light ).endVertex(); - consumer.vertex( matrix, x2, y2, z ).color( r, g, b, a ).uv( u2, v2 ).uv2( light ).endVertex(); - consumer.vertex( matrix, x2, y1, z ).color( r, g, b, a ).uv( u2, v1 ).uv2( light ).endVertex(); + consumer.vertex( poseMatrix, x1, y1, z ).color( r, g, b, a ).uv( u1, v1 ).uv2( light ).endVertex(); + consumer.vertex( poseMatrix, x1, y2, z ).color( r, g, b, a ).uv( u1, v2 ).uv2( light ).endVertex(); + consumer.vertex( poseMatrix, x2, y2, z ).color( r, g, b, a ).uv( u2, v2 ).uv2( light ).endVertex(); + consumer.vertex( poseMatrix, x2, y1, z ).color( r, g, b, a ).uv( u2, v1 ).uv2( light ).endVertex(); } } diff --git a/src/main/java/dan200/computercraft/shared/integration/ShaderMod.java b/src/main/java/dan200/computercraft/shared/integration/ShaderMod.java new file mode 100644 index 000000000..4d6d3b009 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/integration/ShaderMod.java @@ -0,0 +1,96 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.integration; + +import com.mojang.blaze3d.vertex.VertexFormat; +import dan200.computercraft.client.render.RenderTypes; +import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; +import net.irisshaders.iris.api.v0.IrisApi; +import net.irisshaders.iris.api.v0.IrisTextVertexSink; +import net.minecraftforge.fml.ModList; + +import java.nio.ByteBuffer; +import java.util.function.IntFunction; + +public class ShaderMod +{ + public static final ShaderMod INSTANCE + = ModList.get().isLoaded( "oculus" ) ? new IrisImpl() + : new ShaderMod(); + + public boolean isShaderMod() + { + return Optifine.isLoaded(); + } + + public boolean isRenderingShadowPass() + { + return false; + } + + public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter( int vertexCount, IntFunction makeBuffer ) + { + return new DirectFixedWidthFontRenderer.ByteBufferEmitter( + makeBuffer.apply( RenderTypes.TERMINAL.format().getVertexSize() * vertexCount * 4 ) + ); + } + + private static final class IrisImpl extends ShaderMod + { + @Override + public boolean isRenderingShadowPass() + { + return IrisApi.getInstance().isRenderingShadowPass(); + } + + @Override + public boolean isShaderMod() + { + return true; + } + + @Override + public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter( int vertexCount, IntFunction makeBuffer ) + { + return IrisApi.getInstance().getMinorApiRevision() >= 1 + ? new IrisQuadEmitter( vertexCount, makeBuffer ) + : super.getQuadEmitter( vertexCount, makeBuffer ); + } + + private static final class IrisQuadEmitter implements DirectFixedWidthFontRenderer.QuadEmitter + { + private final IrisTextVertexSink sink; + + private IrisQuadEmitter( int vertexCount, IntFunction makeBuffer ) + { + sink = IrisApi.getInstance().createTextVertexSink( vertexCount, makeBuffer ); + } + + @Override + public VertexFormat format() + { + return sink.getUnderlyingVertexFormat(); + } + + @Override + public ByteBuffer buffer() + { + return sink.getUnderlyingByteBuffer(); + } + + @Override + public void quad( float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2 ) + { + sink.quad( x1, y1, x2, y2, z, pack( rgba[0], rgba[1], rgba[2], rgba[3] ), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP ); + } + + private static int pack( int r, int g, int b, int a ) + { + return (a & 255) << 24 | (b & 255) << 16 | (g & 255) << 8 | r & 255; + } + } + } +} diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java index 43a617cf9..883f783a7 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java @@ -33,7 +33,8 @@ public final class ClientMonitor extends ClientTerminal public int tboBuffer; public int tboTexture; public int tboUniform; - public DirectVertexBuffer buffer; + public DirectVertexBuffer backgroundBuffer; + public DirectVertexBuffer foregroundBuffer; public ClientMonitor( boolean colour, TileMonitor origin ) { @@ -79,10 +80,11 @@ public final class ClientMonitor extends ClientTerminal } case VBO: - if( buffer != null ) return false; + if( backgroundBuffer != null ) return false; deleteBuffers(); - buffer = new DirectVertexBuffer(); + backgroundBuffer = new DirectVertexBuffer(); + foregroundBuffer = new DirectVertexBuffer(); addMonitor(); return true; @@ -120,17 +122,23 @@ public final class ClientMonitor extends ClientTerminal tboUniform = 0; } - if( buffer != null ) + if( backgroundBuffer != null ) { - buffer.close(); - buffer = null; + backgroundBuffer.close(); + backgroundBuffer = null; + } + + if( foregroundBuffer != null ) + { + foregroundBuffer.close(); + foregroundBuffer = null; } } @OnlyIn( Dist.CLIENT ) public void destroy() { - if( tboBuffer != 0 || buffer != null ) + if( tboBuffer != 0 || backgroundBuffer != null ) { synchronized( allMonitors ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java index b660e5b29..79c27ede4 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java @@ -7,7 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor; import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.render.TileEntityMonitorRenderer; -import dan200.computercraft.shared.integration.Optifine; +import dan200.computercraft.shared.integration.ShaderMod; import org.lwjgl.opengl.GL; import javax.annotation.Nonnull; @@ -60,7 +60,7 @@ public enum MonitorRenderer return VBO; } - if( Optifine.isLoaded() ) + if( ShaderMod.INSTANCE.isShaderMod() ) { ComputerCraft.log.warn( "Optifine is loaded, assuming shaders are being used. Falling back to VBO monitor renderer." ); return VBO; From ad4a2aa68d53da0975dcd8115920e9bc5a9973de Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 30 Jul 2022 17:56:56 +0100 Subject: [PATCH 2/3] Mirror Oculus with our maven In some ways it's probably less reliable than modrinth, but let's be consistent here. --- build.gradle | 7 ------- 1 file changed, 7 deletions(-) diff --git a/build.gradle b/build.gradle index 278e3e9f6..ec8ba81fe 100644 --- a/build.gradle +++ b/build.gradle @@ -129,13 +129,6 @@ repositories { name "SquidDev" url "https://squiddev.cc/maven" } - maven { - name = "Modrinth" - url = "https://api.modrinth.com/maven" - content { - includeGroup "maven.modrinth" - } - } } configurations { From feb7681c9cfaab9e1bbb8e981d22730c94fe9b17 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 30 Jul 2022 18:14:43 +0100 Subject: [PATCH 3/3] Add some missing test timeouts --- src/testMod/java/dan200/computercraft/ingame/DiskDriveTest.kt | 4 ++-- src/testMod/java/dan200/computercraft/ingame/TurtleTest.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/testMod/java/dan200/computercraft/ingame/DiskDriveTest.kt b/src/testMod/java/dan200/computercraft/ingame/DiskDriveTest.kt index bbe314815..46b284612 100644 --- a/src/testMod/java/dan200/computercraft/ingame/DiskDriveTest.kt +++ b/src/testMod/java/dan200/computercraft/ingame/DiskDriveTest.kt @@ -16,10 +16,10 @@ class Disk_Drive_Test { * * @see [#688](https://github.com/cc-tweaked/CC-Tweaked/issues/688) */ - @GameTest + @GameTest(timeoutTicks = Modem_Test.TIMEOUT) fun Audio_disk(helper: GameTestHelper) = helper.sequence { thenComputerOk() } - @GameTest + @GameTest(timeoutTicks = Modem_Test.TIMEOUT) fun Ejects_disk(helper: GameTestHelper) = helper.sequence { val stackAt = BlockPos(2, 2, 2) this diff --git a/src/testMod/java/dan200/computercraft/ingame/TurtleTest.kt b/src/testMod/java/dan200/computercraft/ingame/TurtleTest.kt index 47f27b495..0d80f6b6e 100644 --- a/src/testMod/java/dan200/computercraft/ingame/TurtleTest.kt +++ b/src/testMod/java/dan200/computercraft/ingame/TurtleTest.kt @@ -77,7 +77,7 @@ class Turtle_Test { * * Currently not required as turtles can no longer right-click cauldrons. */ - @GameTest(required = false) + @GameTest(timeoutTicks = COMPUTER_TIMEOUT, required = false) fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence { thenComputerOk() } /**