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