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