    001package cpw.mods.fml.client;
    002
    003import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
    004import static org.lwjgl.opengl.GL11.GL_TEXTURE_BINDING_2D;
    005
    006import java.awt.Dimension;
    007import java.awt.image.BufferedImage;
    008import java.io.IOException;
    009import java.io.InputStream;
    010import java.nio.ByteBuffer;
    011import java.util.ArrayList;
    012import java.util.HashMap;
    013import java.util.HashSet;
    014import java.util.IdentityHashMap;
    015import java.util.List;
    016import java.util.ListIterator;
    017import java.util.Map;
    018import java.util.logging.Level;
    019
    020import javax.imageio.ImageIO;
    021
    022import net.minecraft.client.Minecraft;
    023import net.minecraft.client.renderer.RenderEngine;
    024import net.minecraft.client.renderer.texturefx.TextureFX;
    025import net.minecraft.client.texturepacks.ITexturePack;
    026import net.minecraft.src.ModTextureStatic;
    027
    028import org.lwjgl.opengl.GL11;
    029
    030import com.google.common.collect.ArrayListMultimap;
    031import com.google.common.collect.Maps;
    032import com.google.common.collect.Multimap;
    033
    034import cpw.mods.fml.common.FMLCommonHandler;
    035import cpw.mods.fml.common.FMLLog;
    036import cpw.mods.fml.common.ModContainer;
    037
    038public class TextureFXManager
    039{
    040    private static final TextureFXManager INSTANCE = new TextureFXManager();
    041
    042    private class TextureProperties
    043    {
    044        private int textureId;
    045        private Dimension dim;
    046    }
    047
    048    private Map<Integer,TextureProperties> textureProperties = Maps.newHashMap();
    049    private Multimap<String, OverrideInfo> overrideInfo = ArrayListMultimap.create();
    050    private HashSet<OverrideInfo> animationSet = new HashSet<OverrideInfo>();
    051
    052    private List<TextureFX> addedTextureFX = new ArrayList<TextureFX>();
    053
    054    private Minecraft client;
    055
    056    void setClient(Minecraft client)
    057    {
    058        this.client = client;
    059    }
    060
    061    public boolean onUpdateTextureEffect(TextureFX effect)
    062    {
    063        ITextureFX ifx = (effect instanceof ITextureFX ? ((ITextureFX)effect) : null);
    064
    065        if (ifx != null && ifx.getErrored())
    066        {
    067            return false;
    068        }
    069
    070        String name = effect.getClass().getSimpleName();
    071        client.mcProfiler.startSection(name);
    072        try
    073        {
    074            if (!FMLClientHandler.instance().hasOptifine())
    075            {
    076                effect.onTick();
    077            }
    078        }
    079        catch (Exception e)
    080        {
    081            FMLLog.log("fml.TextureManager", Level.WARNING, "Texture FX %s has failed to animate. Likely caused by a texture pack change that they did not respond correctly to", name);
    082            if (ifx != null)
    083            {
    084                ifx.setErrored(true);
    085            }
    086            client.mcProfiler.endSection();
    087            return false;
    088        }
    089        client.mcProfiler.endSection();
    090
    091        if (ifx != null)
    092        {
    093            Dimension dim = getTextureDimensions(effect);
    094            int target = ((dim.width >> 4) * (dim.height >> 4)) << 2;
    095            if (effect.imageData.length != target)
    096            {
    097                FMLLog.log("fml.TextureManager", Level.WARNING, "Detected a texture FX sizing discrepancy in %s (%d, %d)", name, effect.imageData.length, target);
    098                ifx.setErrored(true);
    099                return false;
    100            }
    101        }
    102        return true;
    103    }
    104
    105    //Quick and dirty image scaling, no smoothing or fanciness, meant for speed as it will be called every tick.
    106    public void scaleTextureFXData(byte[] data, ByteBuffer buf, int target, int length)
    107    {
    108        int sWidth = (int)Math.sqrt(data.length / 4);
    109        int factor = target / sWidth;
    110        byte[] tmp = new byte[4];
    111
    112        buf.clear();
    113
    114        if (factor > 1)
    115        {
    116            for (int y = 0; y < sWidth; y++)
    117            {
    118                int sRowOff = sWidth * y;
    119                int tRowOff = target * y * factor;
    120                for (int x = 0; x < sWidth; x++)
    121                {
    122                    int sPos = (x + sRowOff) * 4;
    123                    tmp[0] = data[sPos + 0];
    124                    tmp[1] = data[sPos + 1];
    125                    tmp[2] = data[sPos + 2];
    126                    tmp[3] = data[sPos + 3];
    127
    128                    int tPosTop = (x * factor) + tRowOff;
    129                    for (int y2 = 0; y2 < factor; y2++)
    130                    {
    131                        buf.position((tPosTop + (y2 * target)) * 4);
    132                        for (int x2 = 0; x2 < factor; x2++)
    133                        {
    134                            buf.put(tmp);
    135                        }
    136                    }
    137                }
    138            }
    139        }
    140
    141        buf.position(0).limit(length);
    142    }
    143
    144    public void onPreRegisterEffect(TextureFX effect)
    145    {
    146        Dimension dim = getTextureDimensions(effect);
    147        if (effect instanceof ITextureFX)
    148        {
    149            ((ITextureFX)effect).onTextureDimensionsUpdate(dim.width, dim.height);
    150        }
    151    }
    152
    153
    154    public int getEffectTexture(TextureFX effect)
    155    {
    156        Integer id = effectTextures.get(effect);
    157        if (id != null)
    158        {
    159            return id;
    160        }
    161
    162        int old = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
    163        effect.bindImage(client.renderEngine);
    164        id = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
    165        GL11.glBindTexture(GL_TEXTURE_2D, old);
    166        effectTextures.put(effect, id);
    167        effect.textureId = id;
    168        return id;
    169    }
    170
    171    public void onTexturePackChange(RenderEngine engine, ITexturePack texturepack, List<TextureFX> effects)
    172    {
    173        pruneOldTextureFX(texturepack, effects);
    174
    175        for (TextureFX tex : effects)
    176        {
    177            if (tex instanceof ITextureFX)
    178            {
    179                ((ITextureFX)tex).onTexturePackChanged(engine, texturepack, getTextureDimensions(tex));
    180            }
    181        }
    182
    183        loadTextures(texturepack);
    184    }
    185
    186    private HashMap<Integer, Dimension> textureDims = new HashMap<Integer, Dimension>();
    187    private IdentityHashMap<TextureFX, Integer> effectTextures = new IdentityHashMap<TextureFX, Integer>();
    188    private ITexturePack earlyTexturePack;
    189    public void setTextureDimensions(int id, int width, int height, List<TextureFX> effects)
    190    {
    191        Dimension dim = new Dimension(width, height);
    192        textureDims.put(id, dim);
    193
    194        for (TextureFX tex : effects)
    195        {
    196            if (getEffectTexture(tex) == id && tex instanceof ITextureFX)
    197            {
    198                ((ITextureFX)tex).onTextureDimensionsUpdate(width, height);
    199            }
    200        }
    201    }
    202
    203    public Dimension getTextureDimensions(TextureFX effect)
    204    {
    205        return getTextureDimensions(getEffectTexture(effect));
    206    }
    207
    208    public Dimension getTextureDimensions(int id)
    209    {
    210        return textureDims.get(id);
    211    }
    212
    213    public void addAnimation(TextureFX anim)
    214    {
    215        OverrideInfo info=new OverrideInfo();
    216        info.index=anim.iconIndex;
    217        info.imageIndex=anim.tileImage;
    218        info.textureFX=anim;
    219        if (animationSet.contains(info)) {
    220            animationSet.remove(info);
    221        }
    222        animationSet.add(info);
    223    }
    224
    225
    226    public void loadTextures(ITexturePack texturePack)
    227    {
    228        registerTextureOverrides(client.renderEngine);
    229    }
    230
    231
    232    public void registerTextureOverrides(RenderEngine renderer) {
    233        for (OverrideInfo animationOverride : animationSet) {
    234            renderer.registerTextureFX(animationOverride.textureFX);
    235            addedTextureFX.add(animationOverride.textureFX);
    236            FMLLog.log("fml.TextureManager", Level.FINE, "Registered texture override %d (%d) on %s (%d)", animationOverride.index, animationOverride.textureFX.iconIndex, animationOverride.textureFX.getClass().getSimpleName(), animationOverride.textureFX.tileImage);
    237        }
    238
    239        for (String fileToOverride : overrideInfo.keySet()) {
    240            for (OverrideInfo override : overrideInfo.get(fileToOverride)) {
    241                try
    242                {
    243                    BufferedImage image=loadImageFromTexturePack(renderer, override.override);
    244                    ModTextureStatic mts=new ModTextureStatic(override.index, 1, override.texture, image);
    245                    renderer.registerTextureFX(mts);
    246                    addedTextureFX.add(mts);
    247                    FMLLog.log("fml.TextureManager", Level.FINE, "Registered texture override %d (%d) on %s (%d)", override.index, mts.iconIndex, override.texture, mts.tileImage);
    248                }
    249                catch (IOException e)
    250                {
    251                    FMLLog.log("fml.TextureManager", Level.WARNING, e, "Exception occurred registering texture override for %s", fileToOverride);
    252                }
    253            }
    254        }
    255    }
    256
    257    protected void registerAnimatedTexturesFor(ModContainer mod)
    258    {
    259    }
    260
    261    public void onEarlyTexturePackLoad(ITexturePack fallback)
    262    {
    263        if (client==null) {
    264            // We're far too early- let's wait
    265            this.earlyTexturePack = fallback;
    266        } else {
    267            loadTextures(fallback);
    268        }
    269    }
    270
    271
    272    public void pruneOldTextureFX(ITexturePack var1, List<TextureFX> effects)
    273    {
    274        ListIterator<TextureFX> li = addedTextureFX.listIterator();
    275        while (li.hasNext())
    276        {
    277            TextureFX tex = li.next();
    278            if (tex instanceof FMLTextureFX)
    279            {
    280                if (((FMLTextureFX)tex).unregister(client.renderEngine, effects))
    281                {
    282                    li.remove();
    283                }
    284            }
    285            else
    286            {
    287                effects.remove(tex);
    288                li.remove();
    289            }
    290        }
    291    }
    292    public void addNewTextureOverride(String textureToOverride, String overridingTexturePath, int location) {
    293        OverrideInfo info = new OverrideInfo();
    294        info.index = location;
    295        info.override = overridingTexturePath;
    296        info.texture = textureToOverride;
    297        overrideInfo.put(textureToOverride, info);
    298        FMLLog.log("fml.TextureManager", Level.FINE, "Overriding %s @ %d with %s. %d slots remaining",textureToOverride, location, overridingTexturePath, SpriteHelper.freeSlotCount(textureToOverride));
    299    }
    300
    301    public BufferedImage loadImageFromTexturePack(RenderEngine renderEngine, String path) throws IOException
    302    {
    303        InputStream image=client.texturePackList.getSelectedTexturePack().getResourceAsStream(path);
    304        if (image==null) {
    305            throw new RuntimeException(String.format("The requested image path %s is not found",path));
    306        }
    307        BufferedImage result=ImageIO.read(image);
    308        if (result==null)
    309        {
    310            throw new RuntimeException(String.format("The requested image path %s appears to be corrupted",path));
    311        }
    312        return result;
    313    }
    314
    315    public static TextureFXManager instance()
    316    {
    317        return INSTANCE;
    318    }
    319
    320    public void fixTransparency(BufferedImage loadedImage, String textureName)
    321    {
    322        if (textureName.matches("^/mob/.*_eyes.*.png$"))
    323        {
    324            for (int x = 0; x < loadedImage.getWidth(); x++) {
    325                for (int y = 0; y < loadedImage.getHeight(); y++) {
    326                    int argb = loadedImage.getRGB(x, y);
    327                    if ((argb & 0xff000000) == 0 && argb != 0) {
    328                        loadedImage.setRGB(x, y, 0);
    329                    }
    330                }
    331            }
    332        }
    333    }
    334
    335}