/* * This file is part of ComputerCraft - http://www.computercraft.info * Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ package dan200.computercraft.client.render; import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.FrameInfo; 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.Tessellator; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.item.ItemStack; 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.*; import static dan200.computercraft.client.gui.GuiComputer.*; /** * Emulates map rendering for pocket computers. */ @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() { } @SubscribeEvent public static void renderItem( RenderSpecificHandEvent event ) { ItemStack stack = event.getItemStack(); if( !(stack.getItem() instanceof ItemPocketComputer) ) return; event.setCanceled( true ); INSTANCE.renderItemFirstPerson( event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack() ); } @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 ); 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 ) { // 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 ); Tessellator tessellator = Tessellator.getInstance(); BufferBuilder buffer = tessellator.getBuffer(); 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(); } 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 ) { 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++ ) { 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 ); } // 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 ); } } } 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(); } }