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

Rework rendering of in-hand pocket computers (#215)

Rendering an item worked in principle, but had several caveats:
 - The terminal did not fit well within the item's texture, so we had a
   rather large border.
 - The "correctness" of this was very tied to Minecraft's item rendering
   code. This changed a little in 1.13, causing problems like #208.

Instead we effectively reuse the computer GUI rendering code, though
also handling coloured pocket computers and rendering the modem light.

This fixes #208, and hopefully fixes #212.
This commit is contained in:
SquidDev 2019-05-26 15:24:37 +01:00 committed by GitHub
parent b11beb508b
commit 114f913bf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 188 additions and 105 deletions

View File

@ -24,9 +24,10 @@ import java.io.IOException;
public class GuiComputer extends GuiContainer
{
private static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/corners.png" );
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( "computercraft", "textures/gui/corners_advanced.png" );
private static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( "computercraft", "textures/gui/corners_command.png" );
public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners.png" );
public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" );
public static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_command.png" );
public static final ResourceLocation BACKGROUND_COLOUR = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_colour.png" );
private final ComputerFamily m_family;
private final ClientComputer m_computer;
@ -158,7 +159,7 @@ public class GuiComputer extends GuiContainer
{
case Normal:
default:
mc.getTextureManager().bindTexture( BACKGROUND );
mc.getTextureManager().bindTexture( BACKGROUND_NORMAL );
break;
case Advanced:
mc.getTextureManager().bindTexture( BACKGROUND_ADVANCED );
@ -169,12 +170,12 @@ public class GuiComputer extends GuiContainer
}
drawTexturedModalRect( startX - 12, startY - 12, 12, 28, 12, 12 );
drawTexturedModalRect( startX - 12, endY, 12, 40, 12, 16 );
drawTexturedModalRect( startX - 12, endY, 12, 40, 12, 12 );
drawTexturedModalRect( endX, startY - 12, 24, 28, 12, 12 );
drawTexturedModalRect( endX, endY, 24, 40, 12, 16 );
drawTexturedModalRect( endX, endY, 24, 40, 12, 12 );
drawTexturedModalRect( startX, startY - 12, 0, 0, endX - startX, 12 );
drawTexturedModalRect( startX, endY, 0, 12, endX - startX, 16 );
drawTexturedModalRect( startX, endY, 0, 12, endX - startX, 12 );
drawTexturedModalRect( startX - 12, startY, 0, 28, 12, endY - startY );
drawTexturedModalRect( endX, startY, 36, 28, 12, endY - startY );

View File

@ -12,25 +12,24 @@ import dan200.computercraft.client.gui.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
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 dan200.computercraft.shared.util.Palette;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderItem;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.item.ItemStack;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
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.gui.FixedWidthFontRenderer.*;
import static dan200.computercraft.client.gui.GuiComputer.*;
/**
* Emulates map rendering for pocket computers
@ -38,6 +37,10 @@ import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH;
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Side.CLIENT )
public final class ItemPocketRenderer extends ItemMapLikeRenderer
{
private static final int MARGIN = 2;
private static final int FRAME = 12;
private static final int LIGHT_HEIGHT = 8;
private static final ItemPocketRenderer INSTANCE = new ItemPocketRenderer();
private ItemPocketRenderer()
@ -57,115 +60,194 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer
@Override
protected void renderItem( ItemStack stack )
{
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
Terminal terminal = computer == null ? null : computer.getTerminal();
int termWidth, termHeight;
if( terminal == null )
{
termWidth = ComputerCraft.terminalWidth_pocketComputer;
termHeight = ComputerCraft.terminalHeight_pocketComputer;
}
else
{
termWidth = terminal.getWidth();
termHeight = terminal.getHeight();
}
int width = termWidth * FONT_WIDTH + MARGIN * 2;
int height = termHeight * FONT_HEIGHT + MARGIN * 2;
// Setup various transformations. Note that these are partially adapted from the corresponding method
// in ItemRenderer
GlStateManager.pushMatrix();
GlStateManager.disableLighting();
GlStateManager.disableDepth();
GlStateManager.rotate( 180f, 0f, 1f, 0f );
GlStateManager.rotate( 180f, 0f, 0f, 1f );
GlStateManager.scale( 0.5, 0.5, 0.5 );
ClientComputer computer = ItemPocketComputer.createClientComputer( stack );
double scale = 0.75 / Math.max( width + FRAME * 2, height + FRAME * 2 + LIGHT_HEIGHT );
GlStateManager.scale( scale, scale, 0 );
GlStateManager.translate( -0.5 * width, -0.5 * height, 0 );
// Render the main frame
ComputerFamily family = ComputerCraft.Items.pocketComputer.getFamily( stack );
int frameColour = ComputerCraft.Items.pocketComputer.getColour( stack );
renderFrame( family, frameColour, width, height );
// Render the light
int lightColour = ItemPocketComputer.getLightState( stack );
if( lightColour == -1 ) lightColour = Colour.Black.getHex();
renderLight( lightColour, width, height );
if( computer != null && terminal != null )
{
// First render the background item. We use the item's model rather than a direct texture as this ensures
// we display the pocket light and other such decorations.
GlStateManager.pushMatrix();
// If we've a computer and terminal then attempt to render it.
renderTerminal( terminal, !computer.isColour(), width, height );
}
else
{
// Otherwise render a plain background
Minecraft.getMinecraft().getTextureManager().bindTexture( BACKGROUND );
GlStateManager.scale( 1.0f, -1.0f, 1.0f );
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
Minecraft minecraft = Minecraft.getMinecraft();
TextureManager textureManager = minecraft.getTextureManager();
RenderItem renderItem = minecraft.getRenderItem();
// Copy of RenderItem#renderItemModelIntoGUI but without the translation or scaling
textureManager.bindTexture( TextureMap.LOCATION_BLOCKS_TEXTURE );
textureManager.getTexture( TextureMap.LOCATION_BLOCKS_TEXTURE ).setBlurMipmap( false, false );
GlStateManager.enableRescaleNormal();
GlStateManager.enableAlpha();
GlStateManager.alphaFunc( GL11.GL_GREATER, 0.1F );
GlStateManager.enableBlend();
GlStateManager.blendFunc( GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA );
GlStateManager.color( 1.0F, 1.0F, 1.0F, 1.0F );
IBakedModel baked = renderItem.getItemModelWithOverrides( stack, null, null );
baked = ForgeHooksClient.handleCameraTransforms( baked, ItemCameraTransforms.TransformType.GUI, false );
renderItem.renderItem( stack, baked );
GlStateManager.disableAlpha();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
Colour black = Colour.Black;
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION );
renderTexture( buffer, 0, 0, 0, 0, width, height, black.getR(), black.getG(), black.getB() );
tessellator.draw();
}
// If we've a computer and terminal then attempt to render it.
if( computer != null )
GlStateManager.enableDepth();
GlStateManager.enableLighting();
GlStateManager.popMatrix();
}
private static void renderFrame( ComputerFamily family, int colour, int width, int height )
{
Minecraft.getMinecraft().getTextureManager().bindTexture( colour != -1
? BACKGROUND_COLOUR
: family == ComputerFamily.Normal ? BACKGROUND_NORMAL : BACKGROUND_ADVANCED
);
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.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR );
// Top left, middle, right
renderTexture( buffer, -FRAME, -FRAME, 12, 28, FRAME, FRAME, r, g, b );
renderTexture( buffer, 0, -FRAME, 0, 0, width, FRAME, r, g, b );
renderTexture( buffer, width, -FRAME, 24, 28, FRAME, FRAME, r, g, b );
// Left and bright border
renderTexture( buffer, -FRAME, 0, 0, 28, FRAME, height, r, g, b );
renderTexture( buffer, width, 0, 36, 28, FRAME, height, r, g, b );
// Bottom left, middle, right. We do this in three portions: the top inner corners, an extended region for
// lights, and then the bottom outer corners.
renderTexture( buffer, -FRAME, height, 12, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, 0, height, 0, 12, width, FRAME / 2, r, g, b );
renderTexture( buffer, width, height, 24, 40, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, -FRAME, height + FRAME / 2, 12, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, 0, height + FRAME / 2, 0, 16, width, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, width, height + FRAME / 2, 24, 44, FRAME, LIGHT_HEIGHT, FRAME, 4, r, g, b );
renderTexture( buffer, -FRAME, height + LIGHT_HEIGHT + FRAME / 2, 12, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
renderTexture( buffer, 0, height + LIGHT_HEIGHT + FRAME / 2, 0, 12 + FRAME / 2, width, FRAME / 2, r, g, b );
renderTexture( buffer, width, height + LIGHT_HEIGHT + FRAME / 2, 24, 40 + FRAME / 2, FRAME, FRAME / 2, r, g, b );
tessellator.draw();
}
private static void renderLight( int colour, int width, int height )
{
GlStateManager.enableBlend();
GlStateManager.disableTexture2D();
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.getBuffer();
buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR );
buffer.pos( width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width, height + LIGHT_HEIGHT + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
buffer.pos( width - LIGHT_HEIGHT * 2, height + FRAME / 2.0f, 0.0D ).color( r, g, b, 1.0f ).endVertex();
tessellator.draw();
GlStateManager.enableTexture2D();
}
private static void renderTerminal( Terminal terminal, boolean greyscale, int width, int height )
{
synchronized( terminal )
{
Terminal terminal = computer.getTerminal();
if( terminal != null )
int termWidth = terminal.getWidth();
int termHeight = terminal.getHeight();
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
Palette palette = terminal.getPalette();
// Render top/bottom borders
TextBuffer emptyLine = new TextBuffer( ' ', termWidth );
fontRenderer.drawString(
emptyLine, MARGIN, 0,
terminal.getTextColourLine( 0 ), terminal.getBackgroundColourLine( 0 ), MARGIN, MARGIN, greyscale, palette
);
fontRenderer.drawString(
emptyLine, MARGIN, 2 * MARGIN + (termHeight - 1) * FixedWidthFontRenderer.FONT_HEIGHT,
terminal.getTextColourLine( termHeight - 1 ), terminal.getBackgroundColourLine( termHeight - 1 ), MARGIN, MARGIN, greyscale, palette
);
// Render the actual text
for( int line = 0; line < termWidth; line++ )
{
synchronized( terminal )
{
GlStateManager.pushMatrix();
GlStateManager.disableDepth();
TextBuffer text = terminal.getLine( line );
TextBuffer colour = terminal.getTextColourLine( line );
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
fontRenderer.drawString(
text, MARGIN, MARGIN + line * FONT_HEIGHT,
colour, backgroundColour, MARGIN, MARGIN, greyscale, palette
);
}
// Reset the position to be at the top left corner of the pocket computer
// Note we translate towards the screen slightly too.
GlStateManager.translate( -8 / 16.0, -8 / 16.0, 0.5 / 16.0 );
// Translate to the top left of the screen.
GlStateManager.translate( 4 / 16.0, 3 / 16.0, 0 );
// Work out the scaling required to resize the terminal in order to fit on the computer
final int margin = 2;
int tw = terminal.getWidth();
int th = terminal.getHeight();
int width = tw * FONT_WIDTH + margin * 2;
int height = th * FONT_HEIGHT + margin * 2;
int max = Math.max( height, width );
// The grid is 8 * 8 wide, so we start with a base of 1/2 (8 / 16).
double scale = 1.0 / 2.0 / max;
GlStateManager.scale( scale, scale, scale );
// The margin/start positions are determined in order for the terminal to be centred.
int startX = (max - width) / 2 + margin;
int startY = (max - height) / 2 + margin;
FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance();
boolean greyscale = !computer.isColour();
Palette palette = terminal.getPalette();
// Render the actual text
for( int line = 0; line < th; line++ )
{
TextBuffer text = terminal.getLine( line );
TextBuffer colour = terminal.getTextColourLine( line );
TextBuffer backgroundColour = terminal.getBackgroundColourLine( line );
fontRenderer.drawString(
text, startX, startY + line * FONT_HEIGHT,
colour, backgroundColour, margin, margin, greyscale, palette
);
}
// And render the cursor;
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
tx >= 0 && ty >= 0 && tx < tw && ty < th )
{
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
new TextBuffer( '_', 1 ), startX + FONT_WIDTH * tx, startY + FONT_HEIGHT * ty,
cursorColour, null, 0, 0, greyscale, palette
);
}
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
// And render the cursor;
int tx = terminal.getCursorX(), ty = terminal.getCursorY();
if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() &&
tx >= 0 && ty >= 0 && tx < termWidth && ty < termHeight )
{
TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 );
fontRenderer.drawString(
new TextBuffer( '_', 1 ), MARGIN + FONT_WIDTH * tx, MARGIN + FONT_HEIGHT * ty,
cursorColour, null, 0, 0, greyscale, palette
);
}
}
}
GlStateManager.enableLighting();
private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b )
{
renderTexture( builder, x, y, textureX, textureY, width, height, width, height, r, g, b );
}
private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, int textureWidth, int textureHeight, float r, float g, float b )
{
float scale = 1 / 255.0f;
builder.pos( x, y + height, 0 ).tex( textureX * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x + width, y + height, 0 ).tex( (textureX + textureWidth) * scale, (textureY + textureHeight) * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x + width, y, 0 ).tex( (textureX + textureWidth) * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
builder.pos( x, y, 0 ).tex( textureX * scale, textureY * scale ).color( r, g, b, 1.0f ).endVertex();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB