1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-01 22:22:59 +00:00

Compare commits

..

29 Commits

Author SHA1 Message Date
Jonathan Coates
f108ba93af Bump version to 1.100.5
> Modulo any game-breaking bugs [...] this will be the last CC: Tweaked
> release.

Terrible performance is game-breaking right? Or am I just petty?
2022-04-27 13:46:55 +01:00
Jonathan Coates
ad228e94a3 Merge remote-tracking branch 'origin/mc-1.16.x' into mc-1.16.x
I hate doing this, but I have too many merges in progress to rebase.
2022-04-26 22:43:22 +01:00
Jonathan Coates
fccca22d3f Merge branch 'feature/font-rendering-optimise' into mc-1.16.x
This /significantly/ improves performance of the VBO renderer (3fps to
80fps with 120 constantly changing monitors) and offers some minor FPS
improvements to the TBO renderer.

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

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

Please note this is only tested on my machine right now - any help
testing on other CPU/GPU configurations is much appreciated.
2022-04-26 21:43:53 +01:00
Jonathan Coates
4bfdb65989 Use VBO renderer when Optifine is installed
Historically I've been reluctant to do this as people might be running
Optifine for performance rather than shaders, and the VBO renderer was
significantly slower when monitors were changing.

With the recent performance optimisations, the difference isn't as bad.
Given how many people ask/complain about the TBO renderer and shaders, I
think it's worth doing this, even if it's not as granular as I'd like.

Also changes how we do the monitor backend check. We now only check for
compatibility if BEST is selected - if there's an override, we assume
the user knows what they're doing (a bold assumption, if I may say so
myself).
2022-04-26 21:43:21 +01:00
Jonathan Coates
22e8b9b587 Don't render cursors separately
- For TBOs, we now pass cursor position, colour and blink state as
   variables to the shader, and use them to overlay the cursor texture
   in the right place.

   As we no longer need to render the cursor, we can skip the depth
   buffer, meaning we have to do one fewer upload+draw cycle.

 - For VBOs, we bake the cursor into the main VBO, and switch between
   rendering n and n+1 quads. We still need the depth blocker, but can
   save one upload+draw cycle when the cursor is visible.

This saves significant time on the TBO renderer - somewhere between 4
and 7ms/frame, which bumps us up from 35 to 47fps on my test world (480
full-sized monitors, changing every tick). [Taken on 1.18, but should be
similar on 1.16]
2022-04-26 21:43:21 +01:00
Jonathan Coates
77a00b14ae Use UBOs for the TBO renderer
Like #455, this sets our uniforms via a UBO rather than having separate
ones for each value. There are a couple of small differences:

 - Have a UBO for each monitor, rather than sharing one and rewriting it
   every monitor. This means we only need to update the buffer when the
   monitor changes.

 - Use std140 rather than the default layout. This means we don't have
   to care about location/stride in the buffer.

Also like #455, this doesn't actually seem to result in any performance
improvements for me. However, it does make it a bit easier to handle a
large number of uniforms.

Also cleans up the generation of the main monitor texture buffer:

 - Move buffer generation into a separate method - just ensures that it
   shows up separately in profilers.
 - Explicitly pass the position when setting bytes, rather than
   incrementing the internal one. This saves some memory reads/writes (I
   thought Java optimised them out, evidently not!). Saves a few fps
   when updating.
 - Use DSA when possible. Unclear if it helps at all, but nice to do :).
2022-04-26 21:43:21 +01:00
Jonathan Coates
78aa757549 Memorize getRenderBoundingBox
This takes a non-trivial amount of time on the render thread[^1], 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]

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

[^2]: I wish I could provide a narrower range, but it varies so much
between me restarting the game. Makes it impossible to benchmark
anything!
2022-04-26 21:43:21 +01:00
Jonathan Coates
1196568a7c Use Ű̶̹̚n̵̦̂́s̷̭̲͐a̶̞͔̔f̸̠́̀e̵͔̋̀ to upload the monitor's contents
The VBO renderer needs to generate a buffer with two quads for each
cell, and then transfer it to the GPU. For large monitors, generating
this buffer can get quite slow. Most of the issues come from
IVertexBuilder (VertexConsumer under MojMap) having a lot of overhead.

By emitting a ByteBuffer directly (and doing so with Unsafe to avoid
bounds checks), we can improve performance 10 fold, going from
3fps/300ms for 120 monitors to 111fps/9ms.

See 41fa95bce4 and #1065 for some more
context and other exploratory work. The key thing to note is we _need_ a
separate version of FWFR for emitting to a ByteBuffer, as introducing
polymorphism to it comes with a significant performance hit.
2022-04-26 21:43:21 +01:00
Jonathan Coates
48c4f397f9 Backport some text rendering code from 1.18
- Move all RenderType instances into a common class.

Cherry-picked from 41fa95bce4:
 - Render GL_QUADS instead of GL_TRIANGLES.

 - Remove any "immediate mode" methods from FWFR. Most use-cases can be
   replaced with the global MultiBufferSource and a proper RenderType
   (which we weren't using correctly before!).

   Only the GUI code (WidgetTerminal) needs to use the immediate mode.

 - Pre-convert palette colours to bytes, storing both the coloured and
   greyscale versions as a byte array.

Cherry-picked from 3eb601e554:
 - Pass lightmap variables around the various renderers. Fixes #919 for
   1.16!
2022-04-26 17:56:43 +01:00
Jonathan Coates
8871f40ced Move FWFR into a separate package
Just a precursor to doing any work on it.
2022-04-26 16:27:49 +01:00
Jonathan Coates
aa62c1f206 Remove redundant CSS in src/web/styles.css
Moved to illuaminate itself
2022-04-26 16:20:04 +01:00
Jonathan Coates
fd32b06d6f Merge pull request #1078 from Hasaabitt/patch-1
Fixed typo
2022-04-24 22:51:21 +01:00
Hasaabitt
739d6813c0 Fixed typo
"Instead, it is a standard program, which its API into the programs that it launches."
becomes
"Instead, it is a standard program, which injects its API into the programs that it launches."
2022-04-24 16:56:31 -04:00
Jonathan Coates
daf81b897a Use vector helpers to convert BlockPos to Vector3d
A little shorter and more explicit than constructing the Vector3d
manually. Fixes an issue where sounds were centered on the bottom left
of speakers, not the middle (see cc-tweaked/cc-restitched#85).
2022-04-22 09:30:04 +01:00
Jonathan Coates
e865d96f7b Fix some typos in the colors API
Closes #1072
2022-04-16 21:14:43 +01:00
Jonathan Coates
79b1872cab Fall back to the given side if the internal one isn't provided
See #1061, closes #1064.

Nobody ever seems to implement this correctly (though it's better than
1.12, at least we've not seen any crashes), and this isn't a fight I
care enough about fighting any more.
2022-04-08 10:12:41 +01:00
Jonathan Coates
2a92794da3 Merge pull request #1055 from bclindner/mc-1.16.x
Documentation fix for rednet.broadcast
2022-03-27 17:40:33 +01:00
Brian C. Lindner
b3e009cca5 doc fix 2022-03-27 16:12:42 +00:00
Jonathan Coates
2c64186965 Bump version to 1.100.4 2022-03-23 08:36:09 +00:00
Jonathan Coates
31ba17d085 Don't wait for the chunk to be loaded when checking for monitors
There's a couple of alternative ways to solve this. Ideally we'd send
our network messages at the same time as MC does
(ChunkManager.playerLoadedChunk), but this'd require a mixin.

Instead we just rely on the fact that if the chunk isn't loaded,
monitors won't have done anything and so we don't need to send their
contents!

Fixes #1047, probably doesn't cause any regressions. I've not seen any
issues on 1.16, but I also hadn't before so ¯\_(ツ)_/¯.
2022-03-18 19:59:02 +00:00
Jonathan Coates
bcc7dd6991 Fix typo in Javadoc 2022-03-02 12:54:59 +00:00
Jonathan Coates
bd36185662 Bump version
Holding off until Forge releases for 1.18.2
2022-02-28 15:35:16 +00:00
Jonathan Coates
045c4fc88c Merge pull request #1027 from Toad-Dev/issue-1026
Fix large file uploads producing oversized packets.
2022-02-28 11:06:59 +00:00
Jonathan Coates
e0fcc425c6 Prevent id map being null when file is empty
Fixes #1030
2022-02-28 11:01:04 +00:00
Jonathan Coates
e01895d719 Remove turtle_player EntityType
This was added in the 1.13 update and I'm still not sure why. Other mods
seem to get away without it, so I think it's fine to remove.

Also remove the fake net manager, as that's part of Forge nowadays.

Fixes #1044.
2022-02-28 10:34:41 +00:00
Jonathan Coates
87b38f4249 Fix incorrect recipe name in turtle advancement 2022-02-28 10:32:33 +00:00
Toad-Dev
60d1d1bb18 Fix large file uploads producing oversized packets.
- Fixes #1026
- The remaining bytes counter wasn't being decremented, so the code that
  splits off smaller packets was unreachable. Thus all file slices were
  being put into a single UploadFileMessage packet.
2022-01-23 22:31:27 -08:00
Jonathan Coates
cdf8b77ffd Merge pull request #1017 from Possseidon/patch-1
Fix table with mouse button codes in documentation.
2022-01-20 12:40:09 +00:00
Possseidon
e2ce52fe81 Fix table with mouse button codes.
Codes for right and middle mouse buttons were swapped.
2022-01-19 17:39:19 +01:00
51 changed files with 1224 additions and 1096 deletions

View File

@@ -19,8 +19,8 @@ numerical value depending on which button on your mouse was last pressed when th
<!-- Our markdown parser doesn't work on tables!? Guess I'll have to roll my own soonish :/. -->
<tr><th>Button code</th><th>Mouse button</th></tr>
<tr><td align="right">1</td><td>Left button</td></tr>
<tr><td align="right">2</td><td>Middle button</td></tr>
<tr><td align="right">3</td><td>Right button</td></tr>
<tr><td align="right">2</td><td>Right button</td></tr>
<tr><td align="right">3</td><td>Middle button</td></tr>
</table>
## Example

View File

@@ -1,5 +1,5 @@
# Mod properties
mod_version=1.100.2
mod_version=1.100.5
# Minecraft properties (update mods.toml when changing)
mc_version=1.16.5

View File

@@ -118,7 +118,7 @@ public final class ComputerCraftAPI
}
/**
* rers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
* Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations.
*
* @param provider The peripheral provider to register.
* @see IPeripheral

View File

@@ -10,7 +10,6 @@ import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.render.TurtlePlayerRenderer;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
@@ -33,7 +32,6 @@ import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
@@ -140,8 +138,6 @@ public final class ClientRegistry
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
net.minecraftforge.fml.client.registry.ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
registerItemProperty( "state",
( stack, world, player ) -> ItemPocketComputer.getState( stack ).ordinal(),
Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED

View File

@@ -1,349 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.RenderState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.TransformationMatrix;
import org.lwjgl.opengl.GL11;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class FixedWidthFontRenderer
{
private static final Matrix4f IDENTITY = TransformationMatrix.identity().getMatrix();
private static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6;
public static final float WIDTH = 256.0f;
public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
public static final RenderType TYPE = Type.MAIN;
private FixedWidthFontRenderer()
{
}
public static float toGreyscale( double[] rgb )
{
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
}
public static int getColour( char c, Colour def )
{
return 15 - Terminal.getColour( c, def );
}
private static void drawChar( Matrix4f transform, IVertexBuilder buffer, float x, float y, int index, float r, float g, float b )
{
// Short circuit to avoid the common case - the texture should be blank here after all.
if( index == '\0' || index == ' ' ) return;
int column = index % 16;
int row = index / 16;
int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2);
buffer.vertex( transform, x, y, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, yStart / WIDTH ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex();
buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
buffer.vertex( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex();
}
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, float r, float g, float b )
{
buffer.vertex( transform, x, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex();
buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex();
buffer.vertex( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_END ).endVertex();
}
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
{
double[] colour = palette.getColour( getColour( colourIndex, Colour.BLACK ) );
float r, g, b;
if( greyscale )
{
r = g = b = toGreyscale( colour );
}
else
{
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
drawQuad( transform, buffer, x, y, width, height, r, g, b );
}
private static void drawBackground(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y,
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height
)
{
if( leftMarginSize > 0 )
{
drawQuad( transform, renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
}
if( rightMarginSize > 0 )
{
drawQuad( transform, renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) );
}
// Batch together runs of identical background cells.
int blockStart = 0;
char blockColour = '\0';
for( int i = 0; i < backgroundColour.length(); i++ )
{
char colourIndex = backgroundColour.charAt( i );
if( colourIndex == blockColour ) continue;
if( blockColour != '\0' )
{
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
}
}
public static void drawString(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y,
@Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize
)
{
if( backgroundColour != null )
{
drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT );
}
for( int i = 0; i < text.length(); i++ )
{
double[] colour = palette.getColour( getColour( textColour.charAt( i ), Colour.BLACK ) );
float r, g, b;
if( greyscale )
{
r = g = b = toGreyscale( colour );
}
else
{
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
// Draw char
int index = text.charAt( i );
if( index > 255 ) index = '?';
drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b );
}
}
public static void drawString(
float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour,
@Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize
)
{
bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
drawString( IDENTITY, ((IRenderTypeBuffer) renderer).getBuffer( TYPE ), x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize );
renderer.endBatch();
}
public static void drawTerminalWithoutCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
Palette palette = terminal.getPalette();
int height = terminal.getHeight();
// Top and bottom margins
drawBackground(
transform, buffer, x, y - topMarginSize,
terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize
);
drawBackground(
transform, buffer, x, y + height * FONT_HEIGHT,
terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
leftMarginSize, rightMarginSize, bottomMarginSize
);
// The main text
for( int i = 0; i < height; i++ )
{
drawString(
transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i,
terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ),
palette, greyscale, leftMarginSize, rightMarginSize
);
}
}
public static void drawCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale
)
{
Palette palette = terminal.getPalette();
int width = terminal.getWidth();
int height = terminal.getHeight();
int cursorX = terminal.getCursorX();
int cursorY = terminal.getCursorY();
if( terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height && FrameInfo.getGlobalCursorBlink() )
{
double[] colour = palette.getColour( 15 - terminal.getTextColour() );
float r, g, b;
if( greyscale )
{
r = g = b = toGreyscale( colour );
}
else
{
r = (float) colour[0];
g = (float) colour[1];
b = (float) colour[2];
}
drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b );
}
}
public static void drawTerminal(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( transform, buffer, x, y, terminal, greyscale );
}
public static void drawTerminal(
@Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
IVertexBuilder buffer = renderer.getBuffer( TYPE );
drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
renderer.endBatch( TYPE );
}
public static void drawTerminal(
float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
drawTerminal( IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( TYPE ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height )
{
bindFont();
IRenderTypeBuffer.Impl renderer = Minecraft.getInstance().renderBuffers().bufferSource();
drawEmptyTerminal( transform, renderer, x, y, width, height );
renderer.endBatch();
}
public static void drawEmptyTerminal( float x, float y, float width, float height )
{
drawEmptyTerminal( IDENTITY, x, y, width, height );
}
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull IRenderTypeBuffer renderer, float x, float y, float width, float height )
{
Colour colour = Colour.BLACK;
drawQuad( transform, renderer.getBuffer( Type.BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() );
}
private static void bindFont()
{
Minecraft.getInstance().getTextureManager().bind( FONT );
RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP );
}
private static final class Type extends RenderState
{
private static final int GL_MODE = GL11.GL_TRIANGLES;
private static final VertexFormat FORMAT = DefaultVertexFormats.POSITION_COLOR_TEX;
static final RenderType MAIN = RenderType.create(
"terminal_font", FORMAT, GL_MODE, 1024,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setTextureState( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
.setAlphaState( DEFAULT_ALPHA )
.setLightmapState( NO_LIGHTMAP )
.setWriteMaskState( COLOR_WRITE )
.createCompositeState( false )
);
static final RenderType BLOCKER = RenderType.create(
"terminal_blocker", FORMAT, GL_MODE, 256,
false, false, // useDelegate, needsSorting
RenderType.State.builder()
.setTextureState( new RenderState.TextureState( FONT, false, false ) ) // blur, minimap
.setAlphaState( DEFAULT_ALPHA )
.setWriteMaskState( DEPTH_WRITE )
.setLightmapState( NO_LIGHTMAP )
.createCompositeState( false )
);
private Type( String name, Runnable setup, Runnable destroy )
{
super( name, setup, destroy );
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,62 +9,55 @@ import com.google.common.base.Strings;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour;
class MonitorTextureBufferShader
{
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer( 16 );
private static final FloatBuffer PALETTE_BUFFER = BufferUtils.createFloatBuffer( 16 * 3 );
private static int uniformMv;
private static int uniformFont;
private static int uniformWidth;
private static int uniformHeight;
private static int uniformTbo;
private static int uniformPalette;
private static int uniformMonitor;
private static int uniformCursorBlink;
private static boolean initialised;
private static boolean ok;
private static int program;
static void setupUniform( Matrix4f transform, int width, int height, Palette palette, boolean greyscale )
static void setupUniform( Matrix4f transform, int tboUniform )
{
MATRIX_BUFFER.rewind();
transform.store( MATRIX_BUFFER );
MATRIX_BUFFER.rewind();
RenderSystem.glUniformMatrix4( uniformMv, false, MATRIX_BUFFER );
RenderSystem.glUniform1i( uniformWidth, width );
RenderSystem.glUniform1i( uniformHeight, height );
GL31.glBindBufferBase( GL31.GL_UNIFORM_BUFFER, uniformMonitor, tboUniform );
PALETTE_BUFFER.rewind();
for( int i = 0; i < 16; i++ )
{
double[] colour = palette.getColour( i );
if( greyscale )
{
float f = FixedWidthFontRenderer.toGreyscale( colour );
PALETTE_BUFFER.put( f ).put( f ).put( f );
}
else
{
PALETTE_BUFFER.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] );
}
}
PALETTE_BUFFER.flip();
RenderSystem.glUniform3( uniformPalette, PALETTE_BUFFER );
int cursorAlpha = FrameInfo.getGlobalCursorBlink() ? 1 : 0;
RenderSystem.glUniform1i( uniformCursorBlink, cursorAlpha );
}
static boolean use()
@@ -116,10 +109,10 @@ class MonitorTextureBufferShader
uniformMv = getUniformLocation( program, "u_mv" );
uniformFont = getUniformLocation( program, "u_font" );
uniformWidth = getUniformLocation( program, "u_width" );
uniformHeight = getUniformLocation( program, "u_height" );
uniformTbo = getUniformLocation( program, "u_tbo" );
uniformPalette = getUniformLocation( program, "u_palette" );
uniformMonitor = GL31.glGetUniformBlockIndex( program, "u_monitor" );
if( uniformMonitor == -1 ) throw new IllegalStateException( "Could not find uniformMonitor uniform." );
uniformCursorBlink = getUniformLocation( program, "u_cursorBlink" );
ComputerCraft.log.info( "Loaded monitor shader." );
return true;
@@ -159,4 +152,54 @@ class MonitorTextureBufferShader
if( uniform == -1 ) throw new IllegalStateException( "Cannot find uniform " + name );
return uniform;
}
public static void setTerminalData( ByteBuffer buffer, Terminal terminal )
{
int width = terminal.getWidth(), height = terminal.getHeight();
int pos = 0;
for( int y = 0; y < height; y++ )
{
TextBuffer text = terminal.getLine( y ), textColour = terminal.getTextColourLine( y ), background = terminal.getBackgroundColourLine( y );
for( int x = 0; x < width; x++ )
{
buffer.put( pos, (byte) (text.charAt( x ) & 0xFF) );
buffer.put( pos + 1, (byte) getColour( textColour.charAt( x ), Colour.WHITE ) );
buffer.put( pos + 2, (byte) getColour( background.charAt( x ), Colour.BLACK ) );
pos += 3;
}
}
buffer.limit( pos );
}
public static void setUniformData( ByteBuffer buffer, Terminal terminal, boolean greyscale )
{
int pos = 0;
Palette palette = terminal.getPalette();
for( int i = 0; i < 16; i++ )
{
double[] colour = palette.getColour( i );
if( greyscale )
{
float f = FixedWidthFontRenderer.toGreyscale( colour );
buffer.putFloat( pos, f ).putFloat( pos + 4, f ).putFloat( pos + 8, f );
}
else
{
buffer.putFloat( pos, (float) colour[0] ).putFloat( pos + 4, (float) colour[1] ).putFloat( pos + 8, (float) colour[2] );
}
pos += 4 * 4; // std140 requires these are 4-wide
}
boolean showCursor = FixedWidthFontRenderer.isCursorVisible( terminal );
buffer
.putInt( pos, terminal.getWidth() ).putInt( pos + 4, terminal.getHeight() )
.putInt( pos + 8, showCursor ? terminal.getCursorX() : -2 )
.putInt( pos + 12, showCursor ? terminal.getCursorY() : -2 )
.putInt( pos + 16, 15 - terminal.getTextColour() );
buffer.limit( UNIFORM_SIZE );
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,231 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render.text;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.client.util.DirectBuffers;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import org.lwjgl.system.MemoryUtil;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*;
import static org.lwjgl.system.MemoryUtil.memPutByte;
import static org.lwjgl.system.MemoryUtil.memPutFloat;
/**
* An optimised copy of {@link FixedWidthFontRenderer} emitter emits directly to a {@link ByteBuffer} rather than
* emitting to {@link IVertexBuilder}. This allows us to emit vertices very quickly, when using the VBO renderer.
*
* There are some limitations here:
* <ul>
* <li>No transformation matrix (not needed for VBOs).</li>
* <li>Only works with {@link DefaultVertexFormats#POSITION_COLOR_TEX}.</li>
* <li>The buffer <strong>MUST</strong> be allocated with {@link DirectBuffers}, and not through any other means.</li>
* </ul>
*
* Note this is almost an exact copy of {@link FixedWidthFontRenderer}. While the code duplication is unfortunate,
* it is measurably faster than introducing polymorphism into {@link FixedWidthFontRenderer}.
*
* <strong>IMPORTANT: </strong> When making changes to this class, please check if you need to make the same changes to
* {@link FixedWidthFontRenderer}.
*/
public final class DirectFixedWidthFontRenderer
{
private DirectFixedWidthFontRenderer()
{
}
private static void drawChar( ByteBuffer buffer, float x, float y, int index, byte[] colour )
{
// Short circuit to avoid the common case - the texture should be blank here after all.
if( index == '\0' || index == ' ' ) return;
int column = index % 16;
int row = index / 16;
int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2);
quad(
buffer, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, colour,
xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH
);
}
private static void drawQuad( ByteBuffer emitter, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex )
{
byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale );
quad( emitter, x, y, x + width, y + height, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END );
}
private static void drawBackground(
@Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height
)
{
if( leftMarginSize > 0 )
{
drawQuad( buffer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) );
}
if( rightMarginSize > 0 )
{
drawQuad( buffer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) );
}
// Batch together runs of identical background cells.
int blockStart = 0;
char blockColour = '\0';
for( int i = 0; i < backgroundColour.length(); i++ )
{
char colourIndex = backgroundColour.charAt( i );
if( colourIndex == blockColour ) continue;
if( blockColour != '\0' )
{
drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour );
}
}
private static void drawString( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale )
{
for( int i = 0; i < text.length(); i++ )
{
byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale );
int index = text.charAt( i );
if( index > 255 ) index = '?';
drawChar( buffer, x + i * FONT_WIDTH, y, index, colour );
}
}
public static void drawTerminalWithoutCursor(
@Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
Palette palette = terminal.getPalette();
int height = terminal.getHeight();
// Top and bottom margins
drawBackground(
buffer, x, y - topMarginSize, terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize
);
drawBackground(
buffer, x, y + height * FONT_HEIGHT, terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
leftMarginSize, rightMarginSize, bottomMarginSize
);
// The main text
for( int i = 0; i < height; i++ )
{
float rowY = y + FONT_HEIGHT * i;
drawBackground(
buffer, x, rowY, terminal.getBackgroundColourLine( i ), palette, greyscale,
leftMarginSize, rightMarginSize, FONT_HEIGHT
);
drawString(
buffer, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ),
palette, greyscale
);
}
}
public static void drawCursor( @Nonnull ByteBuffer buffer, float x, float y, @Nonnull Terminal terminal, boolean greyscale )
{
if( isCursorVisible( terminal ) )
{
byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale );
drawChar( buffer, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour );
}
}
public static int getVertexCount( Terminal terminal )
{
return (1 + (terminal.getHeight() + 2) * terminal.getWidth() * 2) * 4;
}
private static void quad( ByteBuffer buffer, float x1, float y1, float x2, float y2, byte[] rgba, float u1, float v1, float u2, float v2 )
{
// Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
// underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
// This provides significant performance gains, at the cost of well, using Unsafe.
// Each vertex is 24 bytes, giving 96 bytes in total. Vertices are of the form (xyz:FFF)(rgba:BBBB)(uv:FF),
// which matches the POSITION_COLOR_TEX vertex format.
int position = buffer.position();
long addr = MemoryUtil.memAddress( buffer );
// We're doing terrible unsafe hacks below, so let's be really sure that what we're doing is reasonable.
if( position < 0 || 96 > buffer.limit() - position ) throw new IndexOutOfBoundsException();
// Require the pointer to be aligned to a 32-bit boundary.
if( (addr & 3) != 0 ) throw new IllegalStateException( "Memory is not aligned" );
// Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
if( rgba.length != 4 ) throw new IllegalStateException();
memPutFloat( addr + 0, x1 );
memPutFloat( addr + 4, y1 );
memPutFloat( addr + 8, 0 );
memPutByte( addr + 12, rgba[0] );
memPutByte( addr + 13, rgba[1] );
memPutByte( addr + 14, rgba[2] );
memPutByte( addr + 15, (byte) 255 );
memPutFloat( addr + 16, u1 );
memPutFloat( addr + 20, v1 );
memPutFloat( addr + 24, x1 );
memPutFloat( addr + 28, y2 );
memPutFloat( addr + 32, 0 );
memPutByte( addr + 36, rgba[0] );
memPutByte( addr + 37, rgba[1] );
memPutByte( addr + 38, rgba[2] );
memPutByte( addr + 39, (byte) 255 );
memPutFloat( addr + 40, u1 );
memPutFloat( addr + 44, v2 );
memPutFloat( addr + 48, x2 );
memPutFloat( addr + 52, y2 );
memPutFloat( addr + 56, 0 );
memPutByte( addr + 60, rgba[0] );
memPutByte( addr + 61, rgba[1] );
memPutByte( addr + 62, rgba[2] );
memPutByte( addr + 63, (byte) 255 );
memPutFloat( addr + 64, u2 );
memPutFloat( addr + 68, v2 );
memPutFloat( addr + 72, x2 );
memPutFloat( addr + 76, y1 );
memPutFloat( addr + 80, 0 );
memPutByte( addr + 84, rgba[0] );
memPutByte( addr + 85, rgba[1] );
memPutByte( addr + 86, rgba[2] );
memPutByte( addr + 87, (byte) 255 );
memPutFloat( addr + 88, u2 );
memPutFloat( addr + 92, v1 );
// Finally increment the position.
buffer.position( position + 96 );
// Well done for getting to the end of this method. I recommend you take a break and go look at cute puppies.
}
}

View File

@@ -0,0 +1,243 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render.text;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.util.Colour;
import dan200.computercraft.shared.util.Palette;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import javax.annotation.Nonnull;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
/**
* Handles rendering fixed width text and computer terminals.
*
* This class has several modes of usage:
* <ul>
* <li>{@link #drawString}: Drawing basic text without a terminal (such as for printouts). Unlike the other methods,
* this accepts a lightmap coordinate as, unlike terminals, printed pages render fullbright.</li>
* <li>{@link #drawTerminalWithoutCursor}/{@link #drawCursor}: Draw a terminal without a cursor and then draw the cursor
* separately. This is used by the monitor renderer to render the terminal to a VBO and draw the cursor dynamically.
* </li>
* <li>{@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the
* whole term.</li>
* <li>{@link #drawBlocker}: When rendering a terminal using {@link RenderTypes#TERMINAL_WITHOUT_DEPTH} you need to
* render an additional "depth blocker" on top of the monitor.</li>
* </ul>
*
* <strong>IMPORTANT: </strong> When making changes to this class, please check if you need to make the same changes to
* {@link DirectFixedWidthFontRenderer}.
*/
public final class FixedWidthFontRenderer
{
public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" );
public static final int FONT_HEIGHT = 9;
public static final int FONT_WIDTH = 6;
static final float WIDTH = 256.0f;
static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
private static final byte[] BLACK = new byte[] { byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), byteColour( Colour.BLACK.getR() ), (byte) 255 };
private FixedWidthFontRenderer()
{
}
private static byte byteColour( float c )
{
return (byte) (int) (c * 255);
}
public static float toGreyscale( double[] rgb )
{
return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3);
}
public static int getColour( char c, Colour def )
{
return 15 - Terminal.getColour( c, def );
}
private static void drawChar( Matrix4f transform, IVertexBuilder buffer, float x, float y, int index, byte[] colour, int light )
{
// Short circuit to avoid the common case - the texture should be blank here after all.
if( index == '\0' || index == ' ' ) return;
int column = index % 16;
int row = index / 16;
int xStart = 1 + column * (FONT_WIDTH + 2);
int yStart = 1 + row * (FONT_HEIGHT + 2);
quad(
transform, buffer, x, y, x + FONT_WIDTH, y + FONT_HEIGHT, 0, colour,
xStart / WIDTH, yStart / WIDTH, (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH, light
);
}
public static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float z, float width, float height, byte[] colour, int light )
{
quad( transform, buffer, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light );
}
private static void drawQuad( Matrix4f transform, IVertexBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex, int light )
{
byte[] colour = palette.getByteColour( getColour( colourIndex, Colour.BLACK ), greyscale );
drawQuad( transform, buffer, x, y, 0, width, height, colour, light );
}
private static void drawBackground(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale,
float leftMarginSize, float rightMarginSize, float height, int light
)
{
if( leftMarginSize > 0 )
{
drawQuad( transform, buffer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ), light );
}
if( rightMarginSize > 0 )
{
drawQuad( transform, buffer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ), light );
}
// Batch together runs of identical background cells.
int blockStart = 0;
char blockColour = '\0';
for( int i = 0; i < backgroundColour.length(); i++ )
{
char colourIndex = backgroundColour.charAt( i );
if( colourIndex == blockColour ) continue;
if( blockColour != '\0' )
{
drawQuad( transform, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour, light );
}
blockColour = colourIndex;
blockStart = i;
}
if( blockColour != '\0' )
{
drawQuad( transform, buffer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour, light );
}
}
public static void drawString(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y,
@Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nonnull Palette palette, boolean greyscale, int light
)
{
for( int i = 0; i < text.length(); i++ )
{
byte[] colour = palette.getByteColour( getColour( textColour.charAt( i ), Colour.BLACK ), greyscale );
int index = text.charAt( i );
if( index > 255 ) index = '?';
drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, colour, light );
}
}
public static void drawTerminalWithoutCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
Palette palette = terminal.getPalette();
int height = terminal.getHeight();
// Top and bottom margins
drawBackground(
transform, buffer, x, y - topMarginSize,
terminal.getBackgroundColourLine( 0 ), palette, greyscale,
leftMarginSize, rightMarginSize, topMarginSize, FULL_BRIGHT_LIGHTMAP
);
drawBackground(
transform, buffer, x, y + height * FONT_HEIGHT,
terminal.getBackgroundColourLine( height - 1 ), palette, greyscale,
leftMarginSize, rightMarginSize, bottomMarginSize, FULL_BRIGHT_LIGHTMAP
);
// The main text
for( int i = 0; i < height; i++ )
{
float rowY = y + FONT_HEIGHT * i;
drawBackground(
transform, buffer, x, rowY, terminal.getBackgroundColourLine( i ),
palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT, FULL_BRIGHT_LIGHTMAP
);
drawString(
transform, buffer, x, rowY, terminal.getLine( i ), terminal.getTextColourLine( i ),
palette, greyscale, FULL_BRIGHT_LIGHTMAP
);
}
}
public static boolean isCursorVisible( Terminal terminal )
{
if( !terminal.getCursorBlink() ) return false;
int cursorX = terminal.getCursorX();
int cursorY = terminal.getCursorY();
return cursorX >= 0 && cursorX < terminal.getWidth() && cursorY >= 0 && cursorY < terminal.getHeight();
}
public static void drawCursor(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale
)
{
if( isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() )
{
byte[] colour = terminal.getPalette().getByteColour( 15 - terminal.getTextColour(), greyscale );
drawChar( transform, buffer, x + terminal.getCursorX() * FONT_WIDTH, y + terminal.getCursorY() * FONT_HEIGHT, '_', colour, FULL_BRIGHT_LIGHTMAP );
}
}
public static void drawTerminal(
@Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y,
@Nonnull Terminal terminal, boolean greyscale,
float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize
)
{
drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize );
drawCursor( transform, buffer, x, y, terminal, greyscale );
}
public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull IVertexBuilder renderer, float x, float y, float width, float height )
{
drawQuad( transform, renderer, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull IVertexBuilder buffer, float x, float y, float width, float height )
{
drawQuad( transform, buffer, x, y, 0, width, height, BLACK, FULL_BRIGHT_LIGHTMAP );
}
private static void quad( Matrix4f matrix, IVertexBuilder buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light )
{
byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
buffer.vertex( matrix, x1, y1, z ).color( r, g, b, a ).uv( u1, v1 ).uv2( light ).endVertex();
buffer.vertex( matrix, x1, y2, z ).color( r, g, b, a ).uv( u1, v2 ).uv2( light ).endVertex();
buffer.vertex( matrix, x2, y2, z ).color( r, g, b, a ).uv( u2, v2 ).uv2( light ).endVertex();
buffer.vertex( matrix, x2, y1, z ).color( r, g, b, a ).uv( u2, v1 ).uv2( light ).endVertex();
}
}

View File

@@ -0,0 +1,83 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.util;
import com.mojang.blaze3d.platform.GlStateManager;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL15C;
import org.lwjgl.opengl.GL45C;
import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
/**
* Provides utilities to interact with OpenGL's buffer objects, either using direct state access or binding/unbinding
* it.
*/
public class DirectBuffers
{
public static final boolean HAS_DSA;
static
{
GLCapabilities capabilities = GL.getCapabilities();
HAS_DSA = capabilities.OpenGL45 || capabilities.GL_ARB_direct_state_access;
}
public static int createBuffer()
{
return HAS_DSA ? GL45C.glCreateBuffers() : GL15C.glGenBuffers();
}
public static void setBufferData( int type, int id, ByteBuffer buffer, int flags )
{
if( HAS_DSA )
{
GL45C.glNamedBufferData( id, buffer, flags );
}
else
{
GlStateManager._glBindBuffer( type, id );
GlStateManager._glBufferData( type, buffer, GL15C.GL_STATIC_DRAW );
GlStateManager._glBindBuffer( type, 0 );
}
}
public static void setEmptyBufferData( int type, int id, int flags )
{
if( HAS_DSA )
{
GL45C.glNamedBufferData( id, 0, flags );
}
else
{
GlStateManager._glBindBuffer( type, id );
GL45C.glBufferData( type, 0, GL15C.GL_STATIC_DRAW );
GlStateManager._glBindBuffer( type, 0 );
}
}
private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator( false );
public static ByteBuffer createByteBuffer( int size )
{
long i = ALLOCATOR.malloc( size );
if( i == 0L ) throw new OutOfMemoryError( "Failed to allocate " + size + " bytes" );
return MemoryUtil.memByteBuffer( i, size );
}
public static ByteBuffer resizeByteBuffer( ByteBuffer buffer, int size )
{
long i = ALLOCATOR.realloc( MemoryUtil.memAddress0( buffer ), size );
if( i == 0L )
{
throw new OutOfMemoryError( "Failed to resize buffer from " + buffer.capacity() + " bytes to " + size + " bytes" );
}
return MemoryUtil.memByteBuffer( i, size );
}
}

View File

@@ -0,0 +1,79 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.util;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.math.vector.Matrix4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import java.nio.ByteBuffer;
/**
* A version of {@link VertexBuffer} which allows uploading {@link ByteBuffer}s directly.
*/
public class DirectVertexBuffer implements AutoCloseable
{
private int vertextBufferId;
private int indexCount;
private VertexFormat format;
public DirectVertexBuffer()
{
vertextBufferId = DirectBuffers.createBuffer();
}
public void upload( int vertexCount, VertexFormat format, ByteBuffer buffer )
{
RenderSystem.assertThread( RenderSystem::isOnGameThread );
DirectBuffers.setBufferData( GL15.GL_ARRAY_BUFFER, vertextBufferId, buffer, GL15.GL_STATIC_DRAW );
this.format = format;
indexCount = vertexCount;
}
public void draw( Matrix4f matrix, int indexCount )
{
bind();
format.setupBufferState( 0 );
RenderSystem.pushMatrix();
RenderSystem.loadIdentity();
RenderSystem.multMatrix( matrix );
RenderSystem.drawArrays( GL11.GL_QUADS, 0, indexCount );
RenderSystem.popMatrix();
unbind();
}
public int getIndexCount()
{
return indexCount;
}
@Override
public void close()
{
if( vertextBufferId >= 0 )
{
RenderSystem.glDeleteBuffers( vertextBufferId );
vertextBufferId = -1;
}
}
private void bind()
{
RenderSystem.glBindBuffer( GL15.GL_ARRAY_BUFFER, () -> vertextBufferId );
}
private static void unbind()
{
RenderSystem.glBindBuffer( GL15.GL_ARRAY_BUFFER, () -> 0 );
}
}

View File

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

View File

@@ -57,7 +57,8 @@ public final class ComputerMBean implements DynamicMBean, Tracker
{
ManagementFactory.getPlatformMBeanServer().registerMBean( instance = new ComputerMBean(), new ObjectName( "dan200.computercraft:type=Computers" ) );
}
catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException | MalformedObjectNameException e )
catch( InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException |
MalformedObjectNameException e )
{
ComputerCraft.log.warn( "Failed to register JMX bean", e );
}

View File

@@ -16,6 +16,7 @@ import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import net.minecraft.entity.EntityType;
import net.minecraft.inventory.container.Container;
import net.minecraft.loot.ConstantRange;
import net.minecraft.loot.LootPool;
@@ -24,10 +25,7 @@ import net.minecraft.loot.TableLootEntry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.LootTableLoadEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.*;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@@ -146,4 +144,14 @@ public final class CommonHooks
{
event.addListener( ResourceMount.RELOAD_LISTENER );
}
@SubscribeEvent
public static void onMissingEntityMappingsEvent( RegistryEvent.MissingMappings<EntityType<?>> event )
{
ResourceLocation id = new ResourceLocation( ComputerCraft.MOD_ID, "turtle_player" );
for( RegistryEvent.MissingMappings.Mapping<EntityType<?>> mapping : event.getMappings( ComputerCraft.MOD_ID ) )
{
if( mapping.key.equals( id ) ) mapping.ignore();
}
}
}

View File

@@ -60,7 +60,6 @@ import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
import dan200.computercraft.shared.turtle.blocks.BlockTurtle;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.turtle.inventory.ContainerTurtle;
import dan200.computercraft.shared.turtle.items.ItemTurtle;
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
@@ -70,8 +69,6 @@ import dan200.computercraft.shared.util.*;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.item.*;
import net.minecraft.item.crafting.IRecipeSerializer;
@@ -296,18 +293,6 @@ public final class Registry
ComputerCraftAPI.registerPocketUpgrade( ComputerCraft.PocketUpgrades.speaker = new PocketSpeaker() );
}
public static class ModEntities
{
static final DeferredRegister<EntityType<?>> ENTITIES = DeferredRegister.create( ForgeRegistries.ENTITIES, ComputerCraft.MOD_ID );
public static final RegistryObject<EntityType<TurtlePlayer>> TURTLE_PLAYER = ENTITIES.register( "turtle_player", () ->
EntityType.Builder.<TurtlePlayer>createNothing( EntityClassification.MISC )
.noSave()
.noSummon()
.sized( 0, 0 )
.build( ComputerCraft.MOD_ID + ":turtle_player" ) );
}
public static class ModContainers
{
static final DeferredRegister<ContainerType<?>> CONTAINERS = DeferredRegister.create( ForgeRegistries.CONTAINERS, ComputerCraft.MOD_ID );
@@ -418,7 +403,6 @@ public final class Registry
ModBlocks.BLOCKS.register( bus );
ModTiles.TILES.register( bus );
ModItems.ITEMS.register( bus );
ModEntities.ENTITIES.register( bus );
ModContainers.CONTAINERS.register( bus );
}
}

View File

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

View File

@@ -97,7 +97,7 @@ public class TileCommandComputer extends TileComputer
}
return new CommandSource( receiver,
new Vector3d( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), Vector2f.ZERO,
Vector3d.atCenterOf( worldPosition ), Vector2f.ZERO,
(ServerWorld) getLevel(), 2,
name, new StringTextComponent( name ),
getLevel().getServer(), null

View File

@@ -160,6 +160,7 @@ public class UploadFileMessage extends ComputerServerMessage
contents.position( currentOffset ).limit( currentOffset + canWrite );
slices.add( new FileSlice( fileId, currentOffset, contents.slice() ) );
currentOffset += canWrite;
remaining -= canWrite;
}
contents.position( 0 ).limit( capacity );

View File

@@ -45,7 +45,7 @@ public class GenericPeripheralProvider
for( Capability<?> capability : capabilities )
{
LazyOptional<?> wrapper = tile.getCapability( capability );
LazyOptional<?> wrapper = CapabilityUtil.getCapability( tile, capability, side );
wrapper.ifPresent( contents -> {
List<NamedMethod<PeripheralMethod>> capabilityMethods = PeripheralMethod.GENERATOR.getMethods( contents.getClass() );
if( capabilityMethods.isEmpty() ) return;

View File

@@ -60,8 +60,7 @@ public class TileCable extends TileGeneric
@Override
public Vector3d getPosition()
{
BlockPos pos = getBlockPos();
return new Vector3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
return Vector3d.atCenterOf( getBlockPos() );
}
@Override
@@ -106,8 +105,7 @@ public class TileCable extends TileGeneric
@Override
public Vector3d getPosition()
{
BlockPos pos = getBlockPos().relative( modemDirection );
return new Vector3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
return Vector3d.atCenterOf( getBlockPos().relative( modemDirection ) );
}
@Nonnull

View File

@@ -87,8 +87,7 @@ public class TileWiredModemFull extends TileGeneric
@Override
public Vector3d getPosition()
{
BlockPos pos = entity.getBlockPos();
return new Vector3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
return Vector3d.atCenterOf( entity.getBlockPos() );
}
}
@@ -418,8 +417,7 @@ public class TileWiredModemFull extends TileGeneric
@Override
public Vector3d getPosition()
{
BlockPos pos = getBlockPos().relative( side );
return new Vector3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
return Vector3d.atCenterOf( getBlockPos().relative( side ) );
}
@Nonnull

View File

@@ -14,7 +14,6 @@ import dan200.computercraft.shared.util.TickScheduler;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
@@ -48,8 +47,7 @@ public class TileWirelessModem extends TileGeneric
@Override
public Vector3d getPosition()
{
BlockPos pos = entity.getBlockPos().relative( entity.modemDirection );
return new Vector3d( pos.getX(), pos.getY(), pos.getZ() );
return Vector3d.atLowerCornerOf( entity.getBlockPos().relative( entity.modemDirection ) );
}
@Override

View File

@@ -7,9 +7,9 @@ package dan200.computercraft.shared.peripheral.monitor;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.client.util.DirectBuffers;
import dan200.computercraft.client.util.DirectVertexBuffer;
import dan200.computercraft.shared.common.ClientTerminal;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@@ -33,7 +33,8 @@ public final class ClientMonitor extends ClientTerminal
public int tboBuffer;
public int tboTexture;
public VertexBuffer buffer;
public int tboUniform;
public DirectVertexBuffer buffer;
public ClientMonitor( boolean colour, TileMonitor origin )
{
@@ -64,15 +65,15 @@ public final class ClientMonitor extends ClientTerminal
deleteBuffers();
tboBuffer = GlStateManager._glGenBuffers();
GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, tboBuffer );
GL15.glBufferData( GL31.GL_TEXTURE_BUFFER, 0, GL15.GL_STATIC_DRAW );
tboBuffer = DirectBuffers.createBuffer();
DirectBuffers.setEmptyBufferData( GL31.GL_TEXTURE_BUFFER, tboBuffer, GL15.GL_STATIC_DRAW );
tboTexture = GlStateManager._genTexture();
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, tboTexture );
GL31.glTexBuffer( GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, tboBuffer );
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, 0 );
GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 );
tboUniform = DirectBuffers.createBuffer();
DirectBuffers.setEmptyBufferData( GL31.GL_UNIFORM_BUFFER, tboUniform, GL15.GL_STATIC_DRAW );
addMonitor();
return true;
@@ -82,7 +83,7 @@ public final class ClientMonitor extends ClientTerminal
if( buffer != null ) return false;
deleteBuffers();
buffer = new VertexBuffer( FixedWidthFontRenderer.TYPE.format() );
buffer = new DirectVertexBuffer();
addMonitor();
return true;
@@ -114,6 +115,12 @@ public final class ClientMonitor extends ClientTerminal
tboTexture = 0;
}
if( tboUniform != 0 )
{
RenderSystem.glDeleteBuffers( tboUniform );
tboUniform = 0;
}
if( buffer != null )
{
buffer.close();

View File

@@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import net.minecraftforge.fml.ModList;
import org.lwjgl.opengl.GL;
import javax.annotation.Nonnull;
@@ -47,39 +48,24 @@ public enum MonitorRenderer
public static MonitorRenderer current()
{
MonitorRenderer current = ComputerCraft.monitorRenderer;
switch( current )
{
case BEST:
return best();
case TBO:
checkCapabilities();
if( !textureBuffer )
{
ComputerCraft.log.warn( "Texture buffers are not supported on your graphics card. Falling back to default." );
ComputerCraft.monitorRenderer = BEST;
return best();
}
return TBO;
default:
return current;
}
if( current == BEST ) current = ComputerCraft.monitorRenderer = best();
return current;
}
private static MonitorRenderer best()
{
checkCapabilities();
return textureBuffer ? TBO : VBO;
}
if( !GL.getCapabilities().OpenGL31 )
{
ComputerCraft.log.warn( "Texture buffers are not supported on your graphics card. Falling back to VBO monitor renderer." );
return VBO;
}
private static boolean initialised = false;
private static boolean textureBuffer = false;
if( ModList.get().isLoaded( "optifine" ) )
{
ComputerCraft.log.warn( "Optifine is loaded, assuming shaders are being used. Falling back to VBO monitor renderer." );
return VBO;
}
private static void checkCapabilities()
{
if( initialised ) return;
textureBuffer = GL.getCapabilities().OpenGL31;
initialised = true;
return TBO;
}
}

View File

@@ -15,7 +15,6 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.world.ChunkWatchEvent;
@@ -47,8 +46,10 @@ public final class MonitorWatcher
@SubscribeEvent
public static void onWatch( ChunkWatchEvent.Watch event )
{
// Get the current chunk if it has been loaded. This is safe as, if the chunk hasn't been loaded yet, then the
// monitor will have no contents, and so we won't need to send an update anyway.
ChunkPos chunkPos = event.getPos();
Chunk chunk = (Chunk) event.getWorld().getChunk( chunkPos.x, chunkPos.z, ChunkStatus.FULL, false );
Chunk chunk = event.getWorld().getChunkSource().getChunkNow( chunkPos.x, chunkPos.z );
if( chunk == null ) return;
for( TileEntity te : chunk.getBlockEntities().values() )

View File

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

View File

@@ -13,7 +13,6 @@ import dan200.computercraft.shared.util.CapabilityUtil;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
@@ -90,8 +89,7 @@ public class TileSpeaker extends TileGeneric implements ITickableTileEntity
@Override
public Vector3d getPosition()
{
BlockPos pos = speaker.getBlockPos();
return new Vector3d( pos.getX(), pos.getY(), pos.getZ() );
return Vector3d.atCenterOf( speaker.getBlockPos() );
}
@Override

View File

@@ -69,6 +69,7 @@ import java.util.Optional;
* accessible by accessing the `"left"` or `"right"` peripheral.
*
* [Turtle Graphics]: https://en.wikipedia.org/wiki/Turtle_graphics "Turtle graphics"
*
* @cc.module turtle
* @cc.since 1.3
*/

View File

@@ -8,14 +8,11 @@ package dan200.computercraft.shared.turtle.core;
import com.mojang.authlib.GameProfile;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.FakeNetHandler;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntitySize;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.Pose;
import net.minecraft.entity.passive.horse.AbstractHorseEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
@@ -54,7 +51,6 @@ public final class TurtlePlayer extends FakePlayer
GameProfile profile = turtle.getOwningPlayer();
TurtlePlayer player = new TurtlePlayer( world, getProfile( profile ) );
player.connection = new FakeNetHandler( player );
player.setState( turtle );
if( profile != null && profile.getId() != null )
@@ -203,13 +199,6 @@ public final class TurtlePlayer extends FakePlayer
inventory.setChanged();
}
@Nonnull
@Override
public EntityType<?> getType()
{
return Registry.ModEntities.TURTLE_PLAYER.get();
}
@Override
public Vector3d position()
{

View File

@@ -15,7 +15,6 @@ import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.minecraft.client.renderer.model.ModelResourceLocation;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
@@ -47,8 +46,7 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade
@Override
public Vector3d getPosition()
{
BlockPos pos = turtle.getPosition();
return new Vector3d( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 );
return Vector3d.atCenterOf( turtle.getPosition() );
}
@Override

View File

@@ -5,9 +5,13 @@
*/
package dan200.computercraft.shared.util;
import net.minecraft.util.Direction;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullConsumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class CapabilityUtil
@@ -57,4 +61,21 @@ public final class CapabilityUtil
{
return !p.isPresent() ? null : p.orElseThrow( NullPointerException::new );
}
/**
* Find a capability, preferring the internal/null side but falling back to a given side if a mod doesn't support
* the internal one.
*
* @param provider The capability provider to get the capability from.
* @param capability The capability to get.
* @param side The side we'll fall back to.
* @param <T> The type of the underlying capability.
* @return The extracted capability, if present.
*/
@Nonnull
public static <T> LazyOptional<T> getCapability( ICapabilityProvider provider, Capability<T> capability, Direction side )
{
LazyOptional<T> cap = provider.getCapability( capability );
return cap.isPresent() ? cap : provider.getCapability( capability, side );
}
}

View File

@@ -1,348 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.network.*;
import net.minecraft.network.play.ServerPlayNetHandler;
import net.minecraft.network.play.client.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.common.util.FakePlayer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class FakeNetHandler extends ServerPlayNetHandler
{
public FakeNetHandler( @Nonnull FakePlayer player )
{
super( player.getLevel().getServer(), new FakeNetworkManager(), player );
}
@Override
public void tick()
{
}
@Override
public void disconnect( @Nonnull ITextComponent reason )
{
}
@Override
public void onDisconnect( @Nonnull ITextComponent reason )
{
}
@Override
public void send( @Nonnull IPacket<?> packet )
{
}
@Override
public void send( @Nonnull IPacket<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> whenSent )
{
}
@Override
public void handlePlayerInput( @Nonnull CInputPacket packet )
{
}
@Override
public void handleMoveVehicle( @Nonnull CMoveVehiclePacket packet )
{
}
@Override
public void handleAcceptTeleportPacket( @Nonnull CConfirmTeleportPacket packet )
{
}
@Override
public void handleSeenAdvancements( @Nonnull CSeenAdvancementsPacket packet )
{
}
@Override
public void handleCustomCommandSuggestions( @Nonnull CTabCompletePacket packet )
{
}
@Override
public void handleSetCommandBlock( @Nonnull CUpdateCommandBlockPacket packet )
{
}
@Override
public void handleSetCommandMinecart( @Nonnull CUpdateMinecartCommandBlockPacket packet )
{
}
@Override
public void handlePickItem( @Nonnull CPickItemPacket packet )
{
}
@Override
public void handleRenameItem( @Nonnull CRenameItemPacket packet )
{
}
@Override
public void handleSetBeaconPacket( @Nonnull CUpdateBeaconPacket packet )
{
}
@Override
public void handleSetStructureBlock( @Nonnull CUpdateStructureBlockPacket packet )
{
}
@Override
public void handleSetJigsawBlock( @Nonnull CUpdateJigsawBlockPacket packet )
{
}
@Override
public void handleSelectTrade( @Nonnull CSelectTradePacket packet )
{
}
@Override
public void handleEditBook( @Nonnull CEditBookPacket packet )
{
}
@Override
public void handleEntityTagQuery( @Nonnull CQueryEntityNBTPacket packet )
{
}
@Override
public void handleBlockEntityTagQuery( @Nonnull CQueryTileEntityNBTPacket packet )
{
}
@Override
public void handleMovePlayer( @Nonnull CPlayerPacket packet )
{
}
@Override
public void handlePlayerAction( @Nonnull CPlayerDiggingPacket packet )
{
}
@Override
public void handleUseItemOn( @Nonnull CPlayerTryUseItemOnBlockPacket packet )
{
}
@Override
public void handleUseItem( @Nonnull CPlayerTryUseItemPacket packet )
{
}
@Override
public void handleTeleportToEntityPacket( @Nonnull CSpectatePacket packet )
{
}
@Override
public void handleResourcePackResponse( @Nonnull CResourcePackStatusPacket packet )
{
}
@Override
public void handlePaddleBoat( @Nonnull CSteerBoatPacket packet )
{
}
@Override
public void handleSetCarriedItem( @Nonnull CHeldItemChangePacket packet )
{
}
@Override
public void handleChat( @Nonnull CChatMessagePacket packet )
{
}
@Override
public void handleAnimate( @Nonnull CAnimateHandPacket packet )
{
}
@Override
public void handlePlayerCommand( @Nonnull CEntityActionPacket packet )
{
}
@Override
public void handleInteract( @Nonnull CUseEntityPacket packet )
{
}
@Override
public void handleClientCommand( @Nonnull CClientStatusPacket packet )
{
}
@Override
public void handleContainerClose( @Nonnull CCloseWindowPacket packet )
{
}
@Override
public void handleContainerClick( @Nonnull CClickWindowPacket packet )
{
}
@Override
public void handlePlaceRecipe( @Nonnull CPlaceRecipePacket packet )
{
}
@Override
public void handleContainerButtonClick( @Nonnull CEnchantItemPacket packet )
{
}
@Override
public void handleSetCreativeModeSlot( @Nonnull CCreativeInventoryActionPacket packet )
{
}
@Override
public void handleContainerAck( @Nonnull CConfirmTransactionPacket packet )
{
}
@Override
public void handleSignUpdate( @Nonnull CUpdateSignPacket packet )
{
}
@Override
public void handleKeepAlive( @Nonnull CKeepAlivePacket packet )
{
}
@Override
public void handlePlayerAbilities( @Nonnull CPlayerAbilitiesPacket packet )
{
}
@Override
public void handleClientInformation( @Nonnull CClientSettingsPacket packet )
{
}
@Override
public void handleCustomPayload( @Nonnull CCustomPayloadPacket packet )
{
}
@Override
public void handleChangeDifficulty( @Nonnull CSetDifficultyPacket packet )
{
}
@Override
public void handleLockDifficulty( @Nonnull CLockDifficultyPacket packet )
{
}
private static class FakeNetworkManager extends NetworkManager
{
private INetHandler handler;
private ITextComponent closeReason;
FakeNetworkManager()
{
super( PacketDirection.CLIENTBOUND );
}
@Override
public void channelActive( @Nonnull ChannelHandlerContext context )
{
}
@Override
public void setProtocol( @Nonnull ProtocolType state )
{
}
@Override
public void channelInactive( @Nonnull ChannelHandlerContext context )
{
}
@Override
public void exceptionCaught( @Nonnull ChannelHandlerContext context, @Nonnull Throwable err )
{
}
@Override
protected void channelRead0( @Nonnull ChannelHandlerContext context, @Nonnull IPacket<?> packet )
{
}
@Override
public void setListener( @Nonnull INetHandler handler )
{
this.handler = handler;
}
@Override
public void send( @Nonnull IPacket<?> packet )
{
}
@Override
public void send( @Nonnull IPacket<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> whenSent )
{
}
@Override
public void tick()
{
}
@Override
public void disconnect( @Nonnull ITextComponent message )
{
closeReason = message;
}
@Nonnull
@Override
public INetHandler getPacketListener()
{
return handler;
}
@Nullable
@Override
public ITextComponent getDisconnectedReason()
{
return closeReason;
}
@Override
public void setReadOnly()
{
}
@Override
public void setupCompression( int threshold )
{
}
}
}

View File

@@ -45,21 +45,17 @@ public final class IDAssigner
return ServerLifecycleHooks.getCurrentServer().getWorldPath( FOLDER ).toFile();
}
private static MinecraftServer getCachedServer()
private static boolean hasServerChanged()
{
if( server == null ) return null;
if( server == null ) return true;
MinecraftServer currentServer = server.get();
if( currentServer == null ) return null;
if( currentServer != ServerLifecycleHooks.getCurrentServer() ) return null;
return currentServer;
return currentServer == null || currentServer != ServerLifecycleHooks.getCurrentServer();
}
public static synchronized int getNextId( String kind )
{
MinecraftServer currentServer = getCachedServer();
if( currentServer == null )
if( hasServerChanged() )
{
// The server has changed, refetch our ID map
server = new WeakReference<>( ServerLifecycleHooks.getCurrentServer() );
@@ -68,23 +64,22 @@ public final class IDAssigner
dir.mkdirs();
// Load our ID file from disk
Map<String, Integer> newIds = null;
idFile = new File( dir, "ids.json" ).toPath();
if( Files.isRegularFile( idFile ) )
{
try( Reader reader = Files.newBufferedReader( idFile, StandardCharsets.UTF_8 ) )
{
ids = GSON.fromJson( reader, ID_TOKEN );
newIds = GSON.fromJson( reader, ID_TOKEN );
}
catch( Exception e )
{
ComputerCraft.log.error( "Cannot load id file '" + idFile + "'", e );
ids = new HashMap<>();
}
}
else
{
ids = new HashMap<>();
}
if( newIds == null ) newIds = new HashMap<>();
ids = newIds;
}
Integer existing = ids.get( kind );

View File

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

View File

@@ -4,10 +4,16 @@
#define FONT_HEIGHT 9.0
uniform sampler2D u_font;
uniform int u_width;
uniform int u_height;
uniform usamplerBuffer u_tbo;
uniform vec3 u_palette[16];
layout(std140) uniform u_monitor {
vec3 u_palette[16];
int u_width;
int u_height;
ivec2 u_cursorPos;
int u_cursorColour;
};
uniform int u_cursorBlink;
in vec2 f_pos;
@@ -19,6 +25,10 @@ vec2 texture_corner(int index) {
return vec2(x, y);
}
vec4 recolour(vec4 texture, int colour) {
return vec4(texture.rgb * u_palette[colour], texture.rgba);
}
void main() {
vec2 term_pos = vec2(f_pos.x / FONT_WIDTH, f_pos.y / FONT_HEIGHT);
vec2 corner = floor(term_pos);
@@ -35,6 +45,12 @@ void main() {
int bg = int(texelFetch(u_tbo, index + 2).r);
vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT);
vec4 img = texture(u_font, (texture_corner(character) + pos) / 256.0);
colour = vec4(mix(u_palette[bg], img.rgb * u_palette[fg], img.a * mult), 1.0);
vec4 charTex = recolour(texture(u_font, (texture_corner(character) + pos) / 256.0), fg);
// Applies the cursor on top of the current character if we're blinking and in the current cursor's cell. We do it
// this funky way to avoid branches.
vec4 cursorTex = recolour(texture(u_font, (texture_corner(95) + pos) / 256.0), u_cursorColour); // 95 = '_'
vec4 img = mix(charTex, cursorTex, cursorTex.a * float(u_cursorBlink) * (u_cursorPos == cell ? 1.0 : 0.0));
colour = vec4(mix(u_palette[bg], img.rgb, img.a * mult), 1.0);
}

View File

@@ -1,7 +1,7 @@
{
"parent": "minecraft:recipes/root",
"rewards": {
"recipes": [ "computercraft:normal_turtle_normal" ]
"recipes": [ "computercraft:turtle_normal" ]
},
"criteria": {
"has_normal": {

View File

@@ -268,7 +268,7 @@ end
--- Combine a three-colour RGB value into one hexadecimal representation.
--
-- @tparam number r The red channel, should be between 0 and 1.
-- @tparam number g The red channel, should be between 0 and 1.
-- @tparam number g The green channel, should be between 0 and 1.
-- @tparam number b The blue channel, should be between 0 and 1.
-- @treturn number The combined hexadecimal colour.
-- @usage
@@ -291,7 +291,7 @@ end
--
-- @tparam number rgb The combined hexadecimal colour.
-- @treturn number The red channel, will be between 0 and 1.
-- @treturn number The red channel, will be between 0 and 1.
-- @treturn number The green channel, will be between 0 and 1.
-- @treturn number The blue channel, will be between 0 and 1.
-- @usage
-- ```lua

View File

@@ -217,9 +217,10 @@ end
channel. The message will be received by every device listening to rednet.
@param message The message to send. This should not contain coroutines or
functions, as they will be converted to @{nil}. @tparam[opt] string protocol
The "protocol" to send this message under. When using @{rednet.receive} one can
filter to only receive messages sent under a particular protocol.
functions, as they will be converted to @{nil}.
@tparam[opt] string protocol The "protocol" to send this message under.
When using @{rednet.receive} one can filter to only receive messages sent
under a particular protocol.
@see rednet.receive
@changed 1.6 Added protocol parameter.
@usage Broadcast the words "Hello, world!" to every computer using rednet.

View File

@@ -1,3 +1,25 @@
# New features in CC: Tweaked 1.100.5
* Generic peripherals now use capabilities on the given side if one isn't provided on the internal side.
* Improve performance of monitor rendering.
Several bug fixes:
* Various documentation fixes (bclindner, Hasaabitt)
* Speaker sounds are now correctly positioned on the centre of the speaker block.
# New features in CC: Tweaked 1.100.4
Several bug fixes:
* Fix the monitor watching blocking the main thread when chunks are slow to load.
# New features in CC: Tweaked 1.100.3
Several bug fixes:
* Fix client disconnect when uploading large files.
* Correctly handling empty computer ID file.
* Fix the normal turtle recipe not being unlocked.
* Remove turtle fake EntityType.
# New features in CC: Tweaked 1.100.2
Several bug fixes:

View File

@@ -1,7 +1,10 @@
New features in CC: Tweaked 1.100.2
New features in CC: Tweaked 1.100.5
* Generic peripherals now use capabilities on the given side if one isn't provided on the internal side.
* Improve performance of monitor rendering.
Several bug fixes:
* Fix wired modems swapping the modem/peripheral block state.
* Remove debugging logging line from `turtle.attack`.
* Various documentation fixes (bclindner, Hasaabitt)
* Speaker sounds are now correctly positioned on the centre of the speaker block.
Type "help changelog" to see the full version history.

View File

@@ -3,7 +3,7 @@
-- It allows you to @{run|start programs}, @{setCompletionFunction|add
-- completion for a program}, and much more.
--
-- @{shell} is not a "true" API. Instead, it is a standard program, which its
-- @{shell} is not a "true" API. Instead, it is a standard program, which injects its
-- API into the programs that it launches. This allows for multiple shells to
-- run at the same time, but means that the API is not available in the global
-- environment, and so is unavailable to other @{os.loadAPI|APIs}.
@@ -242,8 +242,8 @@ end
-- @since 1.2
-- @usage Locate the `hello` program.
--
-- shell.resolveProgram("hello")
-- -- => rom/programs/fun/hello.lua
-- shell.resolveProgram("hello")
-- -- => rom/programs/fun/hello.lua
function shell.resolveProgram(command)
expect(1, command, "string")
-- Substitute aliases firsts

View File

@@ -23,8 +23,6 @@ table.pretty-table th {
pre.highlight.highlight-lua {
position: relative;
background: var(--background-2);
padding: 2px;
}
.example-run {