mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-02-15 02:20:05 +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:
commit
59e3608d2a
@ -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:
|
||||
return current;
|
||||
}
|
||||
if( current == BEST ) current = ComputerCraft.monitorRenderer = best();
|
||||
return current;
|
||||
}
|
||||
|
||||
private static MonitorRenderer best()
|
||||
{
|
||||
checkCapabilities();
|
||||
return textureBuffer ? TBO : VBO;
|
||||
}
|
||||
if( !GL.getCapabilities().OpenGL31 )
|
||||
{
|
||||
ComputerCraft.log.warn( "Texture buffers are not supported on your graphics card. Falling back to VBO monitor renderer." );
|
||||
return VBO;
|
||||
}
|
||||
|
||||
private static boolean initialised = false;
|
||||
private static boolean textureBuffer = false;
|
||||
if( ModList.get().isLoaded( "optifine" ) )
|
||||
{
|
||||
ComputerCraft.log.warn( "Optifine is loaded, assuming shaders are being used. Falling back to VBO monitor renderer." );
|
||||
return VBO;
|
||||
}
|
||||
|
||||
private static void checkCapabilities()
|
||||
{
|
||||
if( initialised ) return;
|
||||
|
||||
textureBuffer = GL.getCapabilities().OpenGL31;
|
||||
initialised = true;
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user