001package net.minecraftforge.client;
002
003import java.util.HashMap;
004import java.util.Random;
005import java.util.TreeSet;
006
007import org.lwjgl.opengl.GL11;
008import org.lwjgl.opengl.GL12;
009
010import cpw.mods.fml.client.FMLClientHandler;
011
012import net.minecraft.client.Minecraft;
013import net.minecraft.block.Block;
014import net.minecraft.entity.item.EntityItem;
015import net.minecraft.entity.EntityLiving;
016import net.minecraft.entity.player.EntityPlayer;
017import net.minecraft.client.texturepacks.ITexturePack;
018import net.minecraft.item.Item;
019import net.minecraft.item.ItemBlock;
020import net.minecraft.item.ItemStack;
021import net.minecraft.util.MathHelper;
022import net.minecraft.util.MovingObjectPosition;
023import net.minecraft.client.renderer.RenderBlocks;
024import net.minecraft.client.renderer.RenderEngine;
025import net.minecraft.client.renderer.RenderGlobal;
026import net.minecraft.client.renderer.Tessellator;
027import net.minecraft.client.renderer.entity.RenderItem;
028import net.minecraftforge.client.event.DrawBlockHighlightEvent;
029import net.minecraftforge.client.event.RenderWorldLastEvent;
030import net.minecraftforge.client.event.TextureLoadEvent;
031import net.minecraftforge.common.IArmorTextureProvider;
032import net.minecraftforge.common.MinecraftForge;
033import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*;
034import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*;
035
036public class ForgeHooksClient
037{
038    private static class TesKey implements Comparable<TesKey>
039    {
040        public final int texture, subid;
041        public TesKey(int textureID, int subID)
042        {
043            texture = textureID;
044            subid = subID;
045        }
046
047        public int compareTo(TesKey key)
048        {
049            if (subid == key.subid)
050            {
051                return texture - key.texture;
052            }
053            return subid - key.subid;
054        }
055
056        public boolean equals(Object obj)
057        {
058            return compareTo((TesKey)obj) == 0;
059        }
060
061        public int hashCode()
062        {
063            return texture + 31 * subid;
064        }
065    }
066
067    public static HashMap<TesKey, Tessellator> tessellators = new HashMap<TesKey, Tessellator>();
068    public static HashMap<String, Integer> textures = new HashMap<String, Integer>();
069    public static TreeSet<TesKey> renderTextures = new TreeSet<TesKey>();
070    public static Tessellator defaultTessellator = null;
071    public static boolean inWorld = false;
072    public static HashMap<TesKey, IRenderContextHandler> renderHandlers = new HashMap<TesKey, IRenderContextHandler>();
073    public static IRenderContextHandler unbindContext = null;
074
075    protected static void registerRenderContextHandler(String texture, int subID, IRenderContextHandler handler)
076    {
077        Integer texID = textures.get(texture);
078        if (texID == null)
079        {
080            texID = engine().getTexture(texture);
081            textures.put(texture, texID);
082        }
083        renderHandlers.put(new TesKey(texID, subID), handler);
084    }
085
086    static RenderEngine engine()
087    {
088        return FMLClientHandler.instance().getClient().renderEngine;
089    }
090    public static void bindTexture(String texture, int subID)
091    {
092        Integer texID = textures.get(texture);
093        if (texID == null)
094        {
095            texID = engine().getTexture(texture);
096            textures.put(texture, texID);
097        }
098        if (!inWorld)
099        {
100            if (unbindContext != null)
101            {
102                unbindContext.afterRenderContext();
103                unbindContext = null;
104            }
105            if (Tessellator.instance.isDrawing)
106            {
107                int mode = Tessellator.instance.drawMode;
108                Tessellator.instance.draw();
109                Tessellator.instance.startDrawing(mode);
110            }
111            GL11.glBindTexture(GL11.GL_TEXTURE_2D, texID);
112            unbindContext = renderHandlers.get(new TesKey(texID, subID));
113            if (unbindContext != null)
114            {
115                unbindContext.beforeRenderContext();
116            }
117            return;
118        }
119        bindTessellator(texID, subID);
120    }
121
122    public static void unbindTexture()
123    {
124        if (inWorld)
125        {
126            Tessellator.instance = defaultTessellator;
127        }
128        else
129        {
130            if (Tessellator.instance.isDrawing)
131            {
132                int mode = Tessellator.instance.drawMode;
133                Tessellator.instance.draw();
134                if (unbindContext != null)
135                {
136                    unbindContext.afterRenderContext();
137                    unbindContext = null;
138                }
139                Tessellator.instance.startDrawing(mode);
140            }
141            GL11.glBindTexture(GL11.GL_TEXTURE_2D, engine().getTexture("/terrain.png"));
142            return;
143        }
144    }
145
146    protected static void bindTessellator(int texture, int subID)
147    {
148        TesKey key = new TesKey(texture, subID);
149        Tessellator tess = tessellators.get(key);
150
151        if (tess == null)
152        {
153            tess = new Tessellator();
154            tess.textureID = texture;
155            tessellators.put(key, tess);
156        }
157
158        if (inWorld && !renderTextures.contains(key))
159        {
160            renderTextures.add(key);
161            tess.startDrawingQuads();
162            tess.setTranslation(defaultTessellator.xOffset, defaultTessellator.yOffset, defaultTessellator.zOffset);
163        }
164
165        Tessellator.instance = tess;
166    }
167
168    static int renderPass = -1;
169    public static void beforeRenderPass(int pass)
170    {
171        renderPass = pass;
172        defaultTessellator = Tessellator.instance;
173        Tessellator.renderingWorldRenderer = true;
174        GL11.glBindTexture(GL11.GL_TEXTURE_2D, engine().getTexture("/terrain.png"));
175        renderTextures.clear();
176        inWorld = true;
177    }
178
179    public static void afterRenderPass(int pass)
180    {
181        renderPass = -1;
182        inWorld = false;
183        for (TesKey info : renderTextures)
184        {
185            IRenderContextHandler handler = renderHandlers.get(info);
186            GL11.glBindTexture(GL11.GL_TEXTURE_2D, info.texture);
187            Tessellator tess = tessellators.get(info);
188            if (handler == null)
189            {
190                tess.draw();
191            }
192            else
193            {
194                Tessellator.instance = tess;
195                handler.beforeRenderContext();
196                tess.draw();
197                handler.afterRenderContext();
198            }
199        }
200        GL11.glBindTexture(GL11.GL_TEXTURE_2D, engine().getTexture("/terrain.png"));
201        Tessellator.renderingWorldRenderer = false;
202        Tessellator.instance = defaultTessellator;
203    }
204
205    public static void beforeBlockRender(Block block, RenderBlocks render)
206    {
207        if (!block.isDefaultTexture && render.overrideBlockTexture == -1)
208        {
209            bindTexture(block.getTextureFile(), 0);
210        }
211    }
212
213    public static void afterBlockRender(Block block, RenderBlocks render)
214    {
215        if (!block.isDefaultTexture && render.overrideBlockTexture == -1)
216        {
217            unbindTexture();
218        }
219    }
220
221    public static String getArmorTexture(ItemStack armor, String _default)
222    {
223        if (armor.getItem() instanceof IArmorTextureProvider)
224        {
225            return ((IArmorTextureProvider)armor.getItem()).getArmorTextureFile(armor);
226        }
227        return _default;
228    }
229
230    public static boolean renderEntityItem(EntityItem entity, ItemStack item, float bobing, float rotation, Random random, RenderEngine engine, RenderBlocks renderBlocks)
231    {
232        IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, ENTITY);
233        if (customRenderer == null)
234        {
235            return false;
236        }
237
238        if (customRenderer.shouldUseRenderHelper(ENTITY, item, ENTITY_ROTATION))
239        {
240            GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F);
241        }
242        if (!customRenderer.shouldUseRenderHelper(ENTITY, item, ENTITY_BOBBING))
243        {
244            GL11.glTranslatef(0.0F, -bobing, 0.0F);
245        }
246        boolean is3D = customRenderer.shouldUseRenderHelper(ENTITY, item, BLOCK_3D);
247
248        if (item.getItem() instanceof ItemBlock && (is3D || RenderBlocks.renderItemIn3d(Block.blocksList[item.itemID].getRenderType())))
249        {
250            engine.bindTexture(engine.getTexture(item.getItem().getTextureFile()));
251            int renderType = Block.blocksList[item.itemID].getRenderType();
252            float scale = (renderType == 1 || renderType == 19 || renderType == 12 || renderType == 2 ? 0.5F : 0.25F);
253
254            if (RenderItem.field_82407_g)
255            {
256                GL11.glScalef(1.25F, 1.25F, 1.25F);
257                GL11.glTranslatef(0.0F, 0.05F, 0.0F);
258                GL11.glRotatef(-90.0F, 0.0F, 1.0F, 0.0F);
259            }
260
261            GL11.glScalef(scale, scale, scale);
262            
263            int size = item.stackSize;
264            int count = (size > 20 ? 4 : (size > 5 ? 3 : (size > 1 ? 2 : 1)));
265
266            for(int j = 0; j < count; j++)
267            {
268                GL11.glPushMatrix();
269                if (j > 0)
270                {
271                    GL11.glTranslatef(
272                        ((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / 0.5F,
273                        ((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / 0.5F,
274                        ((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / 0.5F);
275                }
276                customRenderer.renderItem(ENTITY, item, renderBlocks, entity);
277                GL11.glPopMatrix();
278            }
279        }
280        else
281        {
282                engine.bindTexture(engine.getTexture(item.getItem().getTextureFile()));
283            GL11.glScalef(0.5F, 0.5F, 0.5F);
284            customRenderer.renderItem(ENTITY, item, renderBlocks, entity);
285        }
286        return true;
287    }
288
289    public static boolean renderInventoryItem(RenderBlocks renderBlocks, RenderEngine engine, ItemStack item, boolean inColor, float zLevel, float x, float y)
290    {
291        IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, INVENTORY);
292        if (customRenderer == null)
293        {
294                return false;
295        }
296
297        engine.bindTexture(engine.getTexture(Item.itemsList[item.itemID].getTextureFile()));
298        if (customRenderer.shouldUseRenderHelper(INVENTORY, item, INVENTORY_BLOCK))
299        {
300            GL11.glPushMatrix();
301            GL11.glTranslatef(x - 2, y + 3, -3.0F + zLevel);
302            GL11.glScalef(10F, 10F, 10F);
303            GL11.glTranslatef(1.0F, 0.5F, 1.0F);
304            GL11.glScalef(1.0F, 1.0F, -1F);
305            GL11.glRotatef(210F, 1.0F, 0.0F, 0.0F);
306            GL11.glRotatef(45F, 0.0F, 1.0F, 0.0F);
307
308            if(inColor)
309            {
310                int color = Item.itemsList[item.itemID].getColorFromItemStack(item, 0);
311                float r = (float)(color >> 16 & 0xff) / 255F;
312                float g = (float)(color >> 8 & 0xff) / 255F;
313                float b = (float)(color & 0xff) / 255F;
314                GL11.glColor4f(r, g, b, 1.0F);
315            }
316
317            GL11.glRotatef(-90F, 0.0F, 1.0F, 0.0F);
318            renderBlocks.useInventoryTint = inColor;
319            customRenderer.renderItem(INVENTORY, item, renderBlocks);
320            renderBlocks.useInventoryTint = true;
321            GL11.glPopMatrix();
322        }
323        else
324        {
325            GL11.glDisable(GL11.GL_LIGHTING);
326            GL11.glPushMatrix();
327            GL11.glTranslatef(x, y, -3.0F + zLevel);
328
329            if (inColor)
330            {
331                int color = Item.itemsList[item.itemID].getColorFromItemStack(item, 0);
332                float r = (float)(color >> 16 & 255) / 255.0F;
333                float g = (float)(color >> 8 & 255) / 255.0F;
334                float b = (float)(color & 255) / 255.0F;
335                GL11.glColor4f(r, g, b, 1.0F);
336            }
337
338            customRenderer.renderItem(INVENTORY, item, renderBlocks);
339            GL11.glPopMatrix();
340            GL11.glEnable(GL11.GL_LIGHTING);
341        }
342        return true;
343    }
344
345    public static void renderEquippedItem(IItemRenderer customRenderer, RenderBlocks renderBlocks, EntityLiving entity, ItemStack item)
346    {
347        if (customRenderer.shouldUseRenderHelper(EQUIPPED, item, EQUIPPED_BLOCK))
348        {
349            GL11.glPushMatrix();
350            GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
351            customRenderer.renderItem(EQUIPPED, item, renderBlocks, entity);
352            GL11.glPopMatrix();
353        }
354        else
355        {
356            GL11.glPushMatrix();
357            GL11.glEnable(GL12.GL_RESCALE_NORMAL);
358            GL11.glTranslatef(0.0F, -0.3F, 0.0F);
359            GL11.glScalef(1.5F, 1.5F, 1.5F);
360            GL11.glRotatef(50.0F, 0.0F, 1.0F, 0.0F);
361            GL11.glRotatef(335.0F, 0.0F, 0.0F, 1.0F);
362            GL11.glTranslatef(-0.9375F, -0.0625F, 0.0F);
363            customRenderer.renderItem(EQUIPPED, item, renderBlocks, entity);
364            GL11.glDisable(GL12.GL_RESCALE_NORMAL);
365            GL11.glPopMatrix();
366        }
367    }
368
369    //Optifine Helper Functions u.u, these are here specifically for Optifine
370    //Note: When using Optfine, these methods are invoked using reflection, which
371    //incurs a major performance penalty.
372    public static void orientBedCamera(Minecraft mc, EntityLiving entity)
373    {
374        int x = MathHelper.floor_double(entity.posX);
375        int y = MathHelper.floor_double(entity.posY);
376        int z = MathHelper.floor_double(entity.posZ);
377        Block block = Block.blocksList[mc.theWorld.getBlockId(x, y, z)];
378
379        if (block != null && block.isBed(mc.theWorld, x, y, z, entity))
380        {
381            int var12 = block.getBedDirection(mc.theWorld, x, y, z);
382            GL11.glRotatef((float)(var12 * 90), 0.0F, 1.0F, 0.0F);
383        }
384    }
385
386    public static boolean onDrawBlockHighlight(RenderGlobal context, EntityPlayer player, MovingObjectPosition target, int subID, ItemStack currentItem, float partialTicks)
387    {
388        return MinecraftForge.EVENT_BUS.post(new DrawBlockHighlightEvent(context, player, target, subID, currentItem, partialTicks));
389    }
390
391    public static void dispatchRenderLast(RenderGlobal context, float partialTicks)
392    {
393        MinecraftForge.EVENT_BUS.post(new RenderWorldLastEvent(context, partialTicks));
394    }
395
396    public static void onTextureLoad(String texture, ITexturePack pack)
397    {
398        MinecraftForge.EVENT_BUS.post(new TextureLoadEvent(texture, pack));
399    }
400
401    /**
402     * This is added for Optifine's convenience. And to explode if a ModMaker is developing.
403     * @param texture
404     */
405    public static void onTextureLoadPre(String texture)
406    {
407        if (Tessellator.renderingWorldRenderer)
408        {
409            String msg = String.format("Warning: Texture %s not preloaded, will cause render glitches!", texture);
410            System.out.println(msg);
411            if (Tessellator.class.getPackage() != null)
412            {
413                if (Tessellator.class.getPackage().getName().startsWith("net.minecraft."))
414                {
415                    Minecraft mc = FMLClientHandler.instance().getClient();
416                    if (mc.ingameGUI != null)
417                    {
418                        mc.ingameGUI.getChatGUI().printChatMessage(msg);
419                    }
420                }
421            }
422        }
423    }
424
425    public static void setRenderPass(int pass)
426    {
427        renderPass = pass;
428    }
429}