From c0bdd4ff1dad41988ce2d51cfdb9e38fc68aca59 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Mon, 18 Jun 2018 22:09:24 +0100 Subject: [PATCH] Add basic support for fancy rendering of printouts - When held in first-person, single pages are displayed like a map. - When placed in an item frame, the page is drawn instead of the actual item. --- .../computercraft/client/gui/GuiPrintout.java | 47 ++-- .../proxy/ComputerCraftProxyClient.java | 2 + .../client/render/ItemPrintoutRenderer.java | 219 ++++++++++++++++++ .../resources/META-INF/computercraft_at.cfg | 4 + 4 files changed, 248 insertions(+), 24 deletions(-) create mode 100644 src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java diff --git a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java index 06d994150e..f37a57a822 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java @@ -20,10 +20,9 @@ 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; + public static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/printout.png" ); + public static final int X_SIZE = 172; + public static final int Y_SIZE = 209; private final boolean m_book; private final int m_pages; @@ -142,57 +141,57 @@ public void drawScreen(int mouseX, int mouseY, float f) // Draw the printout GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f ); - this.mc.getTextureManager().bindTexture( background ); + 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; + int startY = (height - Y_SIZE) / 2; + //int startX = (width - X_SIZE) / 2 - (m_page * 8); + int startX = (width - (X_SIZE + (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 - 8, startY - 8, X_SIZE + 48, 0, 12, Y_SIZE + 24); + drawTexturedModalRect( startX + X_SIZE + (m_pages - 1)*8 - 4, startY - 8, X_SIZE + 48 + 12, 0, 12, Y_SIZE + 24); - drawTexturedModalRect( startX, startY - 8, 0, ySize, xSize, 12); - drawTexturedModalRect( startX, startY + ySize - 4, 0, ySize + 12, xSize, 12); + drawTexturedModalRect( startX, startY - 8, 0, Y_SIZE, X_SIZE, 12); + drawTexturedModalRect( startX, startY + Y_SIZE - 4, 0, Y_SIZE + 12, X_SIZE, 12); for( int n=1; n=m_page; --n ) { - drawTexturedModalRect( startX + n*8 + (xSize - 12), startY, 24 + xSize, 0, 12, ySize); + drawTexturedModalRect( startX + n*8 + (X_SIZE - 12), startY, 24 + X_SIZE, 0, 12, Y_SIZE ); } - drawTexturedModalRect( startX + m_page*8 + xSize/2, startY, 24 + xSize / 2, 0, xSize / 2, ySize); + drawTexturedModalRect( startX + m_page*8 + X_SIZE /2, startY, 24 + X_SIZE / 2, 0, X_SIZE / 2, Y_SIZE ); } // Draw the text 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..c4703f63ff --- /dev/null +++ b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java @@ -0,0 +1,219 @@ +package dan200.computercraft.client.render; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.gui.FixedWidthFontRenderer; +import dan200.computercraft.client.gui.GuiPrintout; +import dan200.computercraft.core.terminal.TextBuffer; +import dan200.computercraft.shared.media.items.ItemPrintout; +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.ItemRenderer; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +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 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.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; + + // We only allow single pages to be viewed in-hand for now + if( ItemPrintout.getType( stack ) != ItemPrintout.Type.Single ) 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.38f, 0.38f, 0.38f ); + GlStateManager.translate( -0.5f, -0.5f, 0.0f ); + + drawPrintout( stack ); + + GlStateManager.enableLighting(); + } + + @SubscribeEvent + public void onRenderInFrame( RenderItemInFrameEvent event ) + { + ItemStack stack = event.getItem(); + if( stack.getItem() != ComputerCraft.Items.printout ) return; + + // We only allow single pages to be viewed in-hand for now + if( ItemPrintout.getType( stack ) != ItemPrintout.Type.Single ) 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.translate( -0.5f, -0.5f, 0.0f ); + + drawPrintout( stack ); + + GlStateManager.enableLighting(); + } + + private static void drawPrintout( ItemStack stack ) + { + int xMargin = 13; + int yMargin = 11; + + int width = LINE_MAX_LENGTH * FONT_WIDTH + xMargin * 2; + int height = LINES_PER_PAGE * FONT_HEIGHT + yMargin * 2; + int max = Math.max( height, width ); + + // 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 ); + + drawBackground( 0, 0, 0.01 ); + + FixedWidthFontRenderer fontRenderer = (FixedWidthFontRenderer) ComputerCraft.getFixedWidthFontRenderer(); + + String[] text = ItemPrintout.getText( stack ); + String[] colours = ItemPrintout.getColours( stack ); + for( int line = 0; line < LINES_PER_PAGE && line < text.length; ++line ) + { + fontRenderer.drawString( new TextBuffer( text[ line ] ), xMargin, yMargin + line * FONT_HEIGHT, new TextBuffer( colours[ line ] ), null, 0, 0, false, Palette.DEFAULT ); + } + } + + private static void drawBackground( double x, double y, double z ) + { + Minecraft mc = Minecraft.getMinecraft(); + mc.getTextureManager().bindTexture( GuiPrintout.BACKGROUND ); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX ); + buffer.pos( x, y + GuiPrintout.Y_SIZE, z ).tex( 24 / 256.0, GuiPrintout.Y_SIZE / 256.0 ).endVertex(); + buffer.pos( x + GuiPrintout.X_SIZE, y + GuiPrintout.Y_SIZE, z ).tex( (24 + GuiPrintout.X_SIZE) / 256.0, GuiPrintout.Y_SIZE / 256.0 ).endVertex(); + buffer.pos( x + GuiPrintout.X_SIZE, y, z ).tex( (24 + GuiPrintout.X_SIZE) / 256.0, 0 ).endVertex(); + buffer.pos( x, y, z ).tex( 24 / 256.0, 0 ).endVertex(); + tessellator.draw(); + } +} 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