1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-13 19:50:31 +00:00

Cleanup and optimise terminal rendering (#1057)

- Remove the POSITION_COLOR render type. Instead we just render a
   background terminal quad as the pocket computer light - it's a little
   (lot?) more cheaty, but saves having to create a render type.

 - Use the existing position_color_tex shader instead of our copy. I
   looked at using RenderType.text, but had a bunch of problems with GUI
   terminals. Its possible we can fix it, but didn't want to spend too
   much time on it.

 - Remove some methods from FixedWidthFontRenderer, inlining them into
   the call site.

 - Switch back to using GL_QUADS rather than GL_TRIANGLES. I know Lig
   will shout at me for this, but the rest of MC uses QUADS, so I don't
   think best practice really matters here.

 - Fix the TBO backend monitor not rendering monitors with fog.
 
   Unfortunately we can't easily do this to the VBO one without writing
   a custom shader (which defeats the whole point of the VBO backend!),
   as the distance calculation of most render types expect an
   already-transformed position (camera-relative I think!) while we pass
   a world-relative one.

 - When rendering to a VBO we push vertices to a ByteBuffer directly,
   rather than going through MC's VertexConsumer system. This removes
   the overhead which comes with VertexConsumer, significantly improving
   performance.

 - Pre-convert palette colours to bytes, storing both the coloured and
   greyscale versions as a byte array. This allows us to remove the
   multiple casts and conversions (double -> float -> (greyscale) ->
   byte), offering noticeable performance improvements (multiple ms per
   frame).

   We're using a byte[] here rather than a record of three bytes as
   notionally it provides better performance when writing to a
   ByteBuffer directly compared to calling .put() four times. [^1]

 - Memorize getRenderBoundingBox. This was taking about 5% of the total
   time on the render thread[^2], so worth doing.

   I don't actually think the allocation is the heavy thing here -
   VisualVM says it's toWorldPos being slow. I'm not sure why - possibly
   just all the block property lookups? [^2]

Note that none of these changes improve compatibility with Optifine.
Right now there's some serious issues where monitors are writing _over_
blocks in front of them. To fix this, we probably need to remove the
depth blocker and just render characters with a z offset. Will do that
in a separate commit, as I need to evaluate how well that change will
work first.

The main advantage of this commit is the improved performance. In my 
stress test with 120 monitors updating every tick, I'm getting 10-20fps
[^3] (still much worse than TBOs, which manages a solid 60-100).

In practice, we'll actually be much better than this. Our network
bandwidth limits means only 40 change in a single tick - and so FPS is
much more reasonable (+60fps).

[^1]: In general, put(byte[]) is faster than put(byte) multiple times.
Just not clear if this is true when dealing with a small (and loop
unrolled) number of bytes.

[^2]: To be clear, this is with 120 monitors and no other block entities
with custom renderers. so not really representative.

[^3]: I wish I could provide a narrower range, but it varies so much
between me restarting the game. Makes it impossible to benchmark
anything!
This commit is contained in:
Jonathan Coates 2022-04-02 10:54:03 +01:00 committed by GitHub
parent ba7598c689
commit 41fa95bce4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 412 additions and 246 deletions

View File

@ -5,7 +5,7 @@
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import dan200.computercraft.client.FrameInfo;
@ -14,11 +14,11 @@ 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.MultiBufferSource;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
@ -44,15 +44,22 @@ public final class FixedWidthFontRenderer
public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6;
public static final float WIDTH = 256.0f;
private 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;
private static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
private static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
private static final byte[] BLACK = new byte[] { byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), (byte) 255 };
private FixedWidthFontRenderer()
{
}
private static byte byteColour( float c )
{
return (byte) (int) (c * 255);
}
public static float toGreyscale( double[] rgb )
{
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
@ -63,7 +70,7 @@ public final class FixedWidthFontRenderer
return 15 - Terminal.getColour( c, def );
}
private static void drawChar( Matrix4f transform, VertexConsumer buffer, float x, float y, int index, float r, float g, float b, int light )
private static void drawChar( VertexEmitter emitter, float x, float y, int index, byte[] colour, int light )
{
// Short circuit to avoid the common case - the texture should be blank here after all.
if( index == '\0' || index == ' ' ) return;
@ -74,56 +81,40 @@ public final class FixedWidthFontRenderer
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 ).uv2( light ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).uv2( light ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).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 ).uv2( light ).endVertex();
emitter.vertex( x, y, (float) 0, colour, xStart / WIDTH, yStart / WIDTH, light );
emitter.vertex( x, y + FONT_HEIGHT, (float) 0, colour, xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light );
emitter.vertex( x + FONT_WIDTH, y + FONT_HEIGHT, (float) 0, colour, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light );
emitter.vertex( x + FONT_WIDTH, y, (float) 0, colour, (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH, light );
}
private static void drawQuad( Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, float r, float g, float b )
public static void drawQuad( VertexEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light )
{
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();
emitter.vertex( x, y, z, colour, BACKGROUND_START, BACKGROUND_START, light );
emitter.vertex( x, y + height, z, colour, BACKGROUND_START, BACKGROUND_END, light );
emitter.vertex( x + width, y + height, z, colour, BACKGROUND_END, BACKGROUND_END, light );
emitter.vertex( x + width, y, z, colour, BACKGROUND_END, BACKGROUND_START, light );
}
private static void drawQuad( Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
private static void drawQuad( VertexEmitter emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex, int light )
{
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 );
var colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale );
drawQuad( emitter, x, y, 0, width, height, colour, light );
}
private static void drawBackground(
@Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y,
@Nonnull VertexEmitter emitter, float x, float y,
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height
float leftMarginSize, float rightMarginSize, float height, int light
)
{
if( leftMarginSize > 0 )
{
drawQuad( transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
drawQuad( emitter, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light );
}
if( rightMarginSize > 0 )
{
drawQuad( transform, renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) );
drawQuad( emitter, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ), light );
}
// Batch together runs of identical background cells.
@ -136,7 +127,7 @@ public final class FixedWidthFontRenderer
if( blockColour != '\0' )
{
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light );
}
blockColour = colourIndex;
@ -145,46 +136,35 @@ public final class FixedWidthFontRenderer
if( blockColour != '\0' )
{
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
drawQuad( emitter, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light );
}
}
public static void drawString(
@Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y,
@Nonnull VertexEmitter emitter, float x, float y,
@Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize, int light
)
{
if( backgroundColour != null )
{
drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT );
drawBackground( emitter, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT, light );
}
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];
}
var colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale );
// Draw char
int index = text.charAt( i );
if( index > 255 ) index = '?';
drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b, light );
drawChar( emitter, x + i * FONT_WIDTH, y, index, colour, light );
}
}
public static void drawTerminalWithoutCursor(
@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y,
@Nonnull VertexEmitter emitter, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
@ -194,32 +174,29 @@ public final class FixedWidthFontRenderer
// Top and bottom margins
drawBackground(
transform, buffer, x, y - topMarginSize,
emitter, x, y - topMarginSize,
terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize
leftMarginSize, rightMarginSize, topMarginSize, FULL_BRIGHT_LIGHTMAP
);
drawBackground(
transform, buffer, x, y + height * FONT_HEIGHT,
emitter, x, y + height * FONT_HEIGHT,
terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
leftMarginSize, rightMarginSize, bottomMarginSize
leftMarginSize, rightMarginSize, bottomMarginSize, FULL_BRIGHT_LIGHTMAP
);
// The main text
for( int i = 0; i < height; i++ )
{
drawString(
transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
emitter, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ),
palette, greyscale, leftMarginSize, rightMarginSize, FULL_BRIGHT_LIGHTMAP
);
}
}
public static void drawCursor(
@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale
)
public static void drawCursor( @Nonnull VertexEmitter emitter, float x, float y, @Nonnull Terminal terminal, boolean greyscale )
{
Palette palette = terminal.getPalette();
int width = terminal.getWidth();
@ -229,60 +206,100 @@ public final class FixedWidthFontRenderer
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, FULL_BRIGHT_LIGHTMAP );
var colour = palette.getByteColour( 15 - terminal.getTextColour(), greyscale );
drawChar( emitter, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', colour, FULL_BRIGHT_LIGHTMAP );
}
}
public static void drawTerminal(
@Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y,
@Nonnull VertexEmitter 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 );
drawTerminalWithoutCursor( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( 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
)
public static void drawEmptyTerminal( @Nonnull VertexEmitter emitter, float x, float y, float width, float height )
{
MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
VertexConsumer buffer = renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
renderer.endBatch();
drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull MultiBufferSource renderer, float x, float y, float width, float height )
public static void drawBlocker( @Nonnull VertexEmitter emitter, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
drawQuad( emitter, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height )
public static int getVertexCount( Terminal terminal )
{
MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
drawEmptyTerminal( transform, renderer, x, y, width, height );
renderer.endBatch();
int height = terminal.getHeight();
int count = 0;
for( int y = 0; y < height; y++ )
{
// We compress runs of adjacent characters, so we need to do that calculation here too :/.
int background = 2;
TextBuffer backgroundColour = terminal.getBackgroundColourLine( y );
char blockColour = '\0';
for( int x = 0; x < backgroundColour.length(); x++ )
{
char colourIndex = backgroundColour.charAt( x );
if( colourIndex == blockColour ) continue;
if( blockColour != '\0' ) background++;
blockColour = colourIndex;
}
if( blockColour != '\0' ) background++;
count += background;
if( y == 0 ) count += background;
if( y == height - 1 ) count += background;
// Thankfully the normal characters are much easier!
TextBuffer foreground = terminal.getLine( y );
for( int x = 0; x < foreground.length(); x++ )
{
char c = foreground.charAt( x );
if( c != '\0' && c != ' ' ) count++;
}
}
return count * 4;
}
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull MultiBufferSource renderer, float x, float y, float width, float height )
/**
* Emit a single vertex to some buffer.
*
* @see #toVertexConsumer(Matrix4f, VertexConsumer) Emits to a {@link VertexConsumer}.
* @see #toByteBuffer(ByteBuffer) Emits to a {@link ByteBuffer}.
*/
@FunctionalInterface
public interface VertexEmitter
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( RenderTypes.TERMINAL_BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
void vertex( float x, float y, float z, byte[] rgba, float u, float v, int light );
}
public static VertexEmitter toVertexConsumer( Matrix4f matrix, VertexConsumer consumer )
{
return ( float x, float y, float z, byte[] rgba, float u, float v, int light ) ->
consumer.vertex( matrix, x, y, z ).color( rgba[0], rgba[1], rgba[2], rgba[3] ).uv( u, v ).uv2( light ).endVertex();
}
/**
* An optimised vertex emitter which bypasses {@link VertexConsumer}. This allows us to emit vertices very quickly,
* when using the VBO renderer with some limitations:
* <ul>
* <li>No transformation matrix (not needed for VBOs).</li>
* <li>Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX_LIGHTMAP}.</li>
* </ul>
*
* @param buffer The buffer to emit to. This must have space for at least {@link #getVertexCount(Terminal)} vertices.
* @return The emitter, ot be passed to the rendering functions.
*/
public static VertexEmitter toByteBuffer( ByteBuffer buffer )
{
return ( float x, float y, float z, byte[] rgba, float u, float v, int light ) -> buffer
.putFloat( x ).putFloat( y ).putFloat( z ).put( rgba ).putFloat( u ).putFloat( v );
}
}

View File

@ -6,14 +6,17 @@
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.math.Matrix4f;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.TextComponent;
import org.lwjgl.glfw.GLFW;
@ -315,14 +318,24 @@ public class WidgetTerminal extends AbstractWidget
if( !visible ) return;
Matrix4f matrix = transform.last().pose();
Terminal terminal = computer.getTerminal();
var bufferSource = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() );
var emitter = FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) );
if( terminal != null )
{
FixedWidthFontRenderer.drawTerminal( matrix, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN );
boolean greyscale = !computer.isColour();
FixedWidthFontRenderer.drawTerminal(
emitter,
(float) innerX, (float) innerY, terminal, greyscale, (float) MARGIN, (float) MARGIN, (float) MARGIN, (float) MARGIN
);
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, x, y, width, height );
FixedWidthFontRenderer.drawEmptyTerminal( emitter, (float) x, (float) y, (float) width, (float) height );
}
bufferSource.endBatch();
}
@Override

View File

@ -54,7 +54,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
}
@Override
protected void renderItem( PoseStack transform, MultiBufferSource renderer, ItemStack stack, int light )
protected void renderItem( PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light )
{
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal();
@ -91,24 +91,30 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
int frameColour = item.getColour( stack );
Matrix4f matrix = transform.last().pose();
renderFrame( matrix, renderer, family, frameColour, light, 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, renderer, lightColour, width, height );
renderLight( matrix, bufferSource, lightColour, width, height );
if( computer != null && terminal != null )
{
FixedWidthFontRenderer.drawTerminal(
matrix, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ),
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ) ),
MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN
);
FixedWidthFontRenderer.drawBlocker( transform.last().pose(), renderer, 0, 0, width, height );
FixedWidthFontRenderer.drawBlocker(
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ) ),
0, 0, width, height
);
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal( matrix, renderer, 0, 0, width, height );
FixedWidthFontRenderer.drawEmptyTerminal(
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) ),
0, 0, width, height
);
}
transform.popPose();
@ -127,15 +133,16 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
private static void renderLight( Matrix4f transform, MultiBufferSource render, int colour, int width, int height )
{
float r = ((colour >>> 16) & 0xFF) / 255.0f;
float g = ((colour >>> 8) & 0xFF) / 255.0f;
float b = (colour & 0xFF) / 255.0f;
float z = 0.001f;
byte r = (byte) ((colour >>> 16) & 0xFF);
byte g = (byte) ((colour >>> 8) & 0xFF);
byte b = (byte) (colour & 0xFF);
var c = new byte[] { r, g, b, (byte) 255 };
VertexConsumer buffer = render.getBuffer( RenderTypes.POSITION_COLOR );
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex();
VertexConsumer buffer = render.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
FixedWidthFontRenderer.drawQuad(
FixedWidthFontRenderer.toVertexConsumer( transform, buffer ),
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
c, RenderTypes.FULL_BRIGHT_LIGHTMAP
);
}
}

View File

@ -54,12 +54,13 @@ public final class PrintoutRenderer
private PrintoutRenderer() {}
public static void drawText( Matrix4f transform, MultiBufferSource renderer, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours )
public static void drawText( Matrix4f transform, MultiBufferSource bufferSource, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours )
{
VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT );
var buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_TEXT );
var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
FixedWidthFontRenderer.drawString( transform, buffer,
FixedWidthFontRenderer.drawString( emitter,
x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT,
false, 0, 0,
light
@ -67,12 +68,13 @@ public final class PrintoutRenderer
}
}
public static void drawText( Matrix4f transform, MultiBufferSource renderer, int x, int y, int start, int light, String[] text, String[] colours )
public static void drawText( Matrix4f transform, MultiBufferSource bufferSource, int x, int y, int start, int light, String[] text, String[] colours )
{
VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT );
var buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_TEXT );
var emitter = FixedWidthFontRenderer.toVertexConsumer( transform, buffer );
for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ )
{
FixedWidthFontRenderer.drawString( transform, buffer,
FixedWidthFontRenderer.drawString( emitter,
x, y + line * FONT_HEIGHT,
new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ),
null, Palette.DEFAULT, false, 0, 0,
@ -81,12 +83,12 @@ public final class PrintoutRenderer
}
}
public static void drawBorder( Matrix4f transform, MultiBufferSource renderer, float x, float y, float z, int page, int pages, boolean isBook, int light )
public static void drawBorder( Matrix4f transform, MultiBufferSource bufferSource, float x, float y, float z, int page, int pages, boolean isBook, int light )
{
int leftPages = page;
int rightPages = pages - page - 1;
VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_BACKGROUND );
VertexConsumer buffer = bufferSource.getBuffer( RenderTypes.PRINTOUT_BACKGROUND );
if( isBook )
{

View File

@ -9,6 +9,7 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance;
@ -27,24 +28,45 @@ public class RenderTypes
public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20);
private static MonitorTextureBufferShader monitorTboShader;
private static ShaderInstance terminalShader;
public static final RenderType TERMINAL_WITHOUT_DEPTH = Types.TERMINAL_WITHOUT_DEPTH;
public static final RenderType TERMINAL_BLOCKER = Types.TERMINAL_BLOCKER;
public static final RenderType TERMINAL_WITH_DEPTH = Types.TERMINAL_WITH_DEPTH;
public static final RenderType MONITOR_TBO = Types.MONITOR_TBO;
public static final RenderType PRINTOUT_TEXT = Types.PRINTOUT_TEXT;
/**
* This looks wrong (it should be POSITION_COLOR_TEX_LIGHTMAP surely!) but the fragment/vertex shader for that
* appear to entirely ignore the lightmap.
* 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.
*
* Note that vanilla maps do the same, so this isn't unreasonable.
* 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;
/**
* Renders a monitor with the TBO shader.
*
* @see MonitorTextureBufferShader
*/
public static final RenderType MONITOR_TBO = Types.MONITOR_TBO;
/**
* 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" ) );
public static final RenderType POSITION_COLOR = Types.POSITION_COLOR;
@Nonnull
static MonitorTextureBufferShader getMonitorTextureBufferShader()
{
@ -55,8 +77,7 @@ public class RenderTypes
@Nonnull
static ShaderInstance getTerminalShader()
{
if( terminalShader == null ) throw new NullPointerException( "MonitorTboShader has not been registered" );
return terminalShader;
return GameRenderer.getPositionColorTexShader();
}
@SubscribeEvent
@ -70,15 +91,6 @@ public class RenderTypes
),
x -> monitorTboShader = (MonitorTextureBufferShader) x
);
event.registerShader(
new ShaderInstance(
event.getResourceManager(),
new ResourceLocation( ComputerCraft.MOD_ID, "terminal" ),
TERMINAL_WITHOUT_DEPTH.format()
),
x -> terminalShader = x
);
}
private static final class Types extends RenderStateShard
@ -88,7 +100,6 @@ public class RenderTypes
false, false // blur, minimap
);
private static final VertexFormat TERM_FORMAT = DefaultVertexFormat.POSITION_COLOR_TEX;
private static final VertexFormat.Mode TERM_MODE = VertexFormat.Mode.TRIANGLES;
private static final ShaderStateShard TERM_SHADER = new ShaderStateShard( RenderTypes::getTerminalShader );
static final RenderType MONITOR_TBO = RenderType.create(
@ -102,52 +113,32 @@ public class RenderTypes
);
static final RenderType TERMINAL_WITHOUT_DEPTH = RenderType.create(
"terminal_without_depth", TERM_FORMAT, TERM_MODE, 1024,
"terminal_without_depth", TERM_FORMAT, VertexFormat.Mode.QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( TERM_SHADER )
.setLightmapState( LIGHTMAP )
.setWriteMaskState( COLOR_WRITE )
.createCompositeState( false )
);
static final RenderType TERMINAL_BLOCKER = RenderType.create(
"terminal_blocker", TERM_FORMAT, TERM_MODE, 256,
"terminal_blocker", DefaultVertexFormat.POSITION, VertexFormat.Mode.QUADS, 256,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( TERM_SHADER )
.setShaderState( POSITION_SHADER )
.setWriteMaskState( DEPTH_WRITE )
.createCompositeState( false )
);
static final RenderType TERMINAL_WITH_DEPTH = RenderType.create(
"terminal_with_depth", TERM_FORMAT, TERM_MODE, 1024,
"terminal_with_depth", TERM_FORMAT, VertexFormat.Mode.QUADS, 1024,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( TERM_SHADER )
.createCompositeState( false )
);
/**
* A variant of {@link #TERMINAL_WITH_DEPTH} which uses the lightmap rather than rendering fullbright.
*/
static final RenderType PRINTOUT_TEXT = RenderType.create(
"printout_text", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, TERM_MODE, 1024,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setTextureState( TERM_FONT_TEXTURE )
.setShaderState( RenderStateShard.RENDERTYPE_TEXT_SHADER )
.setLightmapState( RenderStateShard.LIGHTMAP )
.createCompositeState( false )
);
static final RenderType POSITION_COLOR = RenderType.create(
"position_color", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 128,
false, false, // useDelegate, needsSorting
RenderType.CompositeState.builder()
.setShaderState( POSITION_COLOR_SHADER )
.setLightmapState( LIGHTMAP )
.createCompositeState( false )
);

View File

@ -8,9 +8,9 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import com.mojang.math.Transformation;
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.FrameInfo;
@ -44,16 +44,14 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
* 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 = Transformation.identity().getMatrix();
private static ByteBuffer backingBuffer;
public TileEntityMonitorRenderer( BlockEntityRendererProvider.Context context )
{
}
@Override
public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource renderer, int lightmapCoord, int overlayLight )
public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource bufferSource, int lightmapCoord, int overlayLight )
{
// Render from the origin monitor
ClientMonitor originTerminal = monitor.getClientMonitor();
@ -114,31 +112,31 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
Matrix4f matrix = transform.last().pose();
renderTerminal( renderer, matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
renderTerminal( bufferSource, 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.
// We don't draw the cursor with the VBO/TBO, as it's dynamic and so we'll end up refreshing far more than
// is reasonable.
FixedWidthFontRenderer.drawCursor(
matrix, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ),
FixedWidthFontRenderer.toVertexConsumer( matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ) ),
0, 0, terminal, !originTerminal.isColour()
);
transform.popPose();
FixedWidthFontRenderer.drawBlocker(
transform.last().pose(), renderer,
FixedWidthFontRenderer.toVertexConsumer( transform.last().pose(), bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ) ),
-MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
);
// Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in
// buffers before calling renderer.finish, which means the blocker isn't actually rendered at that point!
renderer.getBuffer( RenderType.solid() );
bufferSource.getBuffer( RenderType.solid() );
}
else
{
FixedWidthFontRenderer.drawEmptyTerminal(
transform.last().pose(), renderer,
FixedWidthFontRenderer.toVertexConsumer( transform.last().pose(), bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ) ),
-MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
);
@ -147,7 +145,7 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
transform.popPose();
}
private static void renderTerminal( @Nonnull MultiBufferSource renderer, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
private static void renderTerminal( @Nonnull MultiBufferSource bufferSource, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
{
Terminal terminal = monitor.getTerminal();
@ -164,14 +162,7 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
if( redraw )
{
int size = width * height * 3;
if( tboContents == null || tboContents.capacity() < size )
{
tboContents = MemoryTracker.create( size );
}
ByteBuffer monitorBuffer = tboContents;
monitorBuffer.clear();
ByteBuffer monitorBuffer = getBuffer( width * height * 3 );
for( int y = 0; y < height; y++ )
{
TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y );
@ -198,7 +189,7 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
MonitorTextureBufferShader shader = RenderTypes.getMonitorTextureBufferShader();
shader.setupUniform( width, height, terminal.getPalette(), !monitor.isColour() );
VertexConsumer buffer = renderer.getBuffer( RenderTypes.MONITOR_TBO );
VertexConsumer buffer = bufferSource.getBuffer( RenderTypes.MONITOR_TBO );
tboVertex( buffer, matrix, -xMargin, -yMargin );
tboVertex( buffer, matrix, -xMargin, pixelHeight + yMargin );
tboVertex( buffer, matrix, pixelWidth + xMargin, -yMargin );
@ -206,28 +197,27 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
// And force things to flush. We strictly speaking do this later on anyway for the cursor, but nice to
// be consistent.
renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
break;
}
case VBO:
{
VertexBuffer vbo = monitor.buffer;
var vbo = monitor.buffer;
if( redraw )
{
Tesselator tessellator = Tesselator.getInstance();
BufferBuilder builder = tessellator.getBuilder();
builder.begin( RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format() );
int vertexCount = FixedWidthFontRenderer.getVertexCount( terminal );
ByteBuffer buffer = getBuffer( vertexCount * RenderTypes.TERMINAL_WITHOUT_DEPTH.format().getVertexSize() );
FixedWidthFontRenderer.drawTerminalWithoutCursor(
IDENTITY, builder, 0, 0,
FixedWidthFontRenderer.toByteBuffer( buffer ), 0, 0,
terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin
);
buffer.flip();
builder.end();
vbo.upload( builder );
vbo.upload( vertexCount, RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format(), buffer );
}
renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState();
vbo.drawWithShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader() );
break;
@ -241,6 +231,20 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
builder.vertex( matrix, x, y, 0 ).uv( x, y ).endVertex();
}
@Nonnull
private static ByteBuffer getBuffer( int capacity )
{
ByteBuffer buffer = backingBuffer;
if( buffer == null || buffer.capacity() < capacity )
{
buffer = backingBuffer = MemoryTracker.create( capacity );
}
buffer.clear();
return buffer;
}
@Override
public int getViewDistance()
{

View File

@ -0,0 +1,64 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.util;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL45C;
import java.nio.ByteBuffer;
/**
* A version of {@link VertexBuffer} which allows uploading {@link ByteBuffer}s directly.
*
* This should probably be its own class (rather than subclassing), but I need access to {@link VertexBuffer#drawWithShader}.
*/
public class DirectVertexBuffer extends VertexBuffer
{
private static final boolean HAS_DSA;
static
{
var capabilities = GL.getCapabilities();
HAS_DSA = capabilities.OpenGL45 || capabilities.GL_ARB_direct_state_access;
}
public DirectVertexBuffer()
{
if( HAS_DSA )
{
RenderSystem.glDeleteBuffers( vertextBufferId );
vertextBufferId = GL45C.glCreateBuffers();
}
}
public void upload( int vertexCount, VertexFormat.Mode mode, VertexFormat format, ByteBuffer buffer )
{
RenderSystem.assertOnRenderThread();
if( HAS_DSA )
{
GL45C.glNamedBufferData( vertextBufferId, buffer, GL15.GL_STATIC_DRAW );
}
else
{
BufferUploader.reset();
bind();
RenderSystem.glBufferData( GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW );
unbind();
}
this.format = format;
this.mode = mode;
indexCount = mode.indexCount( vertexCount );
indexType = VertexFormat.IndexType.SHORT;
sequentialIndices = true;
}
}

View File

@ -7,7 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexBuffer;
import dan200.computercraft.client.util.DirectVertexBuffer;
import dan200.computercraft.shared.common.ClientTerminal;
import net.minecraft.core.BlockPos;
import net.minecraftforge.api.distmarker.Dist;
@ -32,7 +32,7 @@ public final class ClientMonitor extends ClientTerminal
public int tboBuffer;
public int tboTexture;
public VertexBuffer buffer;
public DirectVertexBuffer buffer;
public ClientMonitor( boolean colour, TileMonitor origin )
{
@ -81,7 +81,7 @@ public final class ClientMonitor extends ClientTerminal
if( buffer != null ) return false;
deleteBuffers();
buffer = new VertexBuffer();
buffer = new DirectVertexBuffer();
addMonitor();
return true;

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 AABB boundingBox;
public TileMonitor( BlockEntityType<? extends TileMonitor> type, BlockPos pos, BlockState state, boolean advanced )
{
super( type, pos, state );
@ -620,9 +625,25 @@ public class TileMonitor extends TileGeneric
@Override
public AABB 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 AABB(
return boundingBox = new AABB(
Math.min( startPos.getX(), endPos.getX() ),
Math.min( startPos.getY(), endPos.getY() ),
Math.min( startPos.getZ(), endPos.getZ() ),

View File

@ -5,13 +5,18 @@
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
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( FriendlyByteBuf 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] );
var colours = decodeRGB8( rgb8[i] );
setColour( i, colours[0], colours[1], colours[2] );
}
}
}

View File

@ -11,3 +11,11 @@ public net.minecraft.client.Minecraft f_91080_ # screen
# SpeakerInstance/SpeakerManager
public com.mojang.blaze3d.audio.Channel m_83652_(I)V # pumpBuffers
public net.minecraft.client.sounds.SoundEngine f_120223_ # executor
# DirectVertexBuffer
protected com.mojang.blaze3d.vertex.VertexBuffer f_166859_ # vertextBufferId
protected com.mojang.blaze3d.vertex.VertexBuffer f_166861_ # indexType
protected com.mojang.blaze3d.vertex.VertexBuffer f_166863_ # indexCount
protected com.mojang.blaze3d.vertex.VertexBuffer f_166864_ # mode
protected com.mojang.blaze3d.vertex.VertexBuffer f_166865_ # sequentialIndices
protected com.mojang.blaze3d.vertex.VertexBuffer f_85917_ # format

View File

@ -1,5 +1,7 @@
#version 150
#moj_import <fog.glsl>
#define FONT_WIDTH 6.0
#define FONT_HEIGHT 9.0
@ -9,9 +11,15 @@ uniform int Height;
uniform usamplerBuffer Tbo;
uniform vec3 Palette[16];
in vec2 f_pos;
uniform vec4 ColorModulator;
uniform float FogStart;
uniform float FogEnd;
uniform vec4 FogColor;
out vec4 colour;
in float vertexDistance;
in vec2 fontPos;
out vec4 fragColor;
vec2 texture_corner(int index) {
float x = 1.0 + float(index % 16) * (FONT_WIDTH + 2.0);
@ -20,7 +28,7 @@ vec2 texture_corner(int index) {
}
void main() {
vec2 term_pos = vec2(f_pos.x / FONT_WIDTH, f_pos.y / FONT_HEIGHT);
vec2 term_pos = vec2(fontPos.x / FONT_WIDTH, fontPos.y / FONT_HEIGHT);
vec2 corner = floor(term_pos);
ivec2 cell = ivec2(corner);
@ -36,5 +44,7 @@ void main() {
vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT);
vec4 img = texture(Sampler0, (texture_corner(character) + pos) / 256.0);
colour = vec4(mix(Palette[bg], img.rgb * Palette[fg], img.a * mult), 1.0);
vec4 colour = vec4(mix(Palette[bg], img.rgb * Palette[fg], img.a * mult), 1.0) * ColorModulator;
fragColor = linear_fog(colour, vertexDistance, FogStart, FogEnd, FogColor);
}

View File

@ -6,6 +6,13 @@
"uniforms": [
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "IViewRotMat", "type": "matrix3x3", "count": 9, "values": [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
{ "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },
{ "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },
{ "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] },
{ "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] },
{ "name": "Width", "type": "int", "count": 1, "values": [ 1 ] },
{ "name": "Height", "type": "int", "count": 1, "values": [ 1 ] },
{ "name": "Tbo", "type": "int", "count": 1, "values": [ 3 ] }

View File

@ -1,14 +1,21 @@
#version 150
#moj_import <fog.glsl>
in vec3 Position;
in vec2 UV0;
uniform mat4 ModelViewMat;
uniform mat4 ProjMat;
uniform mat3 IViewRotMat;
uniform int FogShape;
out vec2 f_pos;
out float vertexDistance;
out vec2 fontPos;
void main() {
gl_Position = ProjMat * ModelViewMat * vec4(Position, 1);
f_pos = UV0;
vertexDistance = fog_distance(ModelViewMat, IViewRotMat * Position, FogShape);
fontPos = UV0;
}

View File

@ -1,11 +0,0 @@
{
"vertex": "minecraft:position_color_tex",
"fragment": "minecraft:position_color_tex",
"attributes": [ "Position", "Color", "UV0" ],
"samplers": [ { "name": "Sampler0" } ],
"uniforms": [
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
]
}