diff --git a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java index 06d994150e..b297fbcc96 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java @@ -6,25 +6,19 @@ package dan200.computercraft.client.gui; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.media.inventory.ContainerHeldItem; import dan200.computercraft.shared.media.items.ItemPrintout; -import dan200.computercraft.shared.util.Palette; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.ResourceLocation; import org.lwjgl.input.Mouse; import java.io.IOException; +import static dan200.computercraft.client.render.PrintoutRenderer.*; + public class GuiPrintout extends GuiContainer { - private static final ResourceLocation background = new ResourceLocation( "computercraft", "textures/gui/printout.png" ); - - private static final int xSize = 172; - private static final int ySize = 209; - private final boolean m_book; private final int m_pages; private final TextBuffer[] m_text; @@ -34,23 +28,18 @@ public class GuiPrintout extends GuiContainer public GuiPrintout( ContainerHeldItem container ) { super( container ); - m_book = (ItemPrintout.getType( container.getStack() ) == ItemPrintout.Type.Book); String[] text = ItemPrintout.getText( container.getStack() ); m_text = new TextBuffer[ text.length ]; - for( int i=0; i 0 ) - { - m_page = m_page - 1; - } + // Left + if( m_page > 0 ) m_page--; } } @@ -106,21 +89,15 @@ public void handleMouseInput() throws IOException super.handleMouseInput(); int mouseWheelChange = Mouse.getEventDWheel(); - if (mouseWheelChange < 0) + if( mouseWheelChange < 0 ) { // Up - if( m_page < m_pages - 1 ) - { - m_page = m_page + 1; - } + if( m_page < m_pages - 1 ) m_page++; } - else if (mouseWheelChange > 0) + else if( mouseWheelChange > 0 ) { // Down - if( m_page > 0 ) - { - m_page = m_page - 1; - } + if( m_page > 0 ) m_page--; } } @@ -135,78 +112,20 @@ protected void drawGuiContainerBackgroundLayer( float var1, int var2, int var3 ) } @Override - public void drawScreen(int mouseX, int mouseY, float f) + public void drawScreen( int mouseX, int mouseY, float f ) { // Draw background + zLevel = zLevel - 1; drawDefaultBackground(); - + zLevel = zLevel + 1; + // Draw the printout GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f ); - this.mc.getTextureManager().bindTexture( background ); - - int startY = (height - ySize) / 2; - //int startX = (width - xSize) / 2 - (m_page * 8); - int startX = (width - (xSize + (m_pages - 1)*8)) / 2; - - if( m_book ) - { - // Border - drawTexturedModalRect( startX - 8, startY - 8, xSize + 48, 0, 12, ySize + 24); - drawTexturedModalRect( startX + xSize + (m_pages - 1)*8 - 4, startY - 8, xSize + 48 + 12, 0, 12, ySize + 24); - - drawTexturedModalRect( startX, startY - 8, 0, ySize, xSize, 12); - drawTexturedModalRect( startX, startY + ySize - 4, 0, ySize + 12, xSize, 12); - for( int n=1; n=m_page; --n ) - { - drawTexturedModalRect( startX + n*8 + (xSize - 12), startY, 24 + xSize, 0, 12, ySize); - } - drawTexturedModalRect( startX + m_page*8 + xSize/2, startY, 24 + xSize / 2, 0, xSize / 2, ySize); - } - // Draw the text - FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer)ComputerCraft.getFixedWidthFontRenderer(); - int x = startX + m_page * 8 + 13; - int y = startY + 11; - for( int line=0; line= 0 && lineIdx < m_text.length ) - { - fontRenderer.drawString( m_text[lineIdx], x, y, m_colours[lineIdx], null, 0, 0, false, Palette.DEFAULT ); - } - y = y + FixedWidthFontRenderer.FONT_HEIGHT; - } + int startY = (height - Y_SIZE) / 2; + int startX = (width - X_SIZE) / 2; + + drawBorder( startX, startY, zLevel, m_page, m_pages, m_book ); + drawText( startX + X_TEXT_MARGIN, startY + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * m_page, m_text, m_colours ); } } diff --git a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java index db2da93303..0c554f3670 100644 --- a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java +++ b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java @@ -8,6 +8,7 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.gui.*; +import dan200.computercraft.client.render.ItemPrintoutRenderer; import dan200.computercraft.client.render.TileEntityMonitorRenderer; import dan200.computercraft.shared.computer.blocks.ComputerState; import dan200.computercraft.shared.computer.blocks.TileComputer; @@ -442,6 +443,7 @@ private void registerForgeHandlers() { ForgeHandlers handlers = new ForgeHandlers(); MinecraftForge.EVENT_BUS.register( handlers ); + MinecraftForge.EVENT_BUS.register( new ItemPrintoutRenderer() ); } public class ForgeHandlers diff --git a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java new file mode 100644 index 0000000000..e3812a6121 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java @@ -0,0 +1,197 @@ +package dan200.computercraft.client.render; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.shared.media.items.ItemPrintout; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumHandSide; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.client.event.RenderItemInFrameEvent; +import net.minecraftforge.client.event.RenderSpecificHandEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +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.shared.media.items.ItemPrintout.LINES_PER_PAGE; +import static dan200.computercraft.shared.media.items.ItemPrintout.LINE_MAX_LENGTH; + +public class ItemPrintoutRenderer +{ + @SubscribeEvent + public void onRenderInHand( RenderSpecificHandEvent event ) + { + ItemStack stack = event.getItemStack(); + if( stack.getItem() != ComputerCraft.Items.printout ) return; + + event.setCanceled( true ); + + EntityPlayer player = Minecraft.getMinecraft().player; + + GlStateManager.pushMatrix(); + if( event.getHand() == EnumHand.MAIN_HAND && player.getHeldItemOffhand().isEmpty() ) + { + renderPrintoutFirstPersonCentre( + event.getInterpolatedPitch(), + event.getEquipProgress(), + event.getSwingProgress(), + stack + ); + } + else + { + renderPrintoutFirstPersonSide( + event.getHand() == EnumHand.MAIN_HAND ? player.getPrimaryHand() : player.getPrimaryHand().opposite(), + event.getEquipProgress(), + event.getSwingProgress(), + stack + ); + } + GlStateManager.popMatrix(); + } + + /** + * Renders a pocket computer to one side of the player. + * + * @param side The side to render on + * @param equipProgress The equip progress of this item + * @param swingProgress The swing progress of this item + * @param stack The stack to render + * @see ItemRenderer#renderMapFirstPersonSide(float, EnumHandSide, float, ItemStack) + */ + private void renderPrintoutFirstPersonSide( EnumHandSide side, float equipProgress, float swingProgress, ItemStack stack ) + { + Minecraft minecraft = Minecraft.getMinecraft(); + float offset = side == EnumHandSide.RIGHT ? 1f : -1f; + GlStateManager.translate( offset * 0.125f, -0.125f, 0f ); + + // If the player is not invisible then render a single arm + if( !minecraft.player.isInvisible() ) + { + GlStateManager.pushMatrix(); + GlStateManager.rotate( offset * 10f, 0f, 0f, 1f ); + minecraft.getItemRenderer().renderArmFirstPerson( equipProgress, swingProgress, side ); + GlStateManager.popMatrix(); + } + + // Setup the appropriate transformations. This is just copied from the + // corresponding method in ItemRenderer. + GlStateManager.pushMatrix(); + GlStateManager.translate( offset * 0.51f, -0.08f + equipProgress * -1.2f, -0.75f ); + float f1 = MathHelper.sqrt( swingProgress ); + float f2 = MathHelper.sin( f1 * (float) Math.PI ); + float f3 = -0.5f * f2; + float f4 = 0.4f * MathHelper.sin( f1 * ((float) Math.PI * 2f) ); + float f5 = -0.3f * MathHelper.sin( swingProgress * (float) Math.PI ); + GlStateManager.translate( offset * f3, f4 - 0.3f * f2, f5 ); + GlStateManager.rotate( f2 * -45f, 1f, 0f, 0f ); + GlStateManager.rotate( offset * f2 * -30f, 0f, 1f, 0f ); + + renderPrintoutFirstPerson( stack ); + + GlStateManager.popMatrix(); + } + + /** + * Render an item in the middle of the screen + * + * @param pitch The pitch of the player + * @param equipProgress The equip progress of this item + * @param swingProgress The swing progress of this item + * @param stack The stack to render + * @see ItemRenderer#renderMapFirstPerson(float, float, float) + */ + private void renderPrintoutFirstPersonCentre( float pitch, float equipProgress, float swingProgress, ItemStack stack ) + { + ItemRenderer itemRenderer = Minecraft.getMinecraft().getItemRenderer(); + + // Setup the appropriate transformations. This is just copied from the + // corresponding method in ItemRenderer. + float swingRt = MathHelper.sqrt( swingProgress ); + float tX = -0.2f * MathHelper.sin( swingProgress * (float) Math.PI ); + float tZ = -0.4f * MathHelper.sin( swingRt * (float) Math.PI ); + GlStateManager.translate( 0f, -tX / 2f, tZ ); + float pitchAngle = itemRenderer.getMapAngleFromPitch( pitch ); + GlStateManager.translate( 0f, 0.04f + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f ); + GlStateManager.rotate( pitchAngle * -85f, 1f, 0f, 0f ); + itemRenderer.renderArms(); + float rX = MathHelper.sin( swingRt * (float) Math.PI ); + GlStateManager.rotate( rX * 20f, 1f, 0f, 0f ); + GlStateManager.scale( 2f, 2f, 2f ); + + renderPrintoutFirstPerson( stack ); + } + + + private static void renderPrintoutFirstPerson( ItemStack stack ) + { + // Setup various transformations. Note that these are partially adapated from the corresponding method + // in ItemRenderer.renderMapFirstPerson + GlStateManager.disableLighting(); + + GlStateManager.rotate( 180f, 0f, 1f, 0f ); + GlStateManager.rotate( 180f, 0f, 0f, 1f ); + GlStateManager.scale( 0.42f, 0.42f, -0.42f ); + GlStateManager.translate( -0.5f, -0.48f, 0.0f ); + + drawPrintout( stack ); + + GlStateManager.enableLighting(); + } + + @SubscribeEvent + public void onRenderInFrame( RenderItemInFrameEvent event ) + { + ItemStack stack = event.getItem(); + if( stack.getItem() != ComputerCraft.Items.printout ) return; + + event.setCanceled( true ); + + GlStateManager.disableLighting(); + + // Move a little bit forward to ensure we're not clipping with the frame + GlStateManager.translate( 0.0f, 0.0f, -0.001f ); + GlStateManager.rotate( 180f, 0f, 0f, 1f ); + GlStateManager.scale( 0.95f, 0.95f, -0.95f ); + GlStateManager.translate( -0.5f, -0.5f, 0.0f ); + + drawPrintout( stack ); + + GlStateManager.enableLighting(); + } + + private static void drawPrintout( ItemStack stack ) + { + int pages = ItemPrintout.getPageCount( stack ); + boolean book = ItemPrintout.getType( stack ) == ItemPrintout.Type.Book; + + double width = LINE_MAX_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2; + double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2; + + // Non-books will be left aligned + if( !book ) width += offsetAt( pages ); + + double visualWidth = width, visualHeight = height; + + // Meanwhile books will be centred + if( book ) + { + visualWidth += 2 * COVER_SIZE + 2 * offsetAt( pages ); + visualHeight += 2 * COVER_SIZE; + } + + double max = Math.max( visualHeight, visualWidth ); + + // Scale the printout to fit correctly. + double scale = 1.0 / max; + GlStateManager.scale( scale, scale, scale ); + GlStateManager.translate( (max - width) / 2.0f, (max - height) / 2.0f, 0.0f ); + + drawBorder( 0, 0, -0.01, 0, pages, book ); + drawText( X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) ); + } +} diff --git a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java new file mode 100644 index 0000000000..da5e8ddb4e --- /dev/null +++ b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java @@ -0,0 +1,169 @@ +package dan200.computercraft.client.render; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.gui.FixedWidthFontRenderer; +import dan200.computercraft.core.terminal.TextBuffer; +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.util.ResourceLocation; +import org.lwjgl.opengl.GL11; + +import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; +import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE; + +public class PrintoutRenderer +{ + private static final ResourceLocation BG = new ResourceLocation( "computercraft", "textures/gui/printout.png" ); + private static final double BG_SIZE = 256.0; + + /** + * Width of a page + */ + public static final int X_SIZE = 172; + + /** + * Height of a page + */ + public static final int Y_SIZE = 209; + + /** + * Padding between the left and right of a page and the text + */ + public static final int X_TEXT_MARGIN = 13; + + /** + * Padding between the top and bottom of a page and the text + */ + public static final int Y_TEXT_MARGIN = 11; + + /** + * Width of the extra page texture + */ + private static final int X_FOLD_SIZE = 12; + + /** + * Size of the leather cover + */ + public static final int COVER_SIZE = 12; + + private static final int COVER_Y = Y_SIZE; + private static final int COVER_X = X_SIZE + 4 * X_FOLD_SIZE; + + public static void drawText( int x, int y, int start, TextBuffer[] text, TextBuffer[] colours ) + { + FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer(); + + for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line ) + { + fontRenderer.drawString( text[ start + line ], x, y + line * FONT_HEIGHT, colours[ start + line ], null, 0, 0, false, Palette.DEFAULT ); + } + } + + public static void drawText( int x, int y, int start, String[] text, String[] colours ) + { + GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f ); + GlStateManager.enableBlend(); + GlStateManager.enableTexture2D(); + + FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer(); + + for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line ) + { + fontRenderer.drawString( new TextBuffer( text[ start + line ] ), x, y + line * FONT_HEIGHT, new TextBuffer( colours[ start + line ] ), null, 0, 0, false, Palette.DEFAULT ); + } + } + + public static void drawBorder( double x, double y, double z, int page, int pages, boolean isBook ) + { + GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f ); + GlStateManager.enableBlend(); + GlStateManager.enableTexture2D(); + + Minecraft.getMinecraft().getTextureManager().bindTexture( BG ); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX ); + + int leftPages = page; + int rightPages = pages - page - 1; + + if( isBook ) + { + // Border + double offset = offsetAt( pages ); + final double left = x - 4 - offset; + final double right = x + X_SIZE + offset - 4; + + // Left and right border + drawTexture( buffer, left - 4, y - 8, z - 0.02, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); + drawTexture( buffer, right, y - 8, z - 0.02, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); + + // Draw centre panel (just stretched texture, sorry). + drawTexture( buffer, + x - offset, y, z - 0.02, X_SIZE + offset * 2, Y_SIZE, + COVER_X + COVER_SIZE / 2, COVER_SIZE, COVER_SIZE, Y_SIZE + ); + + double borderX = left; + while( borderX < right ) + { + double thisWidth = Math.min( right - borderX, X_SIZE ); + drawTexture( buffer, borderX, y - 8, z - 0.02, 0, COVER_Y, thisWidth, COVER_SIZE ); + drawTexture( buffer, borderX, y + Y_SIZE - 4, z - 0.02, 0, COVER_Y + COVER_SIZE, thisWidth, COVER_SIZE ); + borderX += thisWidth; + } + } + + // Left half + drawTexture( buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2, Y_SIZE ); + for( int n = 0; n <= leftPages; n++ ) + { + drawTexture( buffer, + x - offsetAt( n ), y, z - 1e-3 * n, + // Use the left "bold" fold for the outermost page + n == leftPages ? 0 : X_FOLD_SIZE, 0, + X_FOLD_SIZE, Y_SIZE + ); + } + + // Right half + drawTexture( buffer, x + X_SIZE / 2, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2, 0, X_SIZE / 2, Y_SIZE ); + for( int n = 0; n <= rightPages; n++ ) + { + drawTexture( buffer, + x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3 * 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 + ); + } + + tessellator.draw(); + } + + private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double u, double v, double width, double height ) + { + buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + height) / BG_SIZE ).endVertex(); + buffer.pos( x + width, y + height, z ).tex( (u + width) / BG_SIZE, (v + height) / BG_SIZE ).endVertex(); + buffer.pos( x + width, y, z ).tex( (u + width) / BG_SIZE, v / BG_SIZE ).endVertex(); + buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex(); + } + + private static void drawTexture( BufferBuilder buffer, double x, double y, double z, double width, double height, double u, double v, double tWidth, double tHeight ) + { + buffer.pos( x, y + height, z ).tex( u / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex(); + buffer.pos( x + width, y + height, z ).tex( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ).endVertex(); + buffer.pos( x + width, y, z ).tex( (u + tWidth) / BG_SIZE, v / BG_SIZE ).endVertex(); + buffer.pos( x, y, z ).tex( u / BG_SIZE, v / BG_SIZE ).endVertex(); + } + + public static double offsetAt( int page ) + { + return 32 * (1 - Math.pow( 1.2, -page )); + } +} diff --git a/src/main/resources/META-INF/computercraft_at.cfg b/src/main/resources/META-INF/computercraft_at.cfg index 48f245c203..e25cd87c64 100644 --- a/src/main/resources/META-INF/computercraft_at.cfg +++ b/src/main/resources/META-INF/computercraft_at.cfg @@ -1,3 +1,7 @@ # RecordMedia (and related methods) public net.minecraft.item.ItemRecord field_185076_b # sound public net.minecraft.item.ItemRecord field_185077_c # displayName +# ItemPocketRenderer +public net.minecraft.client.renderer.ItemRenderer func_187466_c()V # renderArms +public net.minecraft.client.renderer.ItemRenderer func_178100_c(F)F # getMapAngleFromPitch +public net.minecraft.client.renderer.ItemRenderer func_187456_a(FFLnet/minecraft/util/EnumHandSide;)V # renderArmFirstPerson