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.TexturePackBase;
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, TexturePackBase 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 TexturePackBase 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        /**
217         * @param anim
218         */
219        public void addAnimation(TextureFX anim)
220        {
221            OverrideInfo info=new OverrideInfo();
222            info.index=anim.iconIndex;
223            info.imageIndex=anim.tileImage;
224            info.textureFX=anim;
225            if (animationSet.contains(info)) {
226                animationSet.remove(info);
227            }
228            animationSet.add(info);
229        }
230    
231    
232        /**
233         * @param p_6531_1_
234         */
235        public void loadTextures(TexturePackBase texturePack)
236        {
237            registerTextureOverrides(client.renderEngine);
238        }
239    
240    
241        public void registerTextureOverrides(RenderEngine renderer) {
242            for (OverrideInfo animationOverride : animationSet) {
243                renderer.registerTextureFX(animationOverride.textureFX);
244                addedTextureFX.add(animationOverride.textureFX);
245                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));
246            }
247    
248            for (String fileToOverride : overrideInfo.keySet()) {
249                for (OverrideInfo override : overrideInfo.get(fileToOverride)) {
250                    try
251                    {
252                        BufferedImage image=loadImageFromTexturePack(renderer, override.override);
253                        ModTextureStatic mts=new ModTextureStatic(override.index, 1, override.texture, image);
254                        renderer.registerTextureFX(mts);
255                        addedTextureFX.add(mts);
256                        FMLCommonHandler.instance().getFMLLogger().finer(String.format("Registered texture override %d (%d) on %s (%d)", override.index, mts.iconIndex, override.texture, mts.tileImage));
257                    }
258                    catch (IOException e)
259                    {
260                        FMLCommonHandler.instance().getFMLLogger().throwing("FMLClientHandler", "registerTextureOverrides", e);
261                    }
262                }
263            }
264        }
265    
266        /**
267         * @param mod
268         */
269        protected void registerAnimatedTexturesFor(ModContainer mod)
270        {
271        }
272    
273        /**
274         * @param field_6539_c
275         */
276        public void onEarlyTexturePackLoad(TexturePackBase fallback)
277        {
278            if (client==null) {
279                // We're far too early- let's wait
280                this.earlyTexturePack = fallback;
281            } else {
282                loadTextures(fallback);
283            }
284        }
285    
286    
287        public void pruneOldTextureFX(TexturePackBase var1, List<TextureFX> effects)
288        {
289            ListIterator<TextureFX> li = addedTextureFX.listIterator();
290            while (li.hasNext())
291            {
292                TextureFX tex = li.next();
293                if (tex instanceof FMLTextureFX)
294                {
295                    if (((FMLTextureFX)tex).unregister(client.renderEngine, effects))
296                    {
297                        li.remove();
298                    }
299                }
300                else
301                {
302                    effects.remove(tex);
303                    li.remove();
304                }
305            }
306        }
307        public void addNewTextureOverride(String textureToOverride, String overridingTexturePath, int location) {
308            OverrideInfo info = new OverrideInfo();
309            info.index = location;
310            info.override = overridingTexturePath;
311            info.texture = textureToOverride;
312            overrideInfo.put(textureToOverride, info);
313            FMLLog.fine("Overriding %s @ %d with %s. %d slots remaining",textureToOverride, location, overridingTexturePath, SpriteHelper.freeSlotCount(textureToOverride));
314        }
315    
316        /**
317         * @param renderEngine
318         * @param path
319         * @return
320         */
321        public BufferedImage loadImageFromTexturePack(RenderEngine renderEngine, String path) throws IOException
322        {
323            InputStream image=client.texturePackList.getSelectedTexturePack().getResourceAsStream(path);
324            if (image==null) {
325                throw new RuntimeException(String.format("The requested image path %s is not found",path));
326            }
327            BufferedImage result=ImageIO.read(image);
328            if (result==null)
329            {
330                throw new RuntimeException(String.format("The requested image path %s appears to be corrupted",path));
331            }
332            return result;
333        }
334    
335        public static TextureFXManager instance()
336        {
337            return INSTANCE;
338        }
339    
340    }