1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 04:00:30 +00:00

Merge branch 'feature/font-rendering-optimise' into mc-1.16.x

This /significantly/ improves performance of the VBO renderer (3fps to
80fps with 120 constantly changing monitors) and offers some minor FPS
improvements to the TBO renderer.

This also makes the 1.16 rendering code a little more consistent with
the 1.18 code, cleaning it up a little in the process.

Closes #1065 - this is a backport of those changes for 1.16. I will
merge these changes into 1.18, as with everything else (oh boy, that'll
be fun).

Please note this is only tested on my machine right now - any help
testing on other CPU/GPU configurations is much appreciated.
This commit is contained in:
Jonathan Coates 2022-04-26 21:43:53 +01:00
commit fccca22d3f
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
25 changed files with 1127 additions and 660 deletions

View File

@ -1,349 +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.systems.RenderSystem;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.client.FrameInfo;
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.client.Minecraft;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.TransformationMatrix;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class FixedWidthFontRenderer
{
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
private 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;
public static final float WIDTH = 256.0f;
public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
public static final RenderType TYPE = Type.MAIN;
private FixedWidthFontRenderer()
{
}
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( Matrix4f transform, IVertexBuilder buffer, float x, float y, int index, float r, float g, float b )
{
// 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);
buffer.vertex( transform, x, y, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, yStart / WIDTH ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
}
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, float r, float g, float b )
{
buffer.vertex( transform, x, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.vertex( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_END ).endVertex();
}
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
{
double[] colour = palette.getColour( getColour( colourIndex, Colour.BLACK ) );
float r, g, b;
if( greyscale )
{
r = g = b = toGreyscale( colour );
}
else
{
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
drawQuad( transform, buffer, x, y, width, height, r, g, b );
}
private static void drawBackground(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y,
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height
)
{
if( leftMarginSize > 0 )
{
drawQuad( transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
}
if( rightMarginSize > 0 )
{
drawQuad( transform, renderer, 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( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
}
}
public static void drawString(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y,
@Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize
)
{
if( backgroundColour != null )
{
drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT );
}
for( int i = 0; i < text.length(); i++ )
{
double[] colour = palette.getColour( getColour( textColour.charAt( i ), Colour.BLACK ) );
float r, g, b;
if( greyscale )
{
r = g = b = toGreyscale( colour );
}
else
{
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
// Draw char
int index = text.charAt( i );
if( index > 255 ) index = '?';
drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b );
}
}
public static void drawString(
float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize
)
{
bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
drawString( IDENTITY, ((IRenderTypeBuffer) renderer).getBuffer( TYPE ), x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize );
renderer.endBatch();
}
public static void drawTerminalWithoutCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder 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(
transform, buffer, x, y - topMarginSize,
terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize
);
drawBackground(
transform, 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++ )
{
drawString(
transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ),
palette, greyscale, leftMarginSize, rightMarginSize
);
}
}
public static void drawCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, 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() )
{
double[] colour = palette.getColour( 15 - terminal.getTextColour() );
float r, g, b;
if( greyscale )
{
r = g = b = toGreyscale( colour );
}
else
{
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b );
}
}
public static void drawTerminal(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( transform, buffer, x, y, terminal, greyscale );
}
public static void drawTerminal(
@Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
IVertexBuilder buffer = renderer.getBuffer( TYPE );
drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
renderer.endBatch( TYPE );
}
public static void drawTerminal(
float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
drawTerminal( IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( TYPE ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height )
{
bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
drawEmptyTerminal( transform, renderer, x, y, width, height );
renderer.endBatch();
}
public static void drawEmptyTerminal( float x, float y, float width, float height )
{
drawEmptyTerminal( IDENTITY, x, y, width, height );
}
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( Type.BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
}
private static void bindFont()
{
Minecraft.getInstance().getTextureManager().bind( FONT );
RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
}
private static final class Type extends RenderState
{
private static final int GL_MODE = GL11.GL_TRIANGLES;
private static final VertexFormat FORMAT = DefaultVertexFormats.POSITION_COLOR_TEX;
static final RenderType MAIN = RenderType.create(
"terminal_font", FORMAT, GL_MODE, 1024,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setTextureState( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
.setAlphaState( DEFAULT_ALPHA )
.setLightmapState( NO_LIGHTMAP )
.setWriteMaskState( COLOR_WRITE )
.createCompositeState( false )
);
static final RenderType BLOCKER = RenderType.create(
"terminal_blocker", FORMAT, GL_MODE, 256,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setTextureState( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
.setAlphaState( DEFAULT_ALPHA )
.setWriteMaskState( DEPTH_WRITE )
.setLightmapState( NO_LIGHTMAP )
.createCompositeState( false )
);
private Type( String name, Runnable setup, Runnable destroy )
{
super( name, setup, destroy );
}
}
}

View File

@ -6,7 +6,6 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
@ -19,6 +18,7 @@ import net.minecraft.util.text.ITextComponent;
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>
{
@ -73,9 +73,10 @@ public final class GuiComputer<T extends ContainerComputerBase> extends Computer
public void renderBg( @Nonnull MatrixStack stack, float partialTicks, int mouseX, int mouseY )
{
// Draw a border around the terminal
RenderSystem.color4f( 1, 1, 1, 1 );
minecraft.getTextureManager().bind( ComputerBorderRenderer.getTexture( family ) );
ComputerBorderRenderer.render( terminal.x, terminal.y, getBlitOffset(), terminal.getWidth(), terminal.getHeight() );
ComputerBorderRenderer.render(
ComputerBorderRenderer.getTexture( family ), terminal.x, terminal.y, getBlitOffset(),
FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
);
ComputerSidebar.renderBackground( stack, leftPos, topPos + sidebarYOffset );
}
}

View File

@ -10,9 +10,9 @@ import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.common.ContainerHeldItem;
import dan200.computercraft.shared.media.items.ItemPrintout;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.text.ITextComponent;
@ -21,6 +21,7 @@ import org.lwjgl.glfw.GLFW;
import javax.annotation.Nonnull;
import static dan200.computercraft.client.render.PrintoutRenderer.*;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
{
@ -97,10 +98,10 @@ public class GuiPrintout extends ContainerScreen<ContainerHeldItem>
RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f );
RenderSystem.enableDepthTest();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
IRenderTypeBuffer.Impl renderer = IRenderTypeBuffer.immediate( Tessellator.getInstance().getBuilder() );
Matrix4f matrix = transform.last().pose();
drawBorder( matrix, renderer, leftPos, topPos, getBlitOffset(), page, pages, book );
drawText( matrix, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, text, colours );
drawBorder( matrix, renderer, leftPos, topPos, getBlitOffset(), page, pages, book, FULL_BRIGHT_LIGHTMAP );
drawText( matrix, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours );
renderer.endBatch();
}

View File

@ -6,22 +6,28 @@
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.matrix.MatrixStack;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.IVertexBuilder;
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.client.Minecraft;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.SharedConstants;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.text.StringTextComponent;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
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 Widget
{
@ -314,14 +320,23 @@ public class WidgetTerminal extends Widget
if( !visible ) return;
Matrix4f matrix = transform.last().pose();
Terminal terminal = computer.getTerminal();
Minecraft.getInstance().getTextureManager().bind( FixedWidthFontRenderer.FONT );
RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
IRenderTypeBuffer.Impl renderer = IRenderTypeBuffer.immediate( Tessellator.getInstance().getBuilder() );
IVertexBuilder buffer = renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
if( terminal != null )
{
FixedWidthFontRenderer.drawTerminal( matrix, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
FixedWidthFontRenderer.drawTerminal( matrix, buffer, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, x, y, width, height );
FixedWidthFontRenderer.drawEmptyTerminal( matrix, buffer, x, y, width, height );
}
renderer.endBatch();
}
public static int getWidth( int termWidth )

View File

@ -5,16 +5,14 @@
*/
package dan200.computercraft.client.render;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
@ -59,18 +57,19 @@ public class ComputerBorderRenderer
private final IVertexBuilder builder;
private final int z;
private final float r, g, b;
private final int light;
public ComputerBorderRenderer( Matrix4f transform, IVertexBuilder builder, int z, float r, float g, float b )
public ComputerBorderRenderer( Matrix4f transform, IVertexBuilder builder, int z, int light, float r, float g, float b )
{
this.transform = transform;
this.builder = builder;
this.z = z;
this.light = light;
this.r = r;
this.g = g;
this.b = b;
}
@Nonnull
public static ResourceLocation getTexture( @Nonnull ComputerFamily family )
{
@ -86,31 +85,22 @@ public class ComputerBorderRenderer
}
}
public static void render( int x, int y, int z, int width, int height )
public static RenderType getRenderType( ResourceLocation location )
{
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuilder();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX );
render( IDENTITY, buffer, x, y, z, width, height );
RenderSystem.enableAlphaTest();
tessellator.end();
// See note in RenderTypes about why we use text rather than anything intuitive.
return RenderType.text( location );
}
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height )
public static void render( ResourceLocation location, int x, int y, int z, int light, int width, int height )
{
render( transform, buffer, x, y, z, width, height, 1, 1, 1 );
IRenderTypeBuffer.Impl source = IRenderTypeBuffer.immediate( Tessellator.getInstance().getBuilder() );
render( IDENTITY, source.getBuffer( getRenderType( location ) ), x, y, z, light, width, height, false, 1, 1, 1 );
source.endBatch();
}
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height, float r, float g, float b )
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int light, int width, int height, boolean withLight, float r, float g, float b )
{
render( transform, buffer, x, y, z, width, height, false, r, g, b );
}
public static void render( Matrix4f transform, IVertexBuilder buffer, int x, int y, int z, int width, int height, boolean withLight, float r, float g, float b )
{
new ComputerBorderRenderer( transform, buffer, z, r, g, b ).doRender( x, y, width, height, withLight );
new ComputerBorderRenderer( transform, buffer, z, light, r, g, b ).doRender( x, y, width, height, withLight );
}
public void doRender( int x, int y, int width, int height, boolean withLight )
@ -160,9 +150,9 @@ public class ComputerBorderRenderer
private void renderTexture( int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight )
{
builder.vertex( transform, x, y + height, z ).color( r, g, b, 1.0f ).uv( u * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).endVertex();
builder.vertex( transform, x + width, y + height, z ).color( r, g, b, 1.0f ).uv( (u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).endVertex();
builder.vertex( transform, x + width, y, z ).color( r, g, b, 1.0f ).uv( (u + textureWidth) * TEX_SCALE, v * TEX_SCALE ).endVertex();
builder.vertex( transform, x, y, z ).color( r, g, b, 1.0f ).uv( u * TEX_SCALE, v * TEX_SCALE ).endVertex();
builder.vertex( transform, x, y + height, z ).color( r, g, b, 1.0f ).uv( u * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).uv2( light ).endVertex();
builder.vertex( transform, x + width, y + height, z ).color( r, g, b, 1.0f ).uv( (u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).uv2( light ).endVertex();
builder.vertex( transform, x + width, y, z ).color( r, g, b, 1.0f ).uv( (u + textureWidth) * TEX_SCALE, v * TEX_SCALE ).uv2( light ).endVertex();
builder.vertex( transform, x, y, z ).color( r, g, b, 1.0f ).uv( u * TEX_SCALE, v * TEX_SCALE ).uv2( light ).endVertex();
}
}

View File

@ -25,9 +25,10 @@ public abstract class ItemMapLikeRenderer
* @param transform The matrix transformation stack
* @param render The buffer to render to
* @param stack The stack to render
* @param light The packed lightmap coordinates.
* @see FirstPersonRenderer#renderItemInFirstPerson(AbstractClientPlayerEntity, float, float, Hand, float, ItemStack, float, MatrixStack, IRenderTypeBuffer, int)
*/
protected abstract void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack );
protected abstract void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack, int light );
protected void renderItemFirstPerson( MatrixStack transform, IRenderTypeBuffer render, int lightTexture, Hand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack )
{
@ -89,7 +90,7 @@ public abstract class ItemMapLikeRenderer
transform.mulPose( Vector3f.XP.rotationDegrees( f2 * -45f ) );
transform.mulPose( Vector3f.YP.rotationDegrees( offset * f2 * -30f ) );
renderItem( transform, render, stack );
renderItem( transform, render, stack, combinedLight );
transform.popPose();
}
@ -134,6 +135,6 @@ public abstract class ItemMapLikeRenderer
transform.mulPose( Vector3f.XP.rotationDegrees( rX * 20.0F ) );
transform.scale( 2.0F, 2.0F, 2.0F );
renderItem( transform, render, stack );
renderItem( transform, render, stack, combinedLight );
}
}

View File

@ -6,31 +6,27 @@
package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.IVertexBuilder;
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;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.util.Colour;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.lwjgl.opengl.GL11;
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.
@ -58,7 +54,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
}
@Override
protected void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack )
protected void renderItem( MatrixStack transform, IRenderTypeBuffer bufferSource, ItemStack stack, int light )
{
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal();
@ -95,61 +91,58 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
int frameColour = item.getColour( stack );
Matrix4f matrix = transform.last().pose();
renderFrame( matrix, family, frameColour, width, height );
renderFrame( matrix, bufferSource, family, frameColour, light, width, height );
// Render the light
int lightColour = ItemPocketComputer.getLightState( stack );
if( lightColour == -1 ) lightColour = Colour.BLACK.getHex();
renderLight( matrix, lightColour, width, height );
renderLight( matrix, bufferSource, lightColour, width, height );
if( computer != null && terminal != null )
{
FixedWidthFontRenderer.drawTerminal( matrix, MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
FixedWidthFontRenderer.drawTerminal(
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ),
MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN
);
FixedWidthFontRenderer.drawBlocker(
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ),
0, 0, width, height
);
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, 0, 0, width, height );
FixedWidthFontRenderer.drawEmptyTerminal(
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ),
0, 0, width, height
);
}
transform.popPose();
}
private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height )
private static void renderFrame( Matrix4f transform, IRenderTypeBuffer bufferSource, ComputerFamily family, int colour, int light, int width, int height )
{
RenderSystem.enableBlend();
Minecraft.getInstance().getTextureManager()
.bind( colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ) );
ResourceLocation texture = colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family );
float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f;
float b = (colour & 0xFF) / 255.0f;
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuilder();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR_TEX );
ComputerBorderRenderer.render( transform, buffer, 0, 0, 0, width, height, true, r, g, b );
tessellator.end();
ComputerBorderRenderer.render( transform, bufferSource.getBuffer( ComputerBorderRenderer.getRenderType( texture ) ), 0, 0, 0, light, width, height, true, r, g, b );
}
private static void renderLight( Matrix4f transform, int colour, int width, int height )
private static void renderLight( Matrix4f transform, IRenderTypeBuffer bufferSource, int colour, int width, int height )
{
RenderSystem.disableTexture();
byte r = (byte) ((colour >>> 16) & 0xFF);
byte g = (byte) ((colour >>> 8) & 0xFF);
byte b = (byte) (colour & 0xFF);
byte[] c = new byte[] { r, g, b, (byte) 255 };
float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f;
float b = (colour & 0xFF) / 255.0f;
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuilder();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width, height + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0 ).color( r, g, b, 1.0f ).endVertex();
tessellator.end();
RenderSystem.enableTexture();
IVertexBuilder buffer = bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
FixedWidthFontRenderer.drawQuad(
transform, buffer,
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
c, RenderTypes.FULL_BRIGHT_LIGHTMAP
);
}
}

View File

@ -18,9 +18,9 @@ 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.PrintoutRenderer.*;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE;
import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH;
@ -50,13 +50,13 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
}
@Override
protected void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack )
protected void renderItem( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack, int light )
{
transform.mulPose( Vector3f.XP.rotationDegrees( 180f ) );
transform.scale( 0.42f, 0.42f, -0.42f );
transform.translate( -0.5f, -0.48f, 0.0f );
drawPrintout( transform, render, stack );
drawPrintout( transform, render, stack, light );
}
@SubscribeEvent
@ -74,10 +74,10 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
transform.scale( 0.95f, 0.95f, -0.95f );
transform.translate( -0.5f, -0.5f, 0.0f );
drawPrintout( transform, event.getBuffers(), stack );
drawPrintout( transform, event.getBuffers(), stack, event.getLight() );
}
private static void drawPrintout( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack )
private static void drawPrintout( MatrixStack transform, IRenderTypeBuffer render, ItemStack stack, int light )
{
int pages = ItemPrintout.getPageCount( stack );
boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK;
@ -105,9 +105,10 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer
transform.translate( (max - width) / 2.0, (max - height) / 2.0, 0.0 );
Matrix4f matrix = transform.last().pose();
drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book );
drawText( matrix, render,
X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack )
drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book, light );
drawText(
matrix, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light,
ItemPrintout.getText( stack ), ItemPrintout.getColours( stack )
);
}
}

View File

@ -9,62 +9,55 @@ import com.google.common.base.Strings;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
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 dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour;
class MonitorTextureBufferShader
{
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 FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer( 16 );
private static final FloatBuffer PALETTE_BUFFER = BufferUtils.createFloatBuffer( 16 * 3 );
private static int uniformMv;
private static int uniformFont;
private static int uniformWidth;
private static int uniformHeight;
private static int uniformTbo;
private static int uniformPalette;
private static int uniformMonitor;
private static int uniformCursorBlink;
private static boolean initialised;
private static boolean ok;
private static int program;
static void setupUniform( Matrix4f transform, int width, int height, Palette palette, boolean greyscale )
static void setupUniform( Matrix4f transform, int tboUniform )
{
MATRIX_BUFFER.rewind();
transform.store( MATRIX_BUFFER );
MATRIX_BUFFER.rewind();
RenderSystem.glUniformMatrix4( uniformMv, false, MATRIX_BUFFER );
RenderSystem.glUniform1i( uniformWidth, width );
RenderSystem.glUniform1i( uniformHeight, height );
GL31.glBindBufferBase( GL31.GL_UNIFORM_BUFFER, uniformMonitor, tboUniform );
PALETTE_BUFFER.rewind();
for( int i = 0; i < 16; i++ )
{
double[] colour = palette.getColour( i );
if( greyscale )
{
float f = FixedWidthFontRenderer.toGreyscale( colour );
PALETTE_BUFFER.put( f ).put( f ).put( f );
}
else
{
PALETTE_BUFFER.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] );
}
}
PALETTE_BUFFER.flip();
RenderSystem.glUniform3( uniformPalette, PALETTE_BUFFER );
int cursorAlpha = FrameInfo.getGlobalCursorBlink() ? 1 : 0;
RenderSystem.glUniform1i( uniformCursorBlink, cursorAlpha );
}
static boolean use()
@ -116,10 +109,10 @@ class MonitorTextureBufferShader
uniformMv = getUniformLocation( program, "u_mv" );
uniformFont = getUniformLocation( program, "u_font" );
uniformWidth = getUniformLocation( program, "u_width" );
uniformHeight = getUniformLocation( program, "u_height" );
uniformTbo = getUniformLocation( program, "u_tbo" );
uniformPalette = getUniformLocation( program, "u_palette" );
uniformMonitor = GL31.glGetUniformBlockIndex( program, "u_monitor" );
if( uniformMonitor == -1 ) throw new IllegalStateException( "Could not find uniformMonitor uniform." );
uniformCursorBlink = getUniformLocation( program, "u_cursorBlink" );
ComputerCraft.log.info( "Loaded monitor shader." );
return true;
@ -159,4 +152,54 @@ class MonitorTextureBufferShader
if( uniform == -1 ) throw new IllegalStateException( "Cannot find uniform " + name );
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;
Palette 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 );
}
}

View File

@ -6,18 +6,14 @@
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.IVertexBuilder;
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.IRenderTypeBuffer;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.opengl.GL11;
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
@ -60,37 +56,37 @@ public final class PrintoutRenderer
private PrintoutRenderer() {}
public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, TextBuffer[] text, TextBuffer[] colours )
public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours )
{
IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
IVertexBuilder buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
FixedWidthFontRenderer.drawString( transform, buffer,
x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT,
false, 0, 0
x, y + line * FONT_HEIGHT, text[start + line], colours[start + line],
Palette.DEFAULT, false, light
);
}
}
public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, String[] text, String[] colours )
public static void drawText( Matrix4f transform, IRenderTypeBuffer renderer, int x, int y, int start, int light, String[] text, String[] colours )
{
IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
IVertexBuilder buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
FixedWidthFontRenderer.drawString( transform, buffer,
x, y + line * FONT_HEIGHT,
new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ),
null, Palette.DEFAULT, false, 0, 0
Palette.DEFAULT, false, light
);
}
}
public static void drawBorder( Matrix4f transform, IRenderTypeBuffer renderer, float x, float y, float z, int page, int pages, boolean isBook )
public static void drawBorder( Matrix4f transform, IRenderTypeBuffer renderer, float x, float y, float z, int page, int pages, boolean isBook, int light )
{
int leftPages = page;
int rightPages = pages - page - 1;
IVertexBuilder buffer = renderer.getBuffer( Type.TYPE );
IVertexBuilder buffer = renderer.getBuffer( RenderTypes.PRINTOUT_BACKGROUND );
if( isBook )
{
@ -100,86 +96,73 @@ public final class PrintoutRenderer
float right = x + X_SIZE + offset - 4;
// Left and right border
drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 );
drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light );
drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light );
// Draw centre panel (just stretched texture, sorry).
drawTexture( transform, buffer,
x - offset, y, z - 0.02f, X_SIZE + offset * 2, Y_SIZE,
COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE
COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE, light
);
float borderX = left;
while( borderX < right )
{
double thisWidth = Math.min( right - borderX, X_SIZE );
drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE );
drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE );
drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE, light );
drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE, light );
borderX += thisWidth;
}
}
// Left half
drawTexture( transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE );
drawTexture( transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE, light );
for( int n = 0; n <= leftPages; n++ )
{
drawTexture( transform, buffer,
x - offsetAt( n ), y, z - 1e-3f * n,
// Use the left "bold" fold for the outermost page
n == leftPages ? 0 : X_FOLD_SIZE, 0,
X_FOLD_SIZE, Y_SIZE
X_FOLD_SIZE, Y_SIZE, light
);
}
// Right half
drawTexture( transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE );
drawTexture( transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE, light );
for( int n = 0; n <= rightPages; n++ )
{
drawTexture( transform, buffer,
x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3f * n,
// Two folds, then the main page. Use the right "bold" fold for the outermost page.
X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0,
X_FOLD_SIZE, Y_SIZE
X_FOLD_SIZE, Y_SIZE, light
);
}
}
private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float u, float v, float width, float height )
private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float u, float v, float width, float height, int light )
{
buffer.vertex( matrix, x, y + height, z ).uv( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
buffer.vertex( matrix, x + width, y + height, z ).uv( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex();
buffer.vertex( matrix, x + width, y, z ).uv( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex();
buffer.vertex( matrix, x, y, z ).uv( u / BG_SIZE, v / BG_SIZE ).endVertex();
vertex( buffer, matrix, x, y + height, z, u / BG_SIZE, (v + height) / BG_SIZE, light );
vertex( buffer, matrix, x + width, y + height, z, (u + width) / BG_SIZE, (v + height) / BG_SIZE, light );
vertex( buffer, matrix, x + width, y, z, (u + width) / BG_SIZE, v / BG_SIZE, light );
vertex( buffer, matrix, x, y, z, u / BG_SIZE, v / BG_SIZE, light );
}
private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float width, float height, float u, float v, float tWidth, float tHeight )
private static void drawTexture( Matrix4f matrix, IVertexBuilder buffer, float x, float y, float z, float width, float height, float u, float v, float tWidth, float tHeight, int light )
{
buffer.vertex( matrix, x, y + height, z ).uv( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
buffer.vertex( matrix, x + width, y + height, z ).uv( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex();
buffer.vertex( matrix, x + width, y, z ).uv( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex();
buffer.vertex( matrix, x, y, z ).uv( u / BG_SIZE, v / BG_SIZE ).endVertex();
vertex( buffer, matrix, x, y + height, z, u / BG_SIZE, (v + tHeight) / BG_SIZE, light );
vertex( buffer, matrix, x + width, y + height, z, (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE, light );
vertex( buffer, matrix, x + width, y, z, (u + tWidth) / BG_SIZE, v / BG_SIZE, light );
vertex( buffer, matrix, x, y, z, u / BG_SIZE, v / BG_SIZE, light );
}
private static void vertex( IVertexBuilder buffer, Matrix4f matrix, float x, float y, float z, float u, float v, int light )
{
buffer.vertex( matrix, x, y, z ).color( 255, 255, 255, 255 ).uv( u, v ).uv2( light ).endVertex();
}
public static float offsetAt( int page )
{
return (float) (32 * (1 - Math.pow( 1.2, -page )));
}
private static final class Type extends RenderState
{
static final RenderType TYPE = RenderType.create(
"printout_background", DefaultVertexFormats.POSITION_TEX, GL11.GL_QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setTextureState( new RenderState.TextureState( BG, false, false ) ) // blur, minimap
.setAlphaState( DEFAULT_ALPHA )
.setLightmapState( NO_LIGHTMAP )
.createCompositeState( false )
);
private Type( String name, Runnable setup, Runnable destroy )
{
super( name, setup, destroy );
}
}
}

View File

@ -0,0 +1,91 @@
/*
* 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;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
public class RenderTypes
{
public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20);
/**
* Renders a fullbright terminal without writing to the depth layer. This is used in combination with
* {@link #TERMINAL_BLOCKER} to ensure we can render a terminal without z-fighting.
*/
public static final RenderType TERMINAL_WITHOUT_DEPTH = Types.TERMINAL_WITHOUT_DEPTH;
/**
* A transparent texture which only writes to the depth layer.
*/
public static final RenderType TERMINAL_BLOCKER = Types.TERMINAL_BLOCKER;
/**
* Renders a fullbright terminal which also writes to the depth layer. This is used when z-fighting isn't an issue -
* for instance rendering an empty terminal or inside a GUI.
*
* This is identical to <em>vanilla's</em> {@link RenderType#text}. Forge overrides one with a definition which sets
* sortOnUpload to true, which is entirely broken!
*/
public static final RenderType TERMINAL_WITH_DEPTH = Types.TERMINAL_WITH_DEPTH;
/**
* A variant of {@link #TERMINAL_WITH_DEPTH} which uses the lightmap rather than rendering fullbright.
*/
public static final RenderType PRINTOUT_TEXT = RenderType.text( FixedWidthFontRenderer.FONT );
/**
* Printout's background texture. {@link RenderType#text(ResourceLocation)} is a <em>little</em> questionable, but
* it is what maps use, so should behave the same as vanilla in both item frames and in-hand.
*/
public static final RenderType PRINTOUT_BACKGROUND = RenderType.text( new ResourceLocation( "computercraft", "textures/gui/printout.png" ) );
private static final class Types extends RenderState
{
private static final RenderState.TextureState TERM_FONT_TEXTURE = new RenderState.TextureState(
FixedWidthFontRenderer.FONT,
false, false // blur, minimap
);
private static final VertexFormat TERM_FORMAT = DefaultVertexFormats.POSITION_COLOR_TEX;
static final RenderType TERMINAL_WITHOUT_DEPTH = RenderType.create(
"terminal_without_depth", TERM_FORMAT, GL11.GL_QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setAlphaState( DEFAULT_ALPHA )
.setWriteMaskState( COLOR_WRITE )
.createCompositeState( false )
);
static final RenderType TERMINAL_BLOCKER = RenderType.create(
"terminal_blocker", DefaultVertexFormats.POSITION, GL11.GL_QUADS, 256,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setWriteMaskState( DEPTH_WRITE )
.createCompositeState( false )
);
static final RenderType TERMINAL_WITH_DEPTH = RenderType.create(
"terminal_with_depth", TERM_FORMAT, GL11.GL_QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setAlphaState( DEFAULT_ALPHA )
.createCompositeState( false )
);
private Types( String name, Runnable setup, Runnable destroy )
{
super( name, setup, destroy );
}
}
}

View File

@ -7,25 +7,26 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.IVertexBuilder;
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.client.util.DirectVertexBuffer;
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.*;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraft.util.math.vector.Vector3f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
@ -35,7 +36,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 extends TileEntityRenderer<TileMonitor>
{
@ -44,9 +46,7 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
* the monitor frame and contents.
*/
private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1);
private static ByteBuffer tboContents;
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
private static ByteBuffer backingBuffer;
public TileEntityMonitorRenderer( TileEntityRendererDispatcher rendererDispatcher )
{
@ -114,46 +114,31 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
transform.scale( (float) xScale, (float) -yScale, 1.0f );
Matrix4f matrix = transform.last().pose();
renderTerminal( renderer, matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
// Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
// render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
// for now.
IVertexBuilder buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE );
FixedWidthFontRenderer.TYPE.setupRenderState();
renderTerminal( matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
// We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is
// reasonable.
FixedWidthFontRenderer.drawCursor( matrix, buffer, 0, 0, terminal, !originTerminal.isColour() );
// Force a flush of the buffer. WorldRenderer.updateCameraAndRender will "finish" all the built-in buffers
// before calling renderer.finish, which means the blocker isn't actually rendered at that point!
renderer.getBuffer( RenderType.solid() );
transform.popPose();
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal(
transform.last().pose(), renderer,
transform.last().pose(), renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ),
-MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
);
}
FixedWidthFontRenderer.drawBlocker(
transform.last().pose(), renderer,
-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!
renderer.getBuffer( RenderType.solid() );
transform.popPose();
}
private static void renderTerminal( Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
private static void renderTerminal( IRenderTypeBuffer 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();
@ -165,42 +150,29 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
{
if( !MonitorTextureBufferShader.use() ) return;
int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
if( redraw )
{
int size = width * height * 3;
if( tboContents == null || tboContents.capacity() < size )
{
tboContents = GLAllocation.createByteBuffer( size );
}
ByteBuffer terminalBuffer = getBuffer( width * height * 3 );
MonitorTextureBufferShader.setTerminalData( terminalBuffer, terminal );
DirectBuffers.setBufferData( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer, terminalBuffer, GL20.GL_STATIC_DRAW );
ByteBuffer monitorBuffer = tboContents;
monitorBuffer.clear();
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();
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 );
ByteBuffer uniformBuffer = getBuffer( MonitorTextureBufferShader.UNIFORM_SIZE );
MonitorTextureBufferShader.setUniformData( uniformBuffer, terminal, !monitor.isColour() );
DirectBuffers.setBufferData( GL31.GL_UNIFORM_BUFFER, monitor.tboUniform, uniformBuffer, GL20.GL_STATIC_DRAW );
}
// Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
// render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
// for now.
bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
RenderTypes.TERMINAL_WITH_DEPTH.setupRenderState();
// Nobody knows what they're doing!
GlStateManager._activeTexture( MonitorTextureBufferShader.TEXTURE_INDEX );
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, monitor.tboTexture );
GlStateManager._activeTexture( GL13.GL_TEXTURE0 );
MonitorTextureBufferShader.setupUniform( matrix, width, height, terminal.getPalette(), !monitor.isColour() );
MonitorTextureBufferShader.setupUniform( matrix, monitor.tboUniform );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuilder();
@ -217,28 +189,59 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
case VBO:
{
VertexBuffer vbo = monitor.buffer;
DirectVertexBuffer vbo = monitor.buffer;
if( redraw )
{
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder builder = tessellator.getBuilder();
builder.begin( FixedWidthFontRenderer.TYPE.mode(), FixedWidthFontRenderer.TYPE.format() );
FixedWidthFontRenderer.drawTerminalWithoutCursor(
IDENTITY, builder, 0, 0,
terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin
);
int vertexSize = RenderTypes.TERMINAL_WITHOUT_DEPTH.format().getVertexSize();
ByteBuffer buffer = getBuffer( DirectFixedWidthFontRenderer.getVertexCount( terminal ) * vertexSize );
builder.end();
vbo.upload( builder );
// 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( termIndexes, RenderTypes.TERMINAL_WITHOUT_DEPTH.format(), buffer );
}
vbo.bind();
FixedWidthFontRenderer.TYPE.format().setupBufferState( 0L );
vbo.draw( matrix, FixedWidthFontRenderer.TYPE.mode() );
VertexBuffer.unbind();
FixedWidthFontRenderer.TYPE.format().clearBufferState();
// As with the TBO backend we use getBuffer to flush existing buffers. This time we use TERMINAL_WITHOUT_DEPTH
// instead and render a separate depth blocker.
bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState();
vbo.draw(
matrix,
// As mentioned in the uploading block, 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(
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ),
-xMargin, -yMargin, pixelWidth + xMargin, pixelHeight + yMargin
);
break;
}
}
}
@Nonnull
private static ByteBuffer getBuffer( int capacity )
{
ByteBuffer buffer = backingBuffer;
if( buffer == null || buffer.capacity() < capacity )
{
buffer = backingBuffer = buffer == null ? DirectBuffers.createByteBuffer( capacity ) : DirectBuffers.resizeByteBuffer( buffer, capacity );
}
buffer.clear();
return buffer;
}
}

View File

@ -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.vertex.IVertexBuilder;
import dan200.computercraft.client.util.DirectBuffers;
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.client.renderer.vertex.DefaultVertexFormats;
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 IVertexBuilder}. 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 DefaultVertexFormats#POSITION_COLOR_TEX}.</li>
* <li>The buffer <strong>MUST</strong> be allocated with {@link DirectBuffers}, 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.
}
}

View File

@ -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.IVertexBuilder;
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.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
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( Matrix4f transform, IVertexBuilder buffer, 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(
transform, buffer, 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( Matrix4f transform, IVertexBuilder buffer, float x, float y, float z, float width, float height, byte[] colour, int light )
{
quad( transform, buffer, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light );
}
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, 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( transform, buffer, x, y, 0, width, height, colour, light );
}
private static void drawBackground(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height, int light
)
{
if( leftMarginSize > 0 )
{
drawQuad( transform, buffer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light );
}
if( rightMarginSize > 0 )
{
drawQuad( transform, buffer, 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( transform, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( transform, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light );
}
}
public static void drawString(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, 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( transform, renderer, x + i * FONT_WIDTH, y, index, colour, light );
}
}
public static void drawTerminalWithoutCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder 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(
transform, buffer, x, y - topMarginSize,
terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize, FULL_BRIGHT_LIGHTMAP
);
drawBackground(
transform, buffer, 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 + FONT_HEIGHT * i;
drawBackground(
transform, buffer, x, rowY, terminal.getBackgroundColourLine( i ),
palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT, FULL_BRIGHT_LIGHTMAP
);
drawString(
transform, buffer, 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 Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale
)
{
if( isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() )
{
byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale );
drawChar( transform, buffer, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, FULL_BRIGHT_LIGHTMAP );
}
}
public static void drawTerminal(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( transform, buffer, x, y, terminal, greyscale );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y, float width, float height )
{
drawQuad( transform, renderer, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y, float width, float height )
{
drawQuad( transform, buffer, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
private static void quad( Matrix4f matrix, IVertexBuilder buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light )
{
byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
buffer.vertex( matrix, x1, y1, z ).color( r, g, b, a ).uv( u1, v1 ).uv2( light ).endVertex();
buffer.vertex( matrix, x1, y2, z ).color( r, g, b, a ).uv( u1, v2 ).uv2( light ).endVertex();
buffer.vertex( matrix, x2, y2, z ).color( r, g, b, a ).uv( u2, v2 ).uv2( light ).endVertex();
buffer.vertex( matrix, x2, y1, z ).color( r, g, b, a ).uv( u2, v1 ).uv2( light ).endVertex();
}
}

View File

@ -0,0 +1,83 @@
/*
* 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 org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL15C;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryUtil;
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
{
GLCapabilities 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
{
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
{
GlStateManager._glBindBuffer( type, id );
GL45C.glBufferData( type, 0, GL15C.GL_STATIC_DRAW );
GlStateManager._glBindBuffer( type, 0 );
}
}
private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator( false );
public static ByteBuffer createByteBuffer( int size )
{
long i = ALLOCATOR.malloc( size );
if( i == 0L ) throw new OutOfMemoryError( "Failed to allocate " + size + " bytes" );
return MemoryUtil.memByteBuffer( i, size );
}
public static ByteBuffer resizeByteBuffer( ByteBuffer buffer, int size )
{
long i = ALLOCATOR.realloc( MemoryUtil.memAddress0( buffer ), size );
if( i == 0L )
{
throw new OutOfMemoryError( "Failed to resize buffer from " + buffer.capacity() + " bytes to " + size + " bytes" );
}
return MemoryUtil.memByteBuffer( i, size );
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.systems.RenderSystem;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
/**
* A version of {@link VertexBuffer} which allows uploading {@link ByteBuffer}s directly.
*/
public class DirectVertexBuffer implements AutoCloseable
{
private int vertextBufferId;
private int indexCount;
private VertexFormat format;
public DirectVertexBuffer()
{
vertextBufferId = DirectBuffers.createBuffer();
}
public void upload( int vertexCount, VertexFormat format, ByteBuffer buffer )
{
RenderSystem.assertThread( RenderSystem::isOnGameThread );
DirectBuffers.setBufferData( GL15.GL_ARRAY_BUFFER, vertextBufferId, buffer, GL15.GL_STATIC_DRAW );
this.format = format;
indexCount = vertexCount;
}
public void draw( Matrix4f matrix, int indexCount )
{
bind();
format.setupBufferState( 0 );
RenderSystem.pushMatrix();
RenderSystem.loadIdentity();
RenderSystem.multMatrix( matrix );
RenderSystem.drawArrays( GL11.GL_QUADS, 0, indexCount );
RenderSystem.popMatrix();
unbind();
}
public int getIndexCount()
{
return indexCount;
}
@Override
public void close()
{
if( vertextBufferId >= 0 )
{
RenderSystem.glDeleteBuffers( vertextBufferId );
vertextBufferId = -1;
}
}
private void bind()
{
RenderSystem.glBindBuffer( GL15.GL_ARRAY_BUFFER, () -> vertextBufferId );
}
private static void unbind()
{
RenderSystem.glBindBuffer( GL15.GL_ARRAY_BUFFER, () -> 0 );
}
}

View File

@ -14,8 +14,8 @@ import dan200.computercraft.core.computer.TimeoutState;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.core.tracking.TrackingField;
import dan200.computercraft.shared.util.ThreadUtils;
import org.squiddev.cobalt.*;
import org.squiddev.cobalt.LuaTable;
import org.squiddev.cobalt.*;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LoadState;
import org.squiddev.cobalt.debug.DebugFrame;

View File

@ -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 );
}

View File

@ -54,7 +54,7 @@ public abstract class BlockComputerBase<T extends TileComputerBase> extends Bloc
super.onPlace( state, world, pos, oldState, isMoving );
TileEntity tile = world.getBlockEntity( pos );
if( tile instanceof TileComputerBase ) ((TileComputerBase) tile).updateInputsImmediately( );
if( tile instanceof TileComputerBase ) ((TileComputerBase) tile).updateInputsImmediately();
}
@Override

View File

@ -7,9 +7,9 @@ package dan200.computercraft.shared.peripheral.monitor;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.util.DirectBuffers;
import dan200.computercraft.client.util.DirectVertexBuffer;
import dan200.computercraft.shared.common.ClientTerminal;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@ -33,7 +33,8 @@ public final class ClientMonitor extends ClientTerminal
public int tboBuffer;
public int tboTexture;
public VertexBuffer buffer;
public int tboUniform;
public DirectVertexBuffer buffer;
public ClientMonitor( boolean colour, TileMonitor origin )
{
@ -64,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;
@ -82,7 +83,7 @@ public final class ClientMonitor extends ClientTerminal
if( buffer != null ) return false;
deleteBuffers();
buffer = new VertexBuffer( FixedWidthFontRenderer.TYPE.format() );
buffer = new DirectVertexBuffer();
addMonitor();
return true;
@ -114,6 +115,12 @@ public final class ClientMonitor extends ClientTerminal
tboTexture = 0;
}
if( tboUniform != 0 )
{
RenderSystem.glDeleteBuffers( tboUniform );
tboUniform = 0;
}
if( buffer != null )
{
buffer.close();

View File

@ -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;
}
}

View File

@ -70,6 +70,11 @@ public class TileMonitor extends TileGeneric
private int xIndex = 0;
private int yIndex = 0;
private BlockPos bbPos;
private BlockState bbState;
private int bbX, bbY, bbWidth, bbHeight;
private AxisAlignedBB boundingBox;
public TileMonitor( TileEntityType<? extends TileMonitor> type, boolean advanced )
{
super( type );
@ -624,9 +629,25 @@ public class TileMonitor extends TileGeneric
@Override
public AxisAlignedBB getRenderBoundingBox()
{
// We attempt to cache the bounding box to save having to do property lookups (and allocations!) on every frame.
// Unfortunately the AABB does depend on quite a lot of state, so we need to add a bunch of extra fields -
// ideally these'd be a single object, but I don't think worth doing until Java has value types.
if( boundingBox != null && getBlockState().equals( bbState ) && getBlockPos().equals( bbPos ) &&
xIndex == bbX && yIndex == bbY && width == bbWidth && height == bbHeight )
{
return boundingBox;
}
bbState = getBlockState();
bbPos = getBlockPos();
bbX = xIndex;
bbY = yIndex;
bbWidth = width;
bbHeight = height;
BlockPos startPos = toWorldPos( 0, 0 );
BlockPos endPos = toWorldPos( width, height );
return new AxisAlignedBB(
return boundingBox = new AxisAlignedBB(
Math.min( startPos.getX(), endPos.getX() ),
Math.min( startPos.getY(), endPos.getY() ),
Math.min( startPos.getZ(), endPos.getZ() ),

View File

@ -69,6 +69,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
*/

View File

@ -5,13 +5,18 @@
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.PacketBuffer;
import javax.annotation.Nonnull;
public class Palette
{
private static final int PALETTE_SIZE = 16;
private final double[][] colours = new double[PALETTE_SIZE][3];
private final byte[][] byteColours = new byte[PALETTE_SIZE][4];
private final byte[][] greyByteColours = new byte[PALETTE_SIZE][4];
public static final Palette DEFAULT = new Palette();
@ -19,16 +24,23 @@ public class Palette
{
// Get the default palette
resetColours();
for( int i = 0; i < PALETTE_SIZE; i++ ) byteColours[i][3] = greyByteColours[i][3] = (byte) 255;
}
public void setColour( int i, double r, double g, double b )
{
if( i >= 0 && i < colours.length )
{
colours[i][0] = r;
colours[i][1] = g;
colours[i][2] = b;
}
if( i < 0 || i >= colours.length ) return;
colours[i][0] = r;
colours[i][1] = g;
colours[i][2] = b;
byteColours[i][0] = (byte) (int) (r * 255);
byteColours[i][1] = (byte) (int) (g * 255);
byteColours[i][2] = (byte) (int) (b * 255);
byte grey = (byte) (int) ((r + g + b) / 3 * 255);
greyByteColours[i][0] = greyByteColours[i][1] = greyByteColours[i][2] = grey;
}
public void setColour( int i, Colour colour )
@ -38,19 +50,29 @@ public class Palette
public double[] getColour( int i )
{
if( i >= 0 && i < colours.length )
{
return colours[i];
}
return null;
return i >= 0 && i < colours.length ? colours[i] : null;
}
/**
* Get the colour as a set of bytes rather than floats. This is called frequently by {@link FixedWidthFontRenderer},
* as our vertex format uses bytes.
*
* This allows us to do the conversion once (when setting the colour) rather than for every vertex, at the cost of
* some memory overhead.
*
* @param i The colour index.
* @param greyscale Whether this number should be converted to greyscale.
* @return The number as a tuple of bytes.
*/
@Nonnull
public byte[] getByteColour( int i, boolean greyscale )
{
return greyscale ? greyByteColours[i] : byteColours[i];
}
public void resetColour( int i )
{
if( i >= 0 && i < colours.length )
{
setColour( i, Colour.VALUES[i] );
}
if( i >= 0 && i < colours.length ) setColour( i, Colour.VALUES[i] );
}
public void resetColours()
@ -89,9 +111,12 @@ public class Palette
public void read( PacketBuffer buffer )
{
for( double[] colour : colours )
for( int i = 0; i < PALETTE_SIZE; i++ )
{
for( int i = 0; i < colour.length; i++ ) colour[i] = (buffer.readByte() & 0xFF) / 255.0;
double r = (buffer.readByte() & 0xFF) / 255.0;
double g = (buffer.readByte() & 0xFF) / 255.0;
double b = (buffer.readByte() & 0xFF) / 255.0;
setColour( i, r, g, b );
}
}
@ -117,7 +142,8 @@ public class Palette
for( int i = 0; i < colours.length; i++ )
{
colours[i] = decodeRGB8( rgb8[i] );
double[] colours = decodeRGB8( rgb8[i] );
setColour( i, colours[0], colours[1], colours[2] );
}
}
}

View File

@ -4,10 +4,16 @@
#define FONT_HEIGHT 9.0
uniform sampler2D u_font;
uniform int u_width;
uniform int u_height;
uniform usamplerBuffer u_tbo;
uniform vec3 u_palette[16];
layout(std140) uniform u_monitor {
vec3 u_palette[16];
int u_width;
int u_height;
ivec2 u_cursorPos;
int u_cursorColour;
};
uniform int u_cursorBlink;
in vec2 f_pos;
@ -19,6 +25,10 @@ vec2 texture_corner(int index) {
return vec2(x, y);
}
vec4 recolour(vec4 texture, int colour) {
return vec4(texture.rgb * u_palette[colour], texture.rgba);
}
void main() {
vec2 term_pos = vec2(f_pos.x / FONT_WIDTH, f_pos.y / FONT_HEIGHT);
vec2 corner = floor(term_pos);
@ -35,6 +45,12 @@ void main() {
int bg = int(texelFetch(u_tbo, index + 2).r);
vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT);
vec4 img = texture(u_font, (texture_corner(character) + pos) / 256.0);
colour = vec4(mix(u_palette[bg], img.rgb * u_palette[fg], img.a * mult), 1.0);
vec4 charTex = recolour(texture(u_font, (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(u_font, (texture_corner(95) + pos) / 256.0), u_cursorColour); // 95 = '_'
vec4 img = mix(charTex, cursorTex, cursorTex.a * float(u_cursorBlink) * (u_cursorPos == cell ? 1.0 : 0.0));
colour = vec4(mix(u_palette[bg], img.rgb, img.a * mult), 1.0);
}