    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.src.ModTextureStatic;
    023    import net.minecraft.src.RenderEngine;
    024    import net.minecraft.src.TextureFX;
    025    import net.minecraft.src.ITexturePack;
    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
    163            effect.bindImage(client.renderEngine);
    164
    165            id = GL11.glGetInteger(GL_TEXTURE_BINDING_2D);
    166
    167            GL11.glBindTexture(GL_TEXTURE_2D, old);
    168
    169            effectTextures.put(effect, id);
    170
    171            return id;
    172        }
    173
    174        public void onTexturePackChange(RenderEngine engine, ITexturePack texturepack, List<TextureFX> effects)
    175        {
    176            pruneOldTextureFX(texturepack, effects);
    177
    178            for (TextureFX tex : effects)
    179            {
    180                if (tex instanceof ITextureFX)
    181                {
    182                    ((ITextureFX)tex).onTexturePackChanged(engine, texturepack, getTextureDimensions(tex));
    183                }
    184            }
    185
    186            loadTextures(texturepack);
    187        }
    188
    189        private HashMap<Integer, Dimension> textureDims = new HashMap<Integer, Dimension>();
    190        private IdentityHashMap<TextureFX, Integer> effectTextures = new IdentityHashMap<TextureFX, Integer>();
    191        private ITexturePack earlyTexturePack;
    192        public void setTextureDimensions(int id, int width, int height, List<TextureFX> effects)
    193        {
    194            Dimension dim = new Dimension(width, height);
    195            textureDims.put(id, dim);
    196
    197            for (TextureFX tex : effects)
    198            {
    199                if (getEffectTexture(tex) == id && tex instanceof ITextureFX)
    200                {
    201                    ((ITextureFX)tex).onTextureDimensionsUpdate(width, height);
    202                }
    203            }
    204        }
    205
    206        public Dimension getTextureDimensions(TextureFX effect)
    207        {
    208            return getTextureDimensions(getEffectTexture(effect));
    209        }
    210
    211        public Dimension getTextureDimensions(int id)
    212        {
    213            return textureDims.get(id);
    214        }
    215
    216        public void addAnimation(TextureFX anim)
    217        {
    218            OverrideInfo info=new OverrideInfo();
    219            info.index=anim.iconIndex;
    220            info.imageIndex=anim.tileImage;
    221            info.textureFX=anim;
    222            if (animationSet.contains(info)) {
    223                animationSet.remove(info);
    224            }
    225            animationSet.add(info);
    226        }
    227
    228
    229        public void loadTextures(ITexturePack texturePack)
    230        {
    231            registerTextureOverrides(client.renderEngine);
    232        }
    233
    234
    235        public void registerTextureOverrides(RenderEngine renderer) {
    236            for (OverrideInfo animationOverride : animationSet) {
    237                renderer.registerTextureFX(animationOverride.textureFX);
    238                addedTextureFX.add(animationOverride.textureFX);
    239                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));
    240            }
    241
    242            for (String fileToOverride : overrideInfo.keySet()) {
    243                for (OverrideInfo override : overrideInfo.get(fileToOverride)) {
    244                    try
    245                    {
    246                        BufferedImage image=loadImageFromTexturePack(renderer, override.override);
    247                        ModTextureStatic mts=new ModTextureStatic(override.index, 1, override.texture, image);
    248                        renderer.registerTextureFX(mts);
    249                        addedTextureFX.add(mts);
    250                        FMLCommonHandler.instance().getFMLLogger().finer(String.format("Registered texture override %d (%d) on %s (%d)", override.index, mts.iconIndex, override.texture, mts.tileImage));
    251                    }
    252                    catch (IOException e)
    253                    {
    254                        FMLCommonHandler.instance().getFMLLogger().throwing("FMLClientHandler", "registerTextureOverrides", e);
    255                    }
    256                }
    257            }
    258        }
    259
    260        protected void registerAnimatedTexturesFor(ModContainer mod)
    261        {
    262        }
    263
    264        public void onEarlyTexturePackLoad(ITexturePack fallback)
    265        {
    266            if (client==null) {
    267                // We're far too early- let's wait
    268                this.earlyTexturePack = fallback;
    269            } else {
    270                loadTextures(fallback);
    271            }
    272        }
    273
    274
    275        public void pruneOldTextureFX(ITexturePack var1, List<TextureFX> effects)
    276        {
    277            ListIterator<TextureFX> li = addedTextureFX.listIterator();
    278            while (li.hasNext())
    279            {
    280                TextureFX tex = li.next();
    281                if (tex instanceof FMLTextureFX)
    282                {
    283                    if (((FMLTextureFX)tex).unregister(client.renderEngine, effects))
    284                    {
    285                        li.remove();
    286                    }
    287                }
    288                else
    289                {
    290                    effects.remove(tex);
    291                    li.remove();
    292                }
    293            }
    294        }
    295        public void addNewTextureOverride(String textureToOverride, String overridingTexturePath, int location) {
    296            OverrideInfo info = new OverrideInfo();
    297            info.index = location;
    298            info.override = overridingTexturePath;
    299            info.texture = textureToOverride;
    300            overrideInfo.put(textureToOverride, info);
    301            FMLLog.fine("Overriding %s @ %d with %s. %d slots remaining",textureToOverride, location, overridingTexturePath, SpriteHelper.freeSlotCount(textureToOverride));
    302        }
    303
    304        public BufferedImage loadImageFromTexturePack(RenderEngine renderEngine, String path) throws IOException
    305        {
    306            InputStream image=client.texturePackList.getSelectedTexturePack().getResourceAsStream(path);
    307            if (image==null) {
    308                throw new RuntimeException(String.format("The requested image path %s is not found",path));
    309            }
    310            BufferedImage result=ImageIO.read(image);
    311            if (result==null)
    312            {
    313                throw new RuntimeException(String.format("The requested image path %s appears to be corrupted",path));
    314            }
    315            return result;
    316        }
    317
    318        public static TextureFXManager instance()
    319        {
    320            return INSTANCE;
    321        }
    322
    323    }