mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-30 21:23:00 +00:00 
			
		
		
		
	Merge branch 'mc-1.16.x' into mc-1.18.x
I was right: I did not enjoy this.
This commit is contained in:
		| @@ -1,305 +0,0 @@ | ||||
| /* | ||||
|  * 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.client.gui; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.DefaultVertexFormat; | ||||
| import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import com.mojang.math.Matrix4f; | ||||
| 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; | ||||
| import dan200.computercraft.shared.util.Palette; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
| import java.nio.ByteBuffer; | ||||
| 
 | ||||
| import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP; | ||||
| 
 | ||||
| /** | ||||
|  * Handles rendering fixed width text and computer terminals. | ||||
|  * | ||||
|  * This class has several modes of usage: | ||||
|  * <ul> | ||||
|  * <li>{@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.</li> | ||||
|  * <li>{@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. | ||||
|  * </li> | ||||
|  * <li>{@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the | ||||
|  * whole term.</li> | ||||
|  * <li>{@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.</li> | ||||
|  * </ul> | ||||
|  */ | ||||
| public final class FixedWidthFontRenderer | ||||
| { | ||||
|     public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" ); | ||||
| 
 | ||||
|     public static final int FONT_HEIGHT = 9; | ||||
|     public static final int FONT_WIDTH = 6; | ||||
|     private static final float WIDTH = 256.0f; | ||||
| 
 | ||||
|     private static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; | ||||
|     private 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 FixedWidthFontRenderer() | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private static byte byteColour( float c ) | ||||
|     { | ||||
|         return (byte) (int) (c * 255); | ||||
|     } | ||||
| 
 | ||||
|     public static float toGreyscale( double[] rgb ) | ||||
|     { | ||||
|         return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3); | ||||
|     } | ||||
| 
 | ||||
|     public static int getColour( char c, Colour def ) | ||||
|     { | ||||
|         return 15 - Terminal.getColour( c, def ); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawChar( VertexEmitter emitter, float x, float y, int index, byte[] colour, int light ) | ||||
|     { | ||||
|         // Short circuit to avoid the common case - the texture should be blank here after all. | ||||
|         if( index == '\0' || index == ' ' ) return; | ||||
| 
 | ||||
|         int column = index % 16; | ||||
|         int row = index / 16; | ||||
| 
 | ||||
|         int xStart = 1 + column * (FONT_WIDTH + 2); | ||||
|         int yStart = 1 + row * (FONT_HEIGHT + 2); | ||||
| 
 | ||||
|         emitter.vertex( x, y, (float) 0, colour, xStart / WIDTH, yStart / WIDTH, light ); | ||||
|         emitter.vertex( x, y + FONT_HEIGHT, (float) 0, colour, xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light ); | ||||
|         emitter.vertex( x + FONT_WIDTH, y + FONT_HEIGHT, (float) 0, colour, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light ); | ||||
|         emitter.vertex( x + FONT_WIDTH, y, (float) 0, colour, (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH, light ); | ||||
|     } | ||||
| 
 | ||||
|     public static void drawQuad( VertexEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light ) | ||||
|     { | ||||
|         emitter.vertex( x, y, z, colour, BACKGROUND_START, BACKGROUND_START, light ); | ||||
|         emitter.vertex( x, y + height, z, colour, BACKGROUND_START, BACKGROUND_END, light ); | ||||
|         emitter.vertex( x + width, y + height, z, colour, BACKGROUND_END, BACKGROUND_END, light ); | ||||
|         emitter.vertex( x + width, y, z, colour, BACKGROUND_END, BACKGROUND_START, light ); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawQuad( VertexEmitter emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex, int light ) | ||||
|     { | ||||
|         var colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale ); | ||||
|         drawQuad( emitter, x, y, 0, width, height, colour, light ); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawBackground( | ||||
|         @Nonnull VertexEmitter emitter, float x, float y, | ||||
|         @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, | ||||
|         float leftMarginSize, float rightMarginSize, float height, int light | ||||
|     ) | ||||
|     { | ||||
|         if( leftMarginSize > 0 ) | ||||
|         { | ||||
|             drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light ); | ||||
|         } | ||||
| 
 | ||||
|         if( rightMarginSize > 0 ) | ||||
|         { | ||||
|             drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ), light ); | ||||
|         } | ||||
| 
 | ||||
|         // Batch together runs of identical background cells. | ||||
|         int blockStart = 0; | ||||
|         char blockColour = '\0'; | ||||
|         for( int i = 0; i < backgroundColour.length(); i++ ) | ||||
|         { | ||||
|             char colourIndex = backgroundColour.charAt( i ); | ||||
|             if( colourIndex == blockColour ) continue; | ||||
| 
 | ||||
|             if( blockColour != '\0' ) | ||||
|             { | ||||
|                 drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light ); | ||||
|             } | ||||
| 
 | ||||
|             blockColour = colourIndex; | ||||
|             blockStart = i; | ||||
|         } | ||||
| 
 | ||||
|         if( blockColour != '\0' ) | ||||
|         { | ||||
|             drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void drawString( | ||||
|         @Nonnull VertexEmitter emitter, float x, float y, | ||||
|         @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, | ||||
|         @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize, int light | ||||
|     ) | ||||
|     { | ||||
|         if( backgroundColour != null ) | ||||
|         { | ||||
|             drawBackground( emitter, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT, light ); | ||||
|         } | ||||
| 
 | ||||
|         for( int i = 0; i < text.length(); i++ ) | ||||
|         { | ||||
|             var colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale ); | ||||
| 
 | ||||
|             // Draw char | ||||
|             int index = text.charAt( i ); | ||||
|             if( index > 255 ) index = '?'; | ||||
|             drawChar( emitter, x + i * FONT_WIDTH, y, index, colour, light ); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static void drawTerminalWithoutCursor( | ||||
|         @Nonnull VertexEmitter emitter, float x, float y, | ||||
|         @Nonnull Terminal terminal, boolean greyscale, | ||||
|         float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize | ||||
|     ) | ||||
|     { | ||||
|         Palette palette = terminal.getPalette(); | ||||
|         int height = terminal.getHeight(); | ||||
| 
 | ||||
|         // Top and bottom margins | ||||
|         drawBackground( | ||||
|             emitter, x, y - topMarginSize, | ||||
|             terminal.getBackgroundColourLine( 0 ), palette, greyscale, | ||||
|             leftMarginSize, rightMarginSize, topMarginSize, FULL_BRIGHT_LIGHTMAP | ||||
|         ); | ||||
| 
 | ||||
|         drawBackground( | ||||
|             emitter, x, y + height * FONT_HEIGHT, | ||||
|             terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, | ||||
|             leftMarginSize, rightMarginSize, bottomMarginSize, FULL_BRIGHT_LIGHTMAP | ||||
|         ); | ||||
| 
 | ||||
|         // The main text | ||||
|         for( int i = 0; i < height; i++ ) | ||||
|         { | ||||
|             drawString( | ||||
|                 emitter, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i, | ||||
|                 terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ), | ||||
|                 palette, greyscale, leftMarginSize, rightMarginSize, FULL_BRIGHT_LIGHTMAP | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void drawCursor( @Nonnull VertexEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale ) | ||||
|     { | ||||
|         Palette palette = terminal.getPalette(); | ||||
|         int width = terminal.getWidth(); | ||||
|         int height = terminal.getHeight(); | ||||
| 
 | ||||
|         int cursorX = terminal.getCursorX(); | ||||
|         int cursorY = terminal.getCursorY(); | ||||
|         if( terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height && FrameInfo.getGlobalCursorBlink() ) | ||||
|         { | ||||
|             var colour = palette.getByteColour( 15 - terminal.getTextColour(), greyscale ); | ||||
|             drawChar( emitter, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', colour, FULL_BRIGHT_LIGHTMAP ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void drawTerminal( | ||||
|         @Nonnull VertexEmitter buffer, float x, float y, | ||||
|         @Nonnull Terminal terminal, boolean greyscale, | ||||
|         float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize | ||||
|     ) | ||||
|     { | ||||
|         drawTerminalWithoutCursor( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); | ||||
|         drawCursor( buffer, x, y, terminal, greyscale ); | ||||
|     } | ||||
| 
 | ||||
|     public static void drawEmptyTerminal( @Nonnull VertexEmitter emitter, float x, float y, float width, float height ) | ||||
|     { | ||||
|         drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP ); | ||||
|     } | ||||
| 
 | ||||
|     public static void drawBlocker( @Nonnull VertexEmitter emitter, float x, float y, float width, float height ) | ||||
|     { | ||||
|         drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP ); | ||||
|     } | ||||
| 
 | ||||
|     public static int getVertexCount( Terminal terminal ) | ||||
|     { | ||||
|         int height = terminal.getHeight(); | ||||
|         int count = 0; | ||||
| 
 | ||||
|         for( int y = 0; y < height; y++ ) | ||||
|         { | ||||
|             // We compress runs of adjacent characters, so we need to do that calculation here too :/. | ||||
|             int background = 2; | ||||
|             TextBuffer backgroundColour = terminal.getBackgroundColourLine( y ); | ||||
|             char blockColour = '\0'; | ||||
|             for( int x = 0; x < backgroundColour.length(); x++ ) | ||||
|             { | ||||
|                 char colourIndex = backgroundColour.charAt( x ); | ||||
|                 if( colourIndex == blockColour ) continue; | ||||
| 
 | ||||
|                 if( blockColour != '\0' ) background++; | ||||
|                 blockColour = colourIndex; | ||||
|             } | ||||
|             if( blockColour != '\0' ) background++; | ||||
| 
 | ||||
|             count += background; | ||||
|             if( y == 0 ) count += background; | ||||
|             if( y == height - 1 ) count += background; | ||||
| 
 | ||||
|             // Thankfully the normal characters are much easier! | ||||
|             TextBuffer foreground = terminal.getLine( y ); | ||||
|             for( int x = 0; x < foreground.length(); x++ ) | ||||
|             { | ||||
|                 char c = foreground.charAt( x ); | ||||
|                 if( c != '\0' && c != ' ' ) count++; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return count * 4; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Emit a single vertex to some buffer. | ||||
|      * | ||||
|      * @see #toVertexConsumer(Matrix4f, VertexConsumer) Emits to a {@link VertexConsumer}. | ||||
|      * @see #toByteBuffer(ByteBuffer) Emits to a {@link ByteBuffer}. | ||||
|      */ | ||||
|     @FunctionalInterface | ||||
|     public interface VertexEmitter | ||||
|     { | ||||
|         void vertex( float x, float y, float z, byte[] rgba, float u, float v, int light ); | ||||
|     } | ||||
| 
 | ||||
|     public static VertexEmitter toVertexConsumer( Matrix4f matrix, VertexConsumer consumer ) | ||||
|     { | ||||
|         return ( float x, float y, float z, byte[] rgba, float u, float v, int light ) -> | ||||
|             consumer.vertex( matrix, x, y, z ).color( rgba[0], rgba[1], rgba[2], rgba[3] ).uv( u, v ).uv2( light ).endVertex(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * An optimised vertex emitter which bypasses {@link VertexConsumer}. This allows us to emit vertices very quickly, | ||||
|      * when using the VBO renderer with some limitations: | ||||
|      * <ul> | ||||
|      *     <li>No transformation matrix (not needed for VBOs).</li> | ||||
|      *     <li>Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX_LIGHTMAP}.</li> | ||||
|      * </ul> | ||||
|      * | ||||
|      * @param buffer The buffer to emit to. This must have space for at least {@link #getVertexCount(Terminal)} vertices. | ||||
|      * @return The emitter, ot be passed to the rendering functions. | ||||
|      */ | ||||
|     public static VertexEmitter toByteBuffer( ByteBuffer buffer ) | ||||
|     { | ||||
|         return ( float x, float y, float z, byte[] rgba, float u, float v, int light ) -> buffer | ||||
|             .putFloat( x ).putFloat( y ).putFloat( z ).put( rgba ).putFloat( u ).putFloat( v ); | ||||
|     } | ||||
| } | ||||
| @@ -10,7 +10,6 @@ import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.gui.widgets.ComputerSidebar; | ||||
| import dan200.computercraft.client.gui.widgets.WidgetTerminal; | ||||
| import dan200.computercraft.client.render.ComputerBorderRenderer; | ||||
| import dan200.computercraft.client.render.RenderTypes; | ||||
| import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; | ||||
| import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; | ||||
| import net.minecraft.network.chat.Component; | ||||
| @@ -19,6 +18,7 @@ import net.minecraft.world.entity.player.Inventory; | ||||
| import javax.annotation.Nonnull; | ||||
| 
 | ||||
| import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER; | ||||
| import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP; | ||||
| 
 | ||||
| public final class GuiComputer<T extends ContainerComputerBase> extends ComputerScreenBase<T> | ||||
| { | ||||
| @@ -78,7 +78,7 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Computer | ||||
|         // Draw a border around the terminal | ||||
|         ComputerBorderRenderer.render( | ||||
|             ComputerBorderRenderer.getTexture( family ), terminal.x, terminal.y, getBlitOffset(), | ||||
|             RenderTypes.FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight() | ||||
|             FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight() | ||||
|         ); | ||||
|         ComputerSidebar.renderBackground( stack, leftPos, topPos + sidebarYOffset ); | ||||
|     } | ||||
|   | ||||
| @@ -8,8 +8,8 @@ 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.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.render.RenderTypes; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.shared.computer.core.ClientComputer; | ||||
| import net.minecraft.SharedConstants; | ||||
| @@ -23,9 +23,9 @@ import org.lwjgl.glfw.GLFW; | ||||
| import javax.annotation.Nonnull; | ||||
| import java.util.BitSet; | ||||
| 
 | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH; | ||||
| import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; | ||||
| 
 | ||||
| public class WidgetTerminal extends AbstractWidget | ||||
| { | ||||
|   | ||||
| @@ -70,7 +70,6 @@ public class ComputerBorderRenderer | ||||
|         this.b = b; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Nonnull | ||||
|     public static ResourceLocation getTexture( @Nonnull ComputerFamily family ) | ||||
|     { | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import com.mojang.math.Matrix4f; | ||||
| import com.mojang.math.Vector3f; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.shared.computer.core.ClientComputer; | ||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||
| @@ -24,9 +24,9 @@ import net.minecraftforge.client.event.RenderHandEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH; | ||||
| import static dan200.computercraft.client.render.ComputerBorderRenderer.*; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; | ||||
| 
 | ||||
| /** | ||||
|  * Emulates map rendering for pocket computers. | ||||
| @@ -136,7 +136,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer | ||||
|         byte r = (byte) ((colour >>> 16) & 0xFF); | ||||
|         byte g = (byte) ((colour >>> 8) & 0xFF); | ||||
|         byte b = (byte) (colour & 0xFF); | ||||
|         var c = new byte[] { r, g, b, (byte) 255 }; | ||||
|         byte[] c = new byte[] { r, g, b, (byte) 255 }; | ||||
| 
 | ||||
|         VertexConsumer buffer = render.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ); | ||||
|         FixedWidthFontRenderer.drawQuad( | ||||
|   | ||||
| @@ -19,8 +19,8 @@ import net.minecraftforge.client.event.RenderItemInFrameEvent; | ||||
| import net.minecraftforge.eventbus.api.SubscribeEvent; | ||||
| import net.minecraftforge.fml.common.Mod; | ||||
| 
 | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; | ||||
| import static dan200.computercraft.client.render.PrintoutRenderer.*; | ||||
| import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE; | ||||
| import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH; | ||||
|   | ||||
| @@ -7,95 +7,63 @@ package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import com.mojang.blaze3d.shaders.Uniform; | ||||
| import com.mojang.blaze3d.vertex.VertexFormat; | ||||
| import dan200.computercraft.client.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.shared.util.Palette; | ||||
| import dan200.computercraft.client.FrameInfo; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.core.terminal.TextBuffer; | ||||
| import dan200.computercraft.shared.util.Colour; | ||||
| import net.minecraft.client.renderer.ShaderInstance; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraft.server.packs.resources.ResourceProvider; | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.lwjgl.opengl.GL13; | ||||
| import org.lwjgl.opengl.GL31; | ||||
| 
 | ||||
| import javax.annotation.Nullable; | ||||
| import java.io.IOException; | ||||
| import java.nio.FloatBuffer; | ||||
| import java.nio.ByteBuffer; | ||||
| 
 | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour; | ||||
| 
 | ||||
| public class MonitorTextureBufferShader extends ShaderInstance | ||||
| { | ||||
|     public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4; | ||||
| 
 | ||||
|     static final int TEXTURE_INDEX = GL13.GL_TEXTURE3; | ||||
| 
 | ||||
|     private static final Logger LOGGER = LogManager.getLogger(); | ||||
| 
 | ||||
|     private final Uniform palette; | ||||
|     private final Uniform width; | ||||
|     private final Uniform height; | ||||
|     private final int monitorData; | ||||
|     private int uniformBuffer = 0; | ||||
| 
 | ||||
|     private final Uniform cursorBlink; | ||||
| 
 | ||||
|     public MonitorTextureBufferShader( ResourceProvider provider, ResourceLocation location, VertexFormat format ) throws IOException | ||||
|     { | ||||
|         super( provider, location, format ); | ||||
|         monitorData = GL31.glGetUniformBlockIndex( getId(), "MonitorData" ); | ||||
|         if( monitorData == -1 ) throw new IllegalStateException( "Could not find MonitorData uniform." ); | ||||
| 
 | ||||
|         width = getUniformChecked( "Width" ); | ||||
|         height = getUniformChecked( "Height" ); | ||||
|         palette = new Uniform( "Palette", Uniform.UT_FLOAT3, 16 * 3, this ); | ||||
|         updateUniformLocation( palette ); | ||||
|         cursorBlink = getUniformChecked( "CursorBlink" ); | ||||
| 
 | ||||
|         Uniform tbo = getUniformChecked( "Tbo" ); | ||||
|         if( tbo != null ) tbo.set( TEXTURE_INDEX - GL13.GL_TEXTURE0 ); | ||||
|     } | ||||
| 
 | ||||
|     void setupUniform( int width, int height, Palette palette, boolean greyscale ) | ||||
|     public void setupUniform( int buffer ) | ||||
|     { | ||||
|         if( this.width != null ) this.width.set( width ); | ||||
|         if( this.height != null ) this.height.set( height ); | ||||
|         setupPalette( palette, greyscale ); | ||||
|     } | ||||
|         uniformBuffer = buffer; | ||||
| 
 | ||||
|     private void setupPalette( Palette palette, boolean greyscale ) | ||||
|     { | ||||
|         if( this.palette == null ) return; | ||||
| 
 | ||||
|         FloatBuffer paletteBuffer = this.palette.getFloatBuffer(); | ||||
|         paletteBuffer.rewind(); | ||||
|         for( int i = 0; i < 16; i++ ) | ||||
|         { | ||||
|             double[] colour = palette.getColour( i ); | ||||
|             if( greyscale ) | ||||
|             { | ||||
|                 float f = FixedWidthFontRenderer.toGreyscale( colour ); | ||||
|                 paletteBuffer.put( f ).put( f ).put( f ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 paletteBuffer.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] ); | ||||
|             } | ||||
|         } | ||||
|         int cursorAlpha = FrameInfo.getGlobalCursorBlink() ? 1 : 0; | ||||
|         if( cursorBlink != null && cursorBlink.getIntBuffer().get( 0 ) != cursorAlpha ) cursorBlink.set( cursorAlpha ); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void apply() | ||||
|     { | ||||
|         super.apply(); | ||||
|         palette.upload(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void close() | ||||
|     { | ||||
|         palette.close(); | ||||
|         super.close(); | ||||
|     } | ||||
| 
 | ||||
|     private void updateUniformLocation( Uniform uniform ) | ||||
|     { | ||||
|         int id = Uniform.glGetUniformLocation( getId(), uniform.getName() ); | ||||
|         if( id == -1 ) | ||||
|         { | ||||
|             LOGGER.warn( "Shader {} could not find uniform named {} in the specified shader program.", getName(), uniform.getName() ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             uniform.setLocation( id ); | ||||
|         } | ||||
|         GL31.glBindBufferBase( GL31.GL_UNIFORM_BUFFER, monitorData, uniformBuffer ); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
| @@ -109,4 +77,56 @@ public class MonitorTextureBufferShader extends ShaderInstance | ||||
| 
 | ||||
|         return uniform; | ||||
|     } | ||||
| 
 | ||||
|     public static void setTerminalData( ByteBuffer buffer, Terminal terminal ) | ||||
|     { | ||||
|         int width = terminal.getWidth(), height = terminal.getHeight(); | ||||
| 
 | ||||
|         int pos = 0; | ||||
|         for( int y = 0; y < height; y++ ) | ||||
|         { | ||||
|             TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y ); | ||||
|             for( int x = 0; x < width; x++ ) | ||||
|             { | ||||
|                 buffer.put( pos, (byte) (text.charAt( x ) & 0xFF) ); | ||||
|                 buffer.put( pos + 1, (byte) getColour( textColour.charAt( x ), Colour.WHITE ) ); | ||||
|                 buffer.put( pos + 2, (byte) getColour( background.charAt( x ), Colour.BLACK ) ); | ||||
|                 pos += 3; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         buffer.limit( pos ); | ||||
|     } | ||||
| 
 | ||||
|     public static void setUniformData( ByteBuffer buffer, Terminal terminal, boolean greyscale ) | ||||
|     { | ||||
|         int pos = 0; | ||||
|         var palette = terminal.getPalette(); | ||||
|         for( int i = 0; i < 16; i++ ) | ||||
|         { | ||||
|             { | ||||
|                 double[] colour = palette.getColour( i ); | ||||
|                 if( greyscale ) | ||||
|                 { | ||||
|                     float f = FixedWidthFontRenderer.toGreyscale( colour ); | ||||
|                     buffer.putFloat( pos, f ).putFloat( pos + 4, f ).putFloat( pos + 8, f ); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     buffer.putFloat( pos, (float) colour[0] ).putFloat( pos + 4, (float) colour[1] ).putFloat( pos + 8, (float) colour[2] ); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             pos += 4 * 4; // std140 requires these are 4-wide | ||||
|         } | ||||
| 
 | ||||
|         boolean showCursor = FixedWidthFontRenderer.isCursorVisible( terminal ); | ||||
|         buffer | ||||
|             .putInt( pos, terminal.getWidth() ).putInt( pos + 4, terminal.getHeight() ) | ||||
|             .putInt( pos + 8, showCursor ? terminal.getCursorX() : -2 ) | ||||
|             .putInt( pos + 12, showCursor ? terminal.getCursorY() : -2 ) | ||||
|             .putInt( pos + 16, 15 - terminal.getTextColour() ); | ||||
| 
 | ||||
|         buffer.limit( UNIFORM_SIZE ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,12 +7,12 @@ package dan200.computercraft.client.render; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import com.mojang.math.Matrix4f; | ||||
| import dan200.computercraft.client.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.core.terminal.TextBuffer; | ||||
| import dan200.computercraft.shared.util.Palette; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| 
 | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE; | ||||
| 
 | ||||
| public final class PrintoutRenderer | ||||
| @@ -61,9 +61,8 @@ public final class PrintoutRenderer | ||||
|         for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) | ||||
|         { | ||||
|             FixedWidthFontRenderer.drawString( emitter, | ||||
|                 x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT, | ||||
|                 false, 0, 0, | ||||
|                 light | ||||
|                 x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], | ||||
|                 Palette.DEFAULT, false, light | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| @@ -77,8 +76,7 @@ public final class PrintoutRenderer | ||||
|             FixedWidthFontRenderer.drawString( emitter, | ||||
|                 x, y + line * FONT_HEIGHT, | ||||
|                 new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ), | ||||
|                 null, Palette.DEFAULT, false, 0, 0, | ||||
|                 light | ||||
|                 Palette.DEFAULT, false, light | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ package dan200.computercraft.client.render; | ||||
| import com.mojang.blaze3d.vertex.DefaultVertexFormat; | ||||
| import com.mojang.blaze3d.vertex.VertexFormat; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import net.minecraft.client.renderer.GameRenderer; | ||||
| import net.minecraft.client.renderer.RenderStateShard; | ||||
| import net.minecraft.client.renderer.RenderType; | ||||
| @@ -108,7 +108,6 @@ public class RenderTypes | ||||
|             RenderType.CompositeState.builder() | ||||
|                 .setTextureState( TERM_FONT_TEXTURE ) | ||||
|                 .setShaderState( new ShaderStateShard( RenderTypes::getMonitorTextureBufferShader ) ) | ||||
|                 .setWriteMaskState( COLOR_WRITE ) | ||||
|                 .createCompositeState( false ) | ||||
|         ); | ||||
| 
 | ||||
|   | ||||
| @@ -14,13 +14,13 @@ import com.mojang.math.Matrix4f; | ||||
| import com.mojang.math.Vector3f; | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.FrameInfo; | ||||
| import dan200.computercraft.client.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.util.DirectBuffers; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.core.terminal.TextBuffer; | ||||
| 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.Colour; | ||||
| import dan200.computercraft.shared.util.DirectionUtil; | ||||
| import net.minecraft.client.renderer.MultiBufferSource; | ||||
| import net.minecraft.client.renderer.RenderType; | ||||
| @@ -35,7 +35,8 @@ import org.lwjgl.opengl.GL31; | ||||
| import javax.annotation.Nonnull; | ||||
| import java.nio.ByteBuffer; | ||||
| 
 | ||||
| import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||
| import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; | ||||
| 
 | ||||
| public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonitor> | ||||
| { | ||||
| @@ -114,24 +115,7 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito | ||||
| 
 | ||||
|             renderTerminal( bufferSource, matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) ); | ||||
| 
 | ||||
|             // We don't draw the cursor with the VBO/TBO, as it's dynamic and so we'll end up refreshing far more than | ||||
|             // is reasonable. | ||||
|             FixedWidthFontRenderer.drawCursor( | ||||
|                 FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ) ), | ||||
|                 0, 0, terminal, !originTerminal.isColour() | ||||
|             ); | ||||
| 
 | ||||
|             transform.popPose(); | ||||
| 
 | ||||
|             FixedWidthFontRenderer.drawBlocker( | ||||
|                 FixedWidthFontRenderer.toVertexConsumer( transform.last().pose(), bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ) ), | ||||
|                 -MARGIN, MARGIN, | ||||
|                 (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) | ||||
|             ); | ||||
| 
 | ||||
|             // Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in | ||||
|             // buffers before calling renderer.finish, which means the blocker isn't actually rendered at that point! | ||||
|             bufferSource.getBuffer( RenderType.solid() ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -148,6 +132,8 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito | ||||
|     private static void renderTerminal( @Nonnull MultiBufferSource bufferSource, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin ) | ||||
|     { | ||||
|         Terminal terminal = monitor.getTerminal(); | ||||
|         int width = terminal.getWidth(), height = terminal.getHeight(); | ||||
|         int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; | ||||
| 
 | ||||
|         MonitorRenderer renderType = MonitorRenderer.current(); | ||||
|         boolean redraw = monitor.pollTerminalChanged(); | ||||
| @@ -157,27 +143,15 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito | ||||
|         { | ||||
|             case TBO: | ||||
|             { | ||||
|                 int width = terminal.getWidth(), height = terminal.getHeight(); | ||||
| 
 | ||||
|                 int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; | ||||
|                 if( redraw ) | ||||
|                 { | ||||
|                     ByteBuffer monitorBuffer = getBuffer( width * height * 3 ); | ||||
|                     for( int y = 0; y < height; y++ ) | ||||
|                     { | ||||
|                         TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y ); | ||||
|                         for( int x = 0; x < width; x++ ) | ||||
|                         { | ||||
|                             monitorBuffer.put( (byte) (text.charAt( x ) & 0xFF) ); | ||||
|                             monitorBuffer.put( (byte) getColour( textColour.charAt( x ), Colour.WHITE ) ); | ||||
|                             monitorBuffer.put( (byte) getColour( background.charAt( x ), Colour.BLACK ) ); | ||||
|                         } | ||||
|                     } | ||||
|                     monitorBuffer.flip(); | ||||
|                     var terminalBuffer = getBuffer( width * height * 3 ); | ||||
|                     MonitorTextureBufferShader.setTerminalData( terminalBuffer, terminal ); | ||||
|                     DirectBuffers.setBufferData( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer, terminalBuffer, GL20.GL_STATIC_DRAW ); | ||||
| 
 | ||||
|                     GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer ); | ||||
|                     GlStateManager._glBufferData( GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW ); | ||||
|                     GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 ); | ||||
|                     var uniformBuffer = getBuffer( MonitorTextureBufferShader.UNIFORM_SIZE ); | ||||
|                     MonitorTextureBufferShader.setUniformData( uniformBuffer, terminal, !monitor.isColour() ); | ||||
|                     DirectBuffers.setBufferData( GL31.GL_UNIFORM_BUFFER, monitor.tboUniform, uniformBuffer, GL20.GL_STATIC_DRAW ); | ||||
|                 } | ||||
| 
 | ||||
|                 // Nobody knows what they're doing! | ||||
| @@ -187,17 +161,15 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito | ||||
|                 RenderSystem.activeTexture( active ); | ||||
| 
 | ||||
|                 MonitorTextureBufferShader shader = RenderTypes.getMonitorTextureBufferShader(); | ||||
|                 shader.setupUniform( width, height, terminal.getPalette(), !monitor.isColour() ); | ||||
|                 shader.setupUniform( monitor.tboUniform ); | ||||
| 
 | ||||
|                 // TODO: Switch to using a VBO here? Something to avoid having to do the | ||||
|                 VertexConsumer buffer = bufferSource.getBuffer( RenderTypes.MONITOR_TBO ); | ||||
|                 tboVertex( buffer, matrix, -xMargin, -yMargin ); | ||||
|                 tboVertex( buffer, matrix, -xMargin, pixelHeight + yMargin ); | ||||
|                 tboVertex( buffer, matrix, pixelWidth + xMargin, -yMargin ); | ||||
|                 tboVertex( buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin ); | ||||
| 
 | ||||
|                 // And force things to flush. We strictly speaking do this later on anyway for the cursor, but nice to | ||||
|                 // be consistent. | ||||
|                 bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ); | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
| @@ -206,23 +178,45 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito | ||||
|                 var vbo = monitor.buffer; | ||||
|                 if( redraw ) | ||||
|                 { | ||||
|                     int vertexCount = FixedWidthFontRenderer.getVertexCount( terminal ); | ||||
|                     ByteBuffer buffer = getBuffer( vertexCount * RenderTypes.TERMINAL_WITHOUT_DEPTH.format().getVertexSize() ); | ||||
|                     FixedWidthFontRenderer.drawTerminalWithoutCursor( | ||||
|                         FixedWidthFontRenderer.toByteBuffer( buffer ), 0, 0, | ||||
|                         terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin | ||||
|                     int vertexSize = RenderTypes.TERMINAL_WITHOUT_DEPTH.format().getVertexSize(); | ||||
|                     ByteBuffer buffer = getBuffer( DirectFixedWidthFontRenderer.getVertexCount( terminal ) * vertexSize ); | ||||
| 
 | ||||
|                     // Draw the main terminal and store how many vertices it has. | ||||
|                     DirectFixedWidthFontRenderer.drawTerminalWithoutCursor( | ||||
|                         buffer, 0, 0, terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin | ||||
|                     ); | ||||
|                     int termIndexes = buffer.position() / vertexSize; | ||||
| 
 | ||||
|                     // 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( buffer, 0, 0, terminal, !monitor.isColour() ); | ||||
| 
 | ||||
|                     buffer.flip(); | ||||
| 
 | ||||
|                     vbo.upload( vertexCount, RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format(), buffer ); | ||||
|                     vbo.upload( termIndexes, RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format(), buffer ); | ||||
|                 } | ||||
| 
 | ||||
|                 bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ); | ||||
|                 RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState(); | ||||
|                 vbo.drawWithShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader() ); | ||||
| 
 | ||||
|                 vbo.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.drawBlocker( | ||||
|                     FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ) ), | ||||
|                     -xMargin, -yMargin, pixelWidth + xMargin, pixelHeight + yMargin | ||||
|                 ); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Force a flush of the buffer. WorldRenderer.updateCameraAndRender will "finish" all the built-in buffers | ||||
|         // before calling renderer.finish, which means our TBO quad or depth blocker won't be rendered yet! | ||||
|         bufferSource.getBuffer( RenderType.solid() ); | ||||
|     } | ||||
| 
 | ||||
|     private static void tboVertex( VertexConsumer builder, Matrix4f matrix, float x, float y ) | ||||
| @@ -238,7 +232,7 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito | ||||
|         ByteBuffer buffer = backingBuffer; | ||||
|         if( buffer == null || buffer.capacity() < capacity ) | ||||
|         { | ||||
|             buffer = backingBuffer = MemoryTracker.create( capacity ); | ||||
|             buffer = backingBuffer = buffer == null ? MemoryTracker.create( capacity ) : MemoryTracker.resize( buffer, capacity ); | ||||
|         } | ||||
| 
 | ||||
|         buffer.clear(); | ||||
|   | ||||
| @@ -0,0 +1,231 @@ | ||||
| /* | ||||
|  * 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.client.render.text; | ||||
| 
 | ||||
| import com.mojang.blaze3d.platform.MemoryTracker; | ||||
| import com.mojang.blaze3d.vertex.DefaultVertexFormat; | ||||
| import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import dan200.computercraft.core.terminal.Terminal; | ||||
| import dan200.computercraft.core.terminal.TextBuffer; | ||||
| import dan200.computercraft.shared.util.Colour; | ||||
| import dan200.computercraft.shared.util.Palette; | ||||
| import org.lwjgl.system.MemoryUtil; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * An optimised copy of {@link FixedWidthFontRenderer} emitter emits directly to a {@link ByteBuffer} rather than | ||||
|  * emitting to {@link VertexConsumer}. This allows us to emit vertices very quickly, when using the VBO renderer. | ||||
|  * | ||||
|  * There are some limitations here: | ||||
|  * <ul> | ||||
|  *   <li>No transformation matrix (not needed for VBOs).</li> | ||||
|  *   <li>Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX}.</li> | ||||
|  *   <li>The buffer <strong>MUST</strong> be allocated with {@link MemoryTracker}, and not through any other means.</li> | ||||
|  * </ul> | ||||
|  * | ||||
|  * Note this is almost an exact copy of {@link FixedWidthFontRenderer}. While the code duplication is unfortunate, | ||||
|  * it is measurably faster than introducing polymorphism into {@link FixedWidthFontRenderer}. | ||||
|  * | ||||
|  * <strong>IMPORTANT: </strong> When making changes to this class, please check if you need to make the same changes to | ||||
|  * {@link FixedWidthFontRenderer}. | ||||
|  */ | ||||
| public final class DirectFixedWidthFontRenderer | ||||
| { | ||||
|     private DirectFixedWidthFontRenderer() | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private static void drawChar( ByteBuffer buffer, 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; | ||||
| 
 | ||||
|         int column = index % 16; | ||||
|         int row = index / 16; | ||||
| 
 | ||||
|         int xStart = 1 + column * (FONT_WIDTH + 2); | ||||
|         int yStart = 1 + row * (FONT_HEIGHT + 2); | ||||
| 
 | ||||
|         quad( | ||||
|             buffer, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 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 ) | ||||
|     { | ||||
|         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 ); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawBackground( | ||||
|         @Nonnull ByteBuffer buffer, 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 ) ); | ||||
|         } | ||||
| 
 | ||||
|         if( rightMarginSize > 0 ) | ||||
|         { | ||||
|             drawQuad( buffer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) ); | ||||
|         } | ||||
| 
 | ||||
|         // Batch together runs of identical background cells. | ||||
|         int blockStart = 0; | ||||
|         char blockColour = '\0'; | ||||
|         for( int i = 0; i < backgroundColour.length(); i++ ) | ||||
|         { | ||||
|             char colourIndex = backgroundColour.charAt( i ); | ||||
|             if( colourIndex == blockColour ) continue; | ||||
| 
 | ||||
|             if( blockColour != '\0' ) | ||||
|             { | ||||
|                 drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour ); | ||||
|             } | ||||
| 
 | ||||
|             blockColour = colourIndex; | ||||
|             blockStart = i; | ||||
|         } | ||||
| 
 | ||||
|         if( blockColour != '\0' ) | ||||
|         { | ||||
|             drawQuad( buffer, 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 ) | ||||
|     { | ||||
|         for( int i = 0; i < text.length(); i++ ) | ||||
|         { | ||||
|             byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale ); | ||||
| 
 | ||||
|             int index = text.charAt( i ); | ||||
|             if( index > 255 ) index = '?'; | ||||
|             drawChar( buffer, x + i * FONT_WIDTH, y, index, colour ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void drawTerminalWithoutCursor( | ||||
|         @Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale, | ||||
|         float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize | ||||
|     ) | ||||
|     { | ||||
|         Palette palette = terminal.getPalette(); | ||||
|         int height = terminal.getHeight(); | ||||
| 
 | ||||
|         // Top and bottom margins | ||||
|         drawBackground( | ||||
|             buffer, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale, | ||||
|             leftMarginSize, rightMarginSize, topMarginSize | ||||
|         ); | ||||
| 
 | ||||
|         drawBackground( | ||||
|             buffer, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, | ||||
|             leftMarginSize, rightMarginSize, bottomMarginSize | ||||
|         ); | ||||
| 
 | ||||
|         // The main text | ||||
|         for( int i = 0; i < height; i++ ) | ||||
|         { | ||||
|             float rowY = y + FONT_HEIGHT * i; | ||||
|             drawBackground( | ||||
|                 buffer, 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 ) | ||||
|     { | ||||
|         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 ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static int getVertexCount( Terminal terminal ) | ||||
|     { | ||||
|         return (1 + (terminal.getHeight() + 2) * terminal.getWidth() * 2) * 4; | ||||
|     } | ||||
| 
 | ||||
|     private static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, 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. | ||||
| 
 | ||||
|         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(); | ||||
|         // 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. | ||||
|         if( rgba.length != 4 ) throw new IllegalStateException(); | ||||
| 
 | ||||
|         memPutFloat( addr + 0, x1 ); | ||||
|         memPutFloat( addr + 4, y1 ); | ||||
|         memPutFloat( addr + 8, 0 ); | ||||
|         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 ); | ||||
| 
 | ||||
|         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 + 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 + 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 ); | ||||
| 
 | ||||
|         // Finally increment the position. | ||||
|         buffer.position( position + 96 ); | ||||
| 
 | ||||
|         // Well done for getting to the end of this method. I recommend you take a break and go look at cute puppies. | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,243 @@ | ||||
| /* | ||||
|  * 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.client.render.text; | ||||
| 
 | ||||
| import com.mojang.blaze3d.vertex.VertexConsumer; | ||||
| import com.mojang.math.Matrix4f; | ||||
| 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; | ||||
| import dan200.computercraft.shared.util.Palette; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| 
 | ||||
| import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP; | ||||
| 
 | ||||
| /** | ||||
|  * Handles rendering fixed width text and computer terminals. | ||||
|  * | ||||
|  * This class has several modes of usage: | ||||
|  * <ul> | ||||
|  * <li>{@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.</li> | ||||
|  * <li>{@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. | ||||
|  * </li> | ||||
|  * <li>{@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the | ||||
|  * whole term.</li> | ||||
|  * <li>{@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.</li> | ||||
|  * </ul> | ||||
|  * | ||||
|  * <strong>IMPORTANT: </strong> When making changes to this class, please check if you need to make the same changes to | ||||
|  * {@link DirectFixedWidthFontRenderer}. | ||||
|  */ | ||||
| public final class FixedWidthFontRenderer | ||||
| { | ||||
|     public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" ); | ||||
| 
 | ||||
|     public static final int FONT_HEIGHT = 9; | ||||
|     public static final int FONT_WIDTH = 6; | ||||
|     static final float WIDTH = 256.0f; | ||||
| 
 | ||||
|     static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; | ||||
|     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 FixedWidthFontRenderer() | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     private static byte byteColour( float c ) | ||||
|     { | ||||
|         return (byte) (int) (c * 255); | ||||
|     } | ||||
| 
 | ||||
|     public static float toGreyscale( double[] rgb ) | ||||
|     { | ||||
|         return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3); | ||||
|     } | ||||
| 
 | ||||
|     public static int getColour( char c, Colour def ) | ||||
|     { | ||||
|         return 15 - Terminal.getColour( c, def ); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawChar( QuadEmitter emitter, float x, float y, int index, byte[] colour, int light ) | ||||
|     { | ||||
|         // Short circuit to avoid the common case - the texture should be blank here after all. | ||||
|         if( index == '\0' || index == ' ' ) return; | ||||
| 
 | ||||
|         int column = index % 16; | ||||
|         int row = index / 16; | ||||
| 
 | ||||
|         int xStart = 1 + column * (FONT_WIDTH + 2); | ||||
|         int yStart = 1 + row * (FONT_HEIGHT + 2); | ||||
| 
 | ||||
|         quad( | ||||
|             emitter, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour, | ||||
|             xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     public static void drawQuad( QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light ) | ||||
|     { | ||||
|         quad( emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light ); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawQuad( QuadEmitter emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex, int light ) | ||||
|     { | ||||
|         byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale ); | ||||
|         drawQuad( emitter, x, y, 0, width, height, colour, light ); | ||||
|     } | ||||
| 
 | ||||
|     private static void drawBackground( | ||||
|         @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, | ||||
|         float leftMarginSize, float rightMarginSize, float height, int light | ||||
|     ) | ||||
|     { | ||||
|         if( leftMarginSize > 0 ) | ||||
|         { | ||||
|             drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light ); | ||||
|         } | ||||
| 
 | ||||
|         if( rightMarginSize > 0 ) | ||||
|         { | ||||
|             drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ), light ); | ||||
|         } | ||||
| 
 | ||||
|         // Batch together runs of identical background cells. | ||||
|         int blockStart = 0; | ||||
|         char blockColour = '\0'; | ||||
|         for( int i = 0; i < backgroundColour.length(); i++ ) | ||||
|         { | ||||
|             char colourIndex = backgroundColour.charAt( i ); | ||||
|             if( colourIndex == blockColour ) continue; | ||||
| 
 | ||||
|             if( blockColour != '\0' ) | ||||
|             { | ||||
|                 drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light ); | ||||
|             } | ||||
| 
 | ||||
|             blockColour = colourIndex; | ||||
|             blockStart = i; | ||||
|         } | ||||
| 
 | ||||
|         if( blockColour != '\0' ) | ||||
|         { | ||||
|             drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void drawString( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale, int light ) | ||||
|     { | ||||
|         for( int i = 0; i < text.length(); i++ ) | ||||
|         { | ||||
|             byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale ); | ||||
| 
 | ||||
|             int index = text.charAt( i ); | ||||
|             if( index > 255 ) index = '?'; | ||||
|             drawChar( emitter, x + i * FONT_WIDTH, y, index, colour, light ); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static void drawTerminalWithoutCursor( | ||||
|         @Nonnull QuadEmitter emitter, float x, float y, | ||||
|         @Nonnull Terminal terminal, boolean greyscale, | ||||
|         float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize | ||||
|     ) | ||||
|     { | ||||
|         Palette palette = terminal.getPalette(); | ||||
|         int height = terminal.getHeight(); | ||||
| 
 | ||||
|         // Top and bottom margins | ||||
|         drawBackground( | ||||
|             emitter, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale, | ||||
|             leftMarginSize, rightMarginSize, topMarginSize, FULL_BRIGHT_LIGHTMAP | ||||
|         ); | ||||
| 
 | ||||
|         drawBackground( | ||||
|             emitter, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, | ||||
|             leftMarginSize, rightMarginSize, bottomMarginSize, FULL_BRIGHT_LIGHTMAP | ||||
|         ); | ||||
| 
 | ||||
|         // The main text | ||||
|         for( int i = 0; i < height; i++ ) | ||||
|         { | ||||
|             float rowY = y + FixedWidthFontRenderer.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 | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isCursorVisible( Terminal terminal ) | ||||
|     { | ||||
|         if( !terminal.getCursorBlink() ) return false; | ||||
| 
 | ||||
|         int cursorX = terminal.getCursorX(); | ||||
|         int cursorY = terminal.getCursorY(); | ||||
|         return cursorX >= 0 && cursorX < terminal.getWidth() && cursorY >= 0 && cursorY < terminal.getHeight(); | ||||
|     } | ||||
| 
 | ||||
|     public static void drawCursor( @Nonnull QuadEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale ) | ||||
|     { | ||||
|         if( isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() ) | ||||
|         { | ||||
|             byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale ); | ||||
|             drawChar( emitter, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, FULL_BRIGHT_LIGHTMAP ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void drawTerminal( | ||||
|         @Nonnull QuadEmitter emitter, float x, float y, | ||||
|         @Nonnull Terminal terminal, boolean greyscale, | ||||
|         float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize | ||||
|     ) | ||||
|     { | ||||
|         drawTerminalWithoutCursor( emitter, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); | ||||
|         drawCursor( emitter, x, y, terminal, greyscale ); | ||||
|     } | ||||
| 
 | ||||
|     public static void drawEmptyTerminal( @Nonnull QuadEmitter emitter, float x, float y, float width, float height ) | ||||
|     { | ||||
|         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 ) | ||||
|     { | ||||
|         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 ) | ||||
|     { | ||||
|         return new QuadEmitter( matrix, 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 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(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
|  * 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.client.util; | ||||
| 
 | ||||
| import com.mojang.blaze3d.platform.GlStateManager; | ||||
| import com.mojang.blaze3d.vertex.BufferUploader; | ||||
| import org.lwjgl.opengl.GL; | ||||
| import org.lwjgl.opengl.GL15C; | ||||
| import org.lwjgl.opengl.GL45C; | ||||
| 
 | ||||
| import java.nio.ByteBuffer; | ||||
| 
 | ||||
| /** | ||||
|  * Provides utilities to interact with OpenGL's buffer objects, either using direct state access or binding/unbinding | ||||
|  * it. | ||||
|  */ | ||||
| public class DirectBuffers | ||||
| { | ||||
|     public static final boolean HAS_DSA; | ||||
| 
 | ||||
|     static | ||||
|     { | ||||
|         var capabilities = GL.getCapabilities(); | ||||
|         HAS_DSA = capabilities.OpenGL45 || capabilities.GL_ARB_direct_state_access; | ||||
|     } | ||||
| 
 | ||||
|     public static int createBuffer() | ||||
|     { | ||||
|         return HAS_DSA ? GL45C.glCreateBuffers() : GL15C.glGenBuffers(); | ||||
|     } | ||||
| 
 | ||||
|     public static void setBufferData( int type, int id, ByteBuffer buffer, int flags ) | ||||
|     { | ||||
|         if( HAS_DSA ) | ||||
|         { | ||||
|             GL45C.glNamedBufferData( id, buffer, flags ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if( type == GL45C.GL_ARRAY_BUFFER ) BufferUploader.reset(); | ||||
|             GlStateManager._glBindBuffer( type, id ); | ||||
|             GlStateManager._glBufferData( type, buffer, GL15C.GL_STATIC_DRAW ); | ||||
|             GlStateManager._glBindBuffer( type, 0 ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void setEmptyBufferData( int type, int id, int flags ) | ||||
|     { | ||||
|         if( HAS_DSA ) | ||||
|         { | ||||
|             GL45C.glNamedBufferData( id, 0, flags ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if( type == GL45C.GL_ARRAY_BUFFER ) BufferUploader.reset(); | ||||
|             GlStateManager._glBindBuffer( type, id ); | ||||
|             GlStateManager._glBufferData( type, 0, GL15C.GL_STATIC_DRAW ); | ||||
|             GlStateManager._glBindBuffer( type, 0 ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -6,10 +6,10 @@ | ||||
| package dan200.computercraft.client.util; | ||||
| 
 | ||||
| import com.mojang.blaze3d.systems.RenderSystem; | ||||
| import com.mojang.blaze3d.vertex.BufferUploader; | ||||
| import com.mojang.blaze3d.vertex.VertexBuffer; | ||||
| import com.mojang.blaze3d.vertex.VertexFormat; | ||||
| import org.lwjgl.opengl.GL; | ||||
| import com.mojang.math.Matrix4f; | ||||
| import net.minecraft.client.renderer.ShaderInstance; | ||||
| import org.lwjgl.opengl.GL15; | ||||
| import org.lwjgl.opengl.GL45C; | ||||
| 
 | ||||
| @@ -22,17 +22,11 @@ import java.nio.ByteBuffer; | ||||
|  */ | ||||
| public class DirectVertexBuffer extends VertexBuffer | ||||
| { | ||||
|     private static final boolean HAS_DSA; | ||||
| 
 | ||||
|     static | ||||
|     { | ||||
|         var capabilities = GL.getCapabilities(); | ||||
|         HAS_DSA = capabilities.OpenGL45 || capabilities.GL_ARB_direct_state_access; | ||||
|     } | ||||
|     private int actualIndexCount; | ||||
| 
 | ||||
|     public DirectVertexBuffer() | ||||
|     { | ||||
|         if( HAS_DSA ) | ||||
|         if( DirectBuffers.HAS_DSA ) | ||||
|         { | ||||
|             RenderSystem.glDeleteBuffers( vertextBufferId ); | ||||
|             vertextBufferId = GL45C.glCreateBuffers(); | ||||
| @@ -43,22 +37,24 @@ public class DirectVertexBuffer extends VertexBuffer | ||||
|     { | ||||
|         RenderSystem.assertOnRenderThread(); | ||||
| 
 | ||||
|         if( HAS_DSA ) | ||||
|         { | ||||
|             GL45C.glNamedBufferData( vertextBufferId, buffer, GL15.GL_STATIC_DRAW ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             BufferUploader.reset(); | ||||
|             bind(); | ||||
|             RenderSystem.glBufferData( GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW ); | ||||
|             unbind(); | ||||
|         } | ||||
|         DirectBuffers.setBufferData( GL15.GL_ARRAY_BUFFER, vertextBufferId, buffer, GL15.GL_STATIC_DRAW ); | ||||
| 
 | ||||
|         this.format = format; | ||||
|         this.mode = mode; | ||||
|         indexCount = mode.indexCount( vertexCount ); | ||||
|         actualIndexCount = indexCount = mode.indexCount( vertexCount ); | ||||
|         indexType = VertexFormat.IndexType.SHORT; | ||||
|         sequentialIndices = true; | ||||
|     } | ||||
| 
 | ||||
|     public void drawWithShader( Matrix4f modelView, Matrix4f projection, ShaderInstance shader, int indexCount ) | ||||
|     { | ||||
|         this.indexCount = indexCount; | ||||
|         drawWithShader( modelView, projection, shader ); | ||||
|         this.indexCount = actualIndexCount; | ||||
|     } | ||||
| 
 | ||||
|     public int getIndexCount() | ||||
|     { | ||||
|         return actualIndexCount; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -57,7 +57,8 @@ public final class ComputerMBean implements DynamicMBean, Tracker | ||||
|         { | ||||
|             ManagementFactory.getPlatformMBeanServer().registerMBean( instance = new ComputerMBean(), new ObjectName( "dan200.computercraft:type=Computers" ) ); | ||||
|         } | ||||
|         catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException | MalformedObjectNameException e ) | ||||
|         catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException | | ||||
|                MalformedObjectNameException e ) | ||||
|         { | ||||
|             ComputerCraft.log.warn( "Failed to register JMX bean", e ); | ||||
|         } | ||||
|   | ||||
| @@ -99,7 +99,7 @@ public class TileCommandComputer extends TileComputer | ||||
|         } | ||||
| 
 | ||||
|         return new CommandSourceStack( receiver, | ||||
|             new Vec3( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), Vec2.ZERO, | ||||
|             Vec3.atCenterOf( worldPosition ), Vec2.ZERO, | ||||
|             (ServerLevel) getLevel(), 2, | ||||
|             name, new TextComponent( name ), | ||||
|             getLevel().getServer(), null | ||||
|   | ||||
| @@ -45,7 +45,7 @@ public class GenericPeripheralProvider | ||||
| 
 | ||||
|         for( Capability<?> capability : capabilities ) | ||||
|         { | ||||
|             LazyOptional<?> wrapper = tile.getCapability( capability ); | ||||
|             LazyOptional<?> wrapper = CapabilityUtil.getCapability( tile, capability, side ); | ||||
|             wrapper.ifPresent( contents -> { | ||||
|                 List<NamedMethod<PeripheralMethod>> capabilityMethods = PeripheralMethod.GENERATOR.getMethods( contents.getClass() ); | ||||
|                 if( capabilityMethods.isEmpty() ) return; | ||||
|   | ||||
| @@ -60,8 +60,7 @@ public class TileCable extends TileGeneric | ||||
|         @Override | ||||
|         public Vec3 getPosition() | ||||
|         { | ||||
|             BlockPos pos = getBlockPos(); | ||||
|             return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); | ||||
|             return Vec3.atCenterOf( getBlockPos() ); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
| @@ -104,8 +103,7 @@ public class TileCable extends TileGeneric | ||||
|         @Override | ||||
|         public Vec3 getPosition() | ||||
|         { | ||||
|             BlockPos pos = getBlockPos().relative( getDirection() ); | ||||
|             return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); | ||||
|             return Vec3.atCenterOf( getBlockPos().relative( getDirection() ) ); | ||||
|         } | ||||
| 
 | ||||
|         @Nonnull | ||||
|   | ||||
| @@ -87,8 +87,7 @@ public class TileWiredModemFull extends TileGeneric | ||||
|         @Override | ||||
|         public Vec3 getPosition() | ||||
|         { | ||||
|             BlockPos pos = entity.getBlockPos(); | ||||
|             return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); | ||||
|             return Vec3.atCenterOf( entity.getBlockPos() ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -417,8 +416,7 @@ public class TileWiredModemFull extends TileGeneric | ||||
|             @Override | ||||
|             public Vec3 getPosition() | ||||
|             { | ||||
|                 BlockPos pos = getBlockPos().relative( side ); | ||||
|                 return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); | ||||
|                 return Vec3.atCenterOf( getBlockPos().relative( side ) ); | ||||
|             } | ||||
| 
 | ||||
|             @Nonnull | ||||
|   | ||||
| @@ -48,8 +48,7 @@ public class TileWirelessModem extends TileGeneric | ||||
|         @Override | ||||
|         public Vec3 getPosition() | ||||
|         { | ||||
|             BlockPos pos = entity.getBlockPos().relative( entity.getDirection() ); | ||||
|             return new Vec3( pos.getX(), pos.getY(), pos.getZ() ); | ||||
|             return Vec3.atLowerCornerOf( entity.getBlockPos().relative( entity.getDirection() ) ); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|   | ||||
| @@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor; | ||||
| 
 | ||||
| import com.mojang.blaze3d.platform.GlStateManager; | ||||
| import com.mojang.blaze3d.systems.RenderSystem; | ||||
| import dan200.computercraft.client.util.DirectBuffers; | ||||
| import dan200.computercraft.client.util.DirectVertexBuffer; | ||||
| import dan200.computercraft.shared.common.ClientTerminal; | ||||
| import net.minecraft.core.BlockPos; | ||||
| @@ -32,6 +33,7 @@ public final class ClientMonitor extends ClientTerminal | ||||
| 
 | ||||
|     public int tboBuffer; | ||||
|     public int tboTexture; | ||||
|     public int tboUniform; | ||||
|     public DirectVertexBuffer buffer; | ||||
| 
 | ||||
|     public ClientMonitor( boolean colour, TileMonitor origin ) | ||||
| @@ -63,15 +65,15 @@ public final class ClientMonitor extends ClientTerminal | ||||
| 
 | ||||
|                 deleteBuffers(); | ||||
| 
 | ||||
|                 tboBuffer = GlStateManager._glGenBuffers(); | ||||
|                 GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, tboBuffer ); | ||||
|                 GL15.glBufferData( GL31.GL_TEXTURE_BUFFER, 0, GL15.GL_STATIC_DRAW ); | ||||
|                 tboBuffer = DirectBuffers.createBuffer(); | ||||
|                 DirectBuffers.setEmptyBufferData( GL31.GL_TEXTURE_BUFFER, tboBuffer, GL15.GL_STATIC_DRAW ); | ||||
|                 tboTexture = GlStateManager._genTexture(); | ||||
|                 GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, tboTexture ); | ||||
|                 GL31.glTexBuffer( GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, tboBuffer ); | ||||
|                 GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, 0 ); | ||||
| 
 | ||||
|                 GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 ); | ||||
|                 tboUniform = DirectBuffers.createBuffer(); | ||||
|                 DirectBuffers.setEmptyBufferData( GL31.GL_UNIFORM_BUFFER, tboUniform, GL15.GL_STATIC_DRAW ); | ||||
| 
 | ||||
|                 addMonitor(); | ||||
|                 return true; | ||||
| @@ -113,6 +115,12 @@ public final class ClientMonitor extends ClientTerminal | ||||
|             tboTexture = 0; | ||||
|         } | ||||
| 
 | ||||
|         if( tboUniform != 0 ) | ||||
|         { | ||||
|             RenderSystem.glDeleteBuffers( tboUniform ); | ||||
|             tboUniform = 0; | ||||
|         } | ||||
| 
 | ||||
|         if( buffer != null ) | ||||
|         { | ||||
|             buffer.close(); | ||||
|   | ||||
| @@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor; | ||||
| 
 | ||||
| import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.client.render.TileEntityMonitorRenderer; | ||||
| import net.minecraftforge.fml.ModList; | ||||
| import org.lwjgl.opengl.GL; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| @@ -47,39 +48,24 @@ public enum MonitorRenderer | ||||
|     public static MonitorRenderer current() | ||||
|     { | ||||
|         MonitorRenderer current = ComputerCraft.monitorRenderer; | ||||
|         switch( current ) | ||||
|         { | ||||
|             case BEST: | ||||
|                 return best(); | ||||
|             case TBO: | ||||
|                 checkCapabilities(); | ||||
|                 if( !textureBuffer ) | ||||
|                 { | ||||
|                     ComputerCraft.log.warn( "Texture buffers are not supported on your graphics card. Falling back to default." ); | ||||
|                     ComputerCraft.monitorRenderer = BEST; | ||||
|                     return best(); | ||||
|                 } | ||||
| 
 | ||||
|                 return TBO; | ||||
|             default: | ||||
|         if( current == BEST ) current = ComputerCraft.monitorRenderer = best(); | ||||
|         return current; | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     private static MonitorRenderer best() | ||||
|     { | ||||
|         checkCapabilities(); | ||||
|         return textureBuffer ? TBO : VBO; | ||||
|     } | ||||
| 
 | ||||
|     private static boolean initialised = false; | ||||
|     private static boolean textureBuffer = false; | ||||
| 
 | ||||
|     private static void checkCapabilities() | ||||
|         if( !GL.getCapabilities().OpenGL31 ) | ||||
|         { | ||||
|         if( initialised ) return; | ||||
|             ComputerCraft.log.warn( "Texture buffers are not supported on your graphics card. Falling back to VBO monitor renderer." ); | ||||
|             return VBO; | ||||
|         } | ||||
| 
 | ||||
|         textureBuffer = GL.getCapabilities().OpenGL31; | ||||
|         initialised = true; | ||||
|         if( ModList.get().isLoaded( "optifine" ) ) | ||||
|         { | ||||
|             ComputerCraft.log.warn( "Optifine is loaded, assuming shaders are being used. Falling back to VBO monitor renderer." ); | ||||
|             return VBO; | ||||
|         } | ||||
| 
 | ||||
|         return TBO; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -89,8 +89,7 @@ public class TileSpeaker extends TileGeneric | ||||
|         @Override | ||||
|         public Vec3 getPosition() | ||||
|         { | ||||
|             BlockPos pos = speaker.getBlockPos(); | ||||
|             return new Vec3( pos.getX(), pos.getY(), pos.getZ() ); | ||||
|             return Vec3.atCenterOf( speaker.getBlockPos() ); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|   | ||||
| @@ -66,6 +66,7 @@ import java.util.Optional; | ||||
|  * accessible by accessing the `"left"` or `"right"` peripheral. | ||||
|  * | ||||
|  * [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics" | ||||
|  * | ||||
|  * @cc.module turtle | ||||
|  * @cc.since 1.3 | ||||
|  */ | ||||
|   | ||||
| @@ -13,7 +13,6 @@ import dan200.computercraft.api.turtle.TurtleSide; | ||||
| import dan200.computercraft.api.turtle.TurtleUpgradeType; | ||||
| import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral; | ||||
| import net.minecraft.client.resources.model.ModelResourceLocation; | ||||
| import net.minecraft.core.BlockPos; | ||||
| import net.minecraft.resources.ResourceLocation; | ||||
| import net.minecraft.world.item.ItemStack; | ||||
| import net.minecraft.world.level.Level; | ||||
| @@ -47,8 +46,7 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade | ||||
|         @Override | ||||
|         public Vec3 getPosition() | ||||
|         { | ||||
|             BlockPos pos = turtle.getPosition(); | ||||
|             return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); | ||||
|             return Vec3.atCenterOf( turtle.getPosition() ); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|   | ||||
| @@ -5,9 +5,13 @@ | ||||
|  */ | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import net.minecraft.core.Direction; | ||||
| import net.minecraftforge.common.capabilities.Capability; | ||||
| import net.minecraftforge.common.capabilities.ICapabilityProvider; | ||||
| import net.minecraftforge.common.util.LazyOptional; | ||||
| import net.minecraftforge.common.util.NonNullConsumer; | ||||
| 
 | ||||
| import javax.annotation.Nonnull; | ||||
| import javax.annotation.Nullable; | ||||
| 
 | ||||
| public final class CapabilityUtil | ||||
| @@ -57,4 +61,21 @@ public final class CapabilityUtil | ||||
|     { | ||||
|         return !p.isPresent() ? null : p.orElseThrow( NullPointerException::new ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Find a capability, preferring the internal/null side but falling back to a given side if a mod doesn't support | ||||
|      * the internal one. | ||||
|      * | ||||
|      * @param provider   The capability provider to get the capability from. | ||||
|      * @param capability The capability to get. | ||||
|      * @param side       The side we'll fall back to. | ||||
|      * @param <T>        The type of the underlying capability. | ||||
|      * @return The extracted capability, if present. | ||||
|      */ | ||||
|     @Nonnull | ||||
|     public static <T> LazyOptional<T> getCapability( ICapabilityProvider provider, Capability<T> capability, Direction side ) | ||||
|     { | ||||
|         LazyOptional<T> cap = provider.getCapability( capability ); | ||||
|         return cap.isPresent() ? cap : provider.getCapability( capability, side ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|  */ | ||||
| package dan200.computercraft.shared.util; | ||||
| 
 | ||||
| import dan200.computercraft.client.gui.FixedWidthFontRenderer; | ||||
| import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||
| import net.minecraft.nbt.CompoundTag; | ||||
| import net.minecraft.network.FriendlyByteBuf; | ||||
| 
 | ||||
| @@ -142,7 +142,7 @@ public class Palette | ||||
| 
 | ||||
|         for( int i = 0; i < colours.length; i++ ) | ||||
|         { | ||||
|             var colours = decodeRGB8( rgb8[i] ); | ||||
|             double[] colours = decodeRGB8( rgb8[i] ); | ||||
|             setColour( i, colours[0], colours[1], colours[2] ); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -6,10 +6,16 @@ | ||||
| #define FONT_HEIGHT 9.0 | ||||
|  | ||||
| uniform sampler2D Sampler0; // Font | ||||
| uniform int Width; | ||||
| uniform int Height; | ||||
| uniform usamplerBuffer Tbo; | ||||
| uniform vec3 Palette[16]; | ||||
|  | ||||
| layout(std140) uniform MonitorData { | ||||
|     vec3 Palette[16]; | ||||
|     int Width; | ||||
|     int Height; | ||||
|     ivec2 CursorPos; | ||||
|     int CursorColour; | ||||
| }; | ||||
| uniform int CursorBlink; | ||||
|  | ||||
| uniform vec4 ColorModulator; | ||||
| uniform float FogStart; | ||||
| @@ -27,6 +33,10 @@ vec2 texture_corner(int index) { | ||||
|     return vec2(x, y); | ||||
| } | ||||
|  | ||||
| vec4 recolour(vec4 texture, int colour) { | ||||
|     return vec4(texture.rgb * Palette[colour], texture.rgba); | ||||
| } | ||||
|  | ||||
| void main() { | ||||
|     vec2 term_pos = vec2(fontPos.x / FONT_WIDTH, fontPos.y / FONT_HEIGHT); | ||||
|     vec2 corner = floor(term_pos); | ||||
| @@ -43,8 +53,14 @@ void main() { | ||||
|     int bg = int(texelFetch(Tbo, index + 2).r); | ||||
|  | ||||
|     vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT); | ||||
|     vec4 img = texture(Sampler0, (texture_corner(character) + pos) / 256.0); | ||||
|     vec4 colour = vec4(mix(Palette[bg], img.rgb * Palette[fg], img.a * mult), 1.0) * ColorModulator; | ||||
|     vec4 charTex = recolour(texture(Sampler0, (texture_corner(character) + pos) / 256.0), fg); | ||||
|  | ||||
|     // Applies the cursor on top of the current character if we're blinking and in the current cursor's cell. We do it | ||||
|     // this funky way to avoid branches. | ||||
|     vec4 cursorTex = recolour(texture(Sampler0, (texture_corner(95) + pos) / 256.0), CursorColour); // 95 = '_' | ||||
|     vec4 img = mix(charTex, cursorTex, cursorTex.a * float(CursorBlink) * (CursorPos == cell ? 1.0 : 0.0)); | ||||
|  | ||||
|     vec4 colour = vec4(mix(Palette[bg], img.rgb, img.a * mult), 1.0) * ColorModulator; | ||||
|  | ||||
|     fragColor = linear_fog(colour, vertexDistance, FogStart, FogEnd, FogColor); | ||||
| } | ||||
|   | ||||
| @@ -13,8 +13,7 @@ | ||||
|         { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }, | ||||
|         { "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] }, | ||||
|  | ||||
|         { "name": "Width", "type": "int", "count": 1, "values": [ 1 ] }, | ||||
|         { "name": "Height", "type": "int", "count": 1, "values": [ 1 ] }, | ||||
|         { "name": "Tbo", "type": "int", "count": 1, "values": [ 3 ] } | ||||
|         { "name": "Tbo", "type": "int", "count": 1, "values": [ 3 ] }, | ||||
|         { "name": "CursorBlink", "type": "int", "count": 1, "values": [ 0 ] } | ||||
|     ] | ||||
| } | ||||
|   | ||||
| @@ -23,8 +23,6 @@ table.pretty-table th { | ||||
|  | ||||
| pre.highlight.highlight-lua { | ||||
|     position: relative; | ||||
|     background: var(--background-2); | ||||
|     padding: 2px; | ||||
| } | ||||
|  | ||||
| .example-run { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates