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:
parent
ba7598c689
commit
41fa95bce4
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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 )
|
||||
);
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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() ),
|
||||
|
@ -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] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 ] }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ] }
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user