    001package net.minecraft.client.renderer;
    002
    003import cpw.mods.fml.client.TextureFXManager;
    004import cpw.mods.fml.common.FMLLog;
    005import cpw.mods.fml.relauncher.Side;
    006import cpw.mods.fml.relauncher.SideOnly;
    007import java.awt.Color;
    008import java.awt.Dimension;
    009import java.awt.Graphics;
    010import java.awt.image.BufferedImage;
    011import java.io.IOException;
    012import java.io.InputStream;
    013import java.nio.IntBuffer;
    014import java.util.HashMap;
    015import java.util.Iterator;
    016import java.util.Map;
    017import java.util.logging.Level;
    018import java.util.logging.Logger;
    019
    020import javax.imageio.ImageIO;
    021import net.minecraft.client.Minecraft;
    022import net.minecraft.client.renderer.texture.TextureMap;
    023import net.minecraft.client.settings.GameSettings;
    024import net.minecraft.client.texturepacks.ITexturePack;
    025import net.minecraft.client.texturepacks.TexturePackList;
    026import net.minecraft.util.Icon;
    027import net.minecraft.util.IntHashMap;
    028import org.lwjgl.opengl.GL11;
    029import org.lwjgl.opengl.GL12;
    030
    031import net.minecraftforge.client.ForgeHooksClient;
    032
    033@SideOnly(Side.CLIENT)
    034public class RenderEngine
    035{
    036    private HashMap textureMap = new HashMap();
    037
    038    /** Texture contents map (key: texture name, value: int[] contents) */
    039    private HashMap textureContentsMap = new HashMap();
    040
    041    /** A mapping from GL texture names (integers) to BufferedImage instances */
    042    private IntHashMap textureNameToImageMap = new IntHashMap();
    043
    044    /** Stores the image data for the texture. */
    045    private IntBuffer imageData = GLAllocation.createDirectIntBuffer(4194304);
    046
    047    /** A mapping from image URLs to ThreadDownloadImageData instances */
    048    private Map urlToImageDataMap = new HashMap();
    049
    050    /** Reference to the GameSettings object */
    051    private GameSettings options;
    052
    053    /** Texture pack */
    054    public TexturePackList texturePack;
    055
    056    /** Missing texture image */
    057    private BufferedImage missingTextureImage = new BufferedImage(64, 64, 2);
    058    public final TextureMap textureMapBlocks;
    059    public final TextureMap textureMapItems;
    060    private int boundTexture;
    061
    062    public static Logger log = FMLLog.getLogger();
    063
    064    public RenderEngine(TexturePackList par1TexturePackList, GameSettings par2GameSettings)
    065    {
    066        this.texturePack = par1TexturePackList;
    067        this.options = par2GameSettings;
    068        Graphics graphics = this.missingTextureImage.getGraphics();
    069        graphics.setColor(Color.WHITE);
    070        graphics.fillRect(0, 0, 64, 64);
    071        graphics.setColor(Color.BLACK);
    072        int i = 10;
    073        int j = 0;
    074
    075        while (i < 64)
    076        {
    077            String s = j++ % 2 == 0 ? "missing" : "texture";
    078            graphics.drawString(s, 1, i);
    079            i += graphics.getFont().getSize();
    080
    081            if (j % 2 == 0)
    082            {
    083                i += 5;
    084            }
    085        }
    086
    087        graphics.dispose();
    088        this.textureMapBlocks = new TextureMap(0, "terrain", "textures/blocks/", this.missingTextureImage);
    089        this.textureMapItems = new TextureMap(1, "items", "textures/items/", this.missingTextureImage);
    090    }
    091
    092    public int[] getTextureContents(String par1Str)
    093    {
    094        ITexturePack itexturepack = this.texturePack.getSelectedTexturePack();
    095        int[] aint = (int[])this.textureContentsMap.get(par1Str);
    096
    097        if (aint != null)
    098        {
    099            return aint;
    100        }
    101        else
    102        {
    103            try
    104            {
    105                InputStream inputstream = itexturepack.getResourceAsStream(par1Str);
    106                int[] aint1;
    107
    108                if (inputstream == null)
    109                {
    110                    aint1 = this.getImageContentsAndAllocate(this.missingTextureImage);
    111                }
    112                else
    113                {
    114                    BufferedImage bufferedimage = this.readTextureImage(inputstream);
    115                    TextureFXManager.instance().fixTransparency(bufferedimage, par1Str);
    116                    aint1 = this.getImageContentsAndAllocate(bufferedimage);
    117                }
    118
    119                this.textureContentsMap.put(par1Str, aint1);
    120                return aint1;
    121            }
    122            catch (Exception ioexception)
    123            {
    124                log.log(Level.INFO, String.format("An error occured reading texture file %s (getTexture)", par1Str), ioexception);
    125                ioexception.printStackTrace();
    126                int[] aint2 = this.getImageContentsAndAllocate(this.missingTextureImage);
    127                this.textureContentsMap.put(par1Str, aint2);
    128                return aint2;
    129            }
    130        }
    131    }
    132
    133    private int[] getImageContentsAndAllocate(BufferedImage par1BufferedImage)
    134    {
    135        return this.getImageContents(par1BufferedImage, new int[par1BufferedImage.getWidth() * par1BufferedImage.getHeight()]);
    136    }
    137
    138    private int[] getImageContents(BufferedImage par1BufferedImage, int[] par2ArrayOfInteger)
    139    {
    140        int i = par1BufferedImage.getWidth();
    141        int j = par1BufferedImage.getHeight();
    142        par1BufferedImage.getRGB(0, 0, i, j, par2ArrayOfInteger, 0, i);
    143        return par2ArrayOfInteger;
    144    }
    145
    146    public void bindTexture(String par1Str)
    147    {
    148        this.bindTexture(this.getTexture(par1Str));
    149    }
    150
    151    private void bindTexture(int par1)
    152    {
    153        if (par1 != this.boundTexture)
    154        {
    155            GL11.glBindTexture(GL11.GL_TEXTURE_2D, par1);
    156            this.boundTexture = par1;
    157        }
    158    }
    159
    160    public void resetBoundTexture()
    161    {
    162        this.boundTexture = -1;
    163    }
    164
    165    public int getTexture(String par1Str)
    166    {
    167        if (par1Str.equals("/terrain.png"))
    168        {
    169            this.textureMapBlocks.getTexture().bindTexture(0);
    170            return this.textureMapBlocks.getTexture().getGlTextureId();
    171        }
    172        else if (par1Str.equals("/gui/items.png"))
    173        {
    174            this.textureMapItems.getTexture().bindTexture(0);
    175            return this.textureMapItems.getTexture().getGlTextureId();
    176        }
    177        else
    178        {
    179            Integer integer = (Integer)this.textureMap.get(par1Str);
    180
    181            if (integer != null)
    182            {
    183                return integer.intValue();
    184            }
    185            else
    186            {
    187                String s1 = par1Str;
    188
    189                try
    190                {
    191                    ForgeHooksClient.onTextureLoadPre(par1Str);
    192                    int i = GLAllocation.generateTextureNames();
    193                    TextureFXManager.instance().bindTextureToName(par1Str, i);
    194                    boolean flag = par1Str.startsWith("%blur%");
    195
    196                    if (flag)
    197                    {
    198                        par1Str = par1Str.substring(6);
    199                    }
    200
    201                    boolean flag1 = par1Str.startsWith("%clamp%");
    202
    203                    if (flag1)
    204                    {
    205                        par1Str = par1Str.substring(7);
    206                    }
    207
    208                    InputStream inputstream = this.texturePack.getSelectedTexturePack().getResourceAsStream(par1Str);
    209
    210                    if (inputstream == null)
    211                    {
    212                        this.setupTextureExt(this.missingTextureImage, i, flag, flag1);
    213                    }
    214                    else
    215                    {
    216                        BufferedImage bufferedimage = this.readTextureImage(inputstream);
    217                        TextureFXManager.instance().fixTransparency(bufferedimage, par1Str);
    218                        this.setupTextureExt(bufferedimage, i, flag, flag1);
    219                    }
    220
    221                    this.textureMap.put(s1, Integer.valueOf(i));
    222                    ForgeHooksClient.onTextureLoad(par1Str, texturePack.getSelectedTexturePack());
    223                    return i;
    224                }
    225                catch (Exception exception)
    226                {
    227                    exception.printStackTrace();
    228                    int j = GLAllocation.generateTextureNames();
    229                    this.setupTexture(this.missingTextureImage, j);
    230                    this.textureMap.put(par1Str, Integer.valueOf(j));
    231                    return j;
    232                }
    233            }
    234        }
    235    }
    236
    237    /**
    238     * Copy the supplied image onto a newly-allocated OpenGL texture, returning the allocated texture name
    239     */
    240    public int allocateAndSetupTexture(BufferedImage par1BufferedImage)
    241    {
    242        int i = GLAllocation.generateTextureNames();
    243        this.setupTexture(par1BufferedImage, i);
    244        this.textureNameToImageMap.addKey(i, par1BufferedImage);
    245        return i;
    246    }
    247
    248    /**
    249     * Copy the supplied image onto the specified OpenGL texture
    250     */
    251    public void setupTexture(BufferedImage par1BufferedImage, int par2)
    252    {
    253        this.setupTextureExt(par1BufferedImage, par2, false, false);
    254    }
    255
    256    public void setupTextureExt(BufferedImage par1BufferedImage, int par2, boolean par3, boolean par4)
    257    {
    258        this.bindTexture(par2);
    259        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
    260        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
    261
    262        if (par3)
    263        {
    264            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    265            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    266        }
    267
    268        if (par4)
    269        {
    270            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
    271            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
    272        }
    273        else
    274        {
    275            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
    276            GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
    277        }
    278
    279        int j = par1BufferedImage.getWidth();
    280        int k = par1BufferedImage.getHeight();
    281        TextureFXManager.instance().setTextureDimensions(par2, j, k);
    282        int[] aint = new int[j * k];
    283        par1BufferedImage.getRGB(0, 0, j, k, aint, 0, j);
    284
    285        if (this.options != null && this.options.anaglyph)
    286        {
    287            aint = this.colorToAnaglyph(aint);
    288        }
    289
    290        this.imageData.clear();
    291        this.imageData.put(aint);
    292        this.imageData.position(0).limit(aint.length);
    293        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, j, k, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, this.imageData);
    294    }
    295
    296    private int[] colorToAnaglyph(int[] par1ArrayOfInteger)
    297    {
    298        int[] aint1 = new int[par1ArrayOfInteger.length];
    299
    300        for (int i = 0; i < par1ArrayOfInteger.length; ++i)
    301        {
    302            int j = par1ArrayOfInteger[i] >> 24 & 255;
    303            int k = par1ArrayOfInteger[i] >> 16 & 255;
    304            int l = par1ArrayOfInteger[i] >> 8 & 255;
    305            int i1 = par1ArrayOfInteger[i] & 255;
    306            int j1 = (k * 30 + l * 59 + i1 * 11) / 100;
    307            int k1 = (k * 30 + l * 70) / 100;
    308            int l1 = (k * 30 + i1 * 70) / 100;
    309            aint1[i] = j << 24 | j1 << 16 | k1 << 8 | l1;
    310        }
    311
    312        return aint1;
    313    }
    314
    315    public void createTextureFromBytes(int[] par1ArrayOfInteger, int par2, int par3, int par4)
    316    {
    317        this.bindTexture(par4);
    318        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
    319        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
    320        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
    321        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
    322
    323        if (this.options != null && this.options.anaglyph)
    324        {
    325            par1ArrayOfInteger = this.colorToAnaglyph(par1ArrayOfInteger);
    326        }
    327
    328        this.imageData.clear();
    329        this.imageData.put(par1ArrayOfInteger);
    330        this.imageData.position(0).limit(par1ArrayOfInteger.length);
    331        GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, par2, par3, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, this.imageData);
    332    }
    333
    334    /**
    335     * Deletes a single GL texture
    336     */
    337    public void deleteTexture(int par1)
    338    {
    339        this.textureNameToImageMap.removeObject(par1);
    340        GL11.glDeleteTextures(par1);
    341    }
    342
    343    /**
    344     * Takes a URL of a downloadable image and the name of the local image to be used as a fallback.  If the image has
    345     * been downloaded, returns the GL texture of the downloaded image, otherwise returns the GL texture of the fallback
    346     * image.
    347     */
    348    public int getTextureForDownloadableImage(String par1Str, String par2Str)
    349    {
    350        ThreadDownloadImageData threaddownloadimagedata = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
    351
    352        if (threaddownloadimagedata != null && threaddownloadimagedata.image != null && !threaddownloadimagedata.textureSetupComplete)
    353        {
    354            if (threaddownloadimagedata.textureName < 0)
    355            {
    356                threaddownloadimagedata.textureName = this.allocateAndSetupTexture(threaddownloadimagedata.image);
    357            }
    358            else
    359            {
    360                this.setupTexture(threaddownloadimagedata.image, threaddownloadimagedata.textureName);
    361            }
    362
    363            threaddownloadimagedata.textureSetupComplete = true;
    364        }
    365
    366        return threaddownloadimagedata != null && threaddownloadimagedata.textureName >= 0 ? threaddownloadimagedata.textureName : (par2Str == null ? -1 : this.getTexture(par2Str));
    367    }
    368
    369    /**
    370     * Checks if urlToImageDataMap has image data for the given key
    371     */
    372    public boolean hasImageData(String par1Str)
    373    {
    374        return this.urlToImageDataMap.containsKey(par1Str);
    375    }
    376
    377    /**
    378     * Return a ThreadDownloadImageData instance for the given URL.  If it does not already exist, it is created and
    379     * uses the passed ImageBuffer.  If it does, its reference count is incremented.
    380     */
    381    public ThreadDownloadImageData obtainImageData(String par1Str, IImageBuffer par2IImageBuffer)
    382    {
    383        ThreadDownloadImageData threaddownloadimagedata = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
    384
    385        if (threaddownloadimagedata == null)
    386        {
    387            this.urlToImageDataMap.put(par1Str, new ThreadDownloadImageData(par1Str, par2IImageBuffer));
    388        }
    389        else
    390        {
    391            ++threaddownloadimagedata.referenceCount;
    392        }
    393
    394        return threaddownloadimagedata;
    395    }
    396
    397    /**
    398     * Decrements the reference count for a given URL, deleting the image data if the reference count hits 0
    399     */
    400    public void releaseImageData(String par1Str)
    401    {
    402        ThreadDownloadImageData threaddownloadimagedata = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
    403
    404        if (threaddownloadimagedata != null)
    405        {
    406            --threaddownloadimagedata.referenceCount;
    407
    408            if (threaddownloadimagedata.referenceCount == 0)
    409            {
    410                if (threaddownloadimagedata.textureName >= 0)
    411                {
    412                    this.deleteTexture(threaddownloadimagedata.textureName);
    413                }
    414
    415                this.urlToImageDataMap.remove(par1Str);
    416            }
    417        }
    418    }
    419
    420    public void updateDynamicTextures()
    421    {
    422        this.textureMapBlocks.updateAnimations();
    423        this.textureMapItems.updateAnimations();
    424    }
    425
    426    /**
    427     * Call setupTexture on all currently-loaded textures again to account for changes in rendering options
    428     */
    429    public void refreshTextures()
    430    {
    431        ITexturePack itexturepack = this.texturePack.getSelectedTexturePack();
    432        this.refreshTextureMaps();
    433        Iterator iterator = this.textureNameToImageMap.getKeySet().iterator();
    434        BufferedImage bufferedimage;
    435
    436        while (iterator.hasNext())
    437        {
    438            int i = ((Integer)iterator.next()).intValue();
    439            bufferedimage = (BufferedImage)this.textureNameToImageMap.lookup(i);
    440            this.setupTexture(bufferedimage, i);
    441        }
    442
    443        ThreadDownloadImageData threaddownloadimagedata;
    444
    445        for (iterator = this.urlToImageDataMap.values().iterator(); iterator.hasNext(); threaddownloadimagedata.textureSetupComplete = false)
    446        {
    447            threaddownloadimagedata = (ThreadDownloadImageData)iterator.next();
    448        }
    449
    450        iterator = this.textureMap.keySet().iterator();
    451        String s;
    452
    453        while (iterator.hasNext())
    454        {
    455            s = (String)iterator.next();
    456
    457            try
    458            {
    459                int j = ((Integer)this.textureMap.get(s)).intValue();
    460                boolean flag = s.startsWith("%blur%");
    461
    462                if (flag)
    463                {
    464                    s = s.substring(6);
    465                }
    466
    467                boolean flag1 = s.startsWith("%clamp%");
    468
    469                if (flag1)
    470                {
    471                    s = s.substring(7);
    472                }
    473
    474                BufferedImage bufferedimage1 = this.readTextureImage(itexturepack.getResourceAsStream(s));
    475                TextureFXManager.instance().fixTransparency(bufferedimage1, s);
    476                this.setupTextureExt(bufferedimage1, j, flag, flag1);
    477            }
    478            catch (Exception ioexception)
    479            {
    480                log.log(Level.INFO,String.format("An error occured reading texture file %s (refreshTexture)", s), ioexception);
    481                ioexception.printStackTrace();
    482            }
    483        }
    484
    485        iterator = this.textureContentsMap.keySet().iterator();
    486
    487        while (iterator.hasNext())
    488        {
    489            s = (String)iterator.next();
    490
    491            try
    492            {
    493                bufferedimage = this.readTextureImage(itexturepack.getResourceAsStream(s));
    494                TextureFXManager.instance().fixTransparency(bufferedimage, s);
    495                this.getImageContents(bufferedimage, (int[])this.textureContentsMap.get(s));
    496            }
    497            catch (Exception ioexception1)
    498            {
    499                log.log(Level.INFO,String.format("An error occured reading texture file data %s (refreshTexture)", s), ioexception1);
    500                ioexception1.printStackTrace();
    501            }
    502        }
    503
    504        Minecraft.getMinecraft().fontRenderer.readFontData();
    505        Minecraft.getMinecraft().standardGalacticFontRenderer.readFontData();
    506    }
    507
    508    /**
    509     * Returns a BufferedImage read off the provided input stream.  Args: inputStream
    510     */
    511    private BufferedImage readTextureImage(InputStream par1InputStream) throws IOException
    512    {
    513        BufferedImage bufferedimage = ImageIO.read(par1InputStream);
    514        par1InputStream.close();
    515        return bufferedimage;
    516    }
    517
    518    public void refreshTextureMaps()
    519    {
    520        this.textureMapBlocks.refreshTextures();
    521        this.textureMapItems.refreshTextures();
    522    }
    523
    524    public Icon getMissingIcon(int par1)
    525    {
    526        switch (par1)
    527        {
    528            case 0:
    529                return this.textureMapBlocks.getMissingIcon();
    530            case 1:
    531            default:
    532                return this.textureMapItems.getMissingIcon();
    533        }
    534    }
    535}