001 package net.minecraft.src;
002
003 import cpw.mods.fml.common.Side;
004 import cpw.mods.fml.common.asm.SideOnly;
005 import java.awt.Color;
006 import java.awt.Dimension;
007 import java.awt.Graphics;
008 import java.awt.image.BufferedImage;
009 import java.awt.image.ImageObserver;
010 import java.io.IOException;
011 import java.io.InputStream;
012 import java.nio.ByteBuffer;
013 import java.nio.IntBuffer;
014 import java.util.ArrayList;
015 import java.util.HashMap;
016 import java.util.Iterator;
017 import java.util.List;
018 import java.util.Map;
019 import java.util.logging.Level;
020 import java.util.logging.Logger;
021
022 import javax.imageio.ImageIO;
023
024 import net.minecraftforge.client.ForgeHooksClient;
025
026 import org.lwjgl.opengl.GL11;
027
028 import cpw.mods.fml.client.TextureFXManager;
029 import cpw.mods.fml.common.FMLLog;
030
031 @SideOnly(Side.CLIENT)
032 public class RenderEngine
033 {
034 private HashMap textureMap = new HashMap();
035
036 /** Texture contents map (key: texture name, value: int[] contents) */
037 private HashMap textureContentsMap = new HashMap();
038
039 /** A mapping from GL texture names (integers) to BufferedImage instances */
040 private IntHashMap textureNameToImageMap = new IntHashMap();
041
042 /** An IntBuffer storing 1 int used as scratch space in RenderEngine */
043 private IntBuffer singleIntBuffer = GLAllocation.createDirectIntBuffer(1);
044
045 /** Stores the image data for the texture. */
046 private ByteBuffer imageData = GLAllocation.createDirectByteBuffer(16777216);
047 public List textureList = new ArrayList();
048
049 /** A mapping from image URLs to ThreadDownloadImageData instances */
050 private Map urlToImageDataMap = new HashMap();
051
052 /** Reference to the GameSettings object */
053 private GameSettings options;
054
055 /** Flag set when a texture should not be repeated */
056 public boolean clampTexture = false;
057
058 /** Flag set when a texture should use blurry resizing */
059 public boolean blurTexture = false;
060
061 /** Texture pack */
062 public TexturePackList texturePack;
063
064 /** Missing texture image */
065 private BufferedImage missingTextureImage = new BufferedImage(64, 64, 2);
066 public static Logger log = FMLLog.getLogger();
067
068 public RenderEngine(TexturePackList par1TexturePackList, GameSettings par2GameSettings)
069 {
070 this.texturePack = par1TexturePackList;
071 this.options = par2GameSettings;
072 Graphics var3 = this.missingTextureImage.getGraphics();
073 var3.setColor(Color.WHITE);
074 var3.fillRect(0, 0, 64, 64);
075 var3.setColor(Color.BLACK);
076 var3.drawString("missingtex", 1, 10);
077 var3.dispose();
078 }
079
080 public int[] getTextureContents(String par1Str)
081 {
082 ITexturePack var2 = this.texturePack.getSelectedTexturePack();
083 int[] var3 = (int[])this.textureContentsMap.get(par1Str);
084
085 if (var3 != null)
086 {
087 return var3;
088 }
089 else
090 {
091 try
092 {
093 Object var4 = null;
094 int[] var7;
095
096 if (par1Str.startsWith("##"))
097 {
098 var7 = this.getImageContentsAndAllocate(this.unwrapImageByColumns(this.readTextureImage(var2.getResourceAsStream(par1Str.substring(2)))));
099 }
100 else if (par1Str.startsWith("%clamp%"))
101 {
102 this.clampTexture = true;
103 var7 = this.getImageContentsAndAllocate(this.readTextureImage(var2.getResourceAsStream(par1Str.substring(7))));
104 this.clampTexture = false;
105 }
106 else if (par1Str.startsWith("%blur%"))
107 {
108 this.blurTexture = true;
109 this.clampTexture = true;
110 var7 = this.getImageContentsAndAllocate(this.readTextureImage(var2.getResourceAsStream(par1Str.substring(6))));
111 this.clampTexture = false;
112 this.blurTexture = false;
113 }
114 else
115 {
116 InputStream var8 = var2.getResourceAsStream(par1Str);
117
118 if (var8 == null)
119 {
120 var7 = this.getImageContentsAndAllocate(this.missingTextureImage);
121 }
122 else
123 {
124 var7 = this.getImageContentsAndAllocate(this.readTextureImage(var8));
125 }
126 }
127
128 this.textureContentsMap.put(par1Str, var7);
129 return var7;
130 }
131 catch (Exception var6)
132 {
133 log.log(Level.INFO, String.format("An error occured reading texture file %s (getTexture)", par1Str), var6);
134 var6.printStackTrace();
135 int[] var5 = this.getImageContentsAndAllocate(this.missingTextureImage);
136 this.textureContentsMap.put(par1Str, var5);
137 return var5;
138 }
139 }
140 }
141
142 private int[] getImageContentsAndAllocate(BufferedImage par1BufferedImage)
143 {
144 int var2 = par1BufferedImage.getWidth();
145 int var3 = par1BufferedImage.getHeight();
146 int[] var4 = new int[var2 * var3];
147 par1BufferedImage.getRGB(0, 0, var2, var3, var4, 0, var2);
148 return var4;
149 }
150
151 private int[] getImageContents(BufferedImage par1BufferedImage, int[] par2ArrayOfInteger)
152 {
153 int var3 = par1BufferedImage.getWidth();
154 int var4 = par1BufferedImage.getHeight();
155 par1BufferedImage.getRGB(0, 0, var3, var4, par2ArrayOfInteger, 0, var3);
156 return par2ArrayOfInteger;
157 }
158
159 public int getTexture(String par1Str)
160 {
161 Integer var2 = (Integer)this.textureMap.get(par1Str);
162
163 if (var2 != null)
164 {
165 return var2.intValue();
166 }
167 else
168 {
169 ITexturePack var6 = this.texturePack.getSelectedTexturePack();
170
171 try
172 {
173 ForgeHooksClient.onTextureLoadPre(par1Str);
174 this.singleIntBuffer.clear();
175 GLAllocation.generateTextureNames(this.singleIntBuffer);
176 int var3 = this.singleIntBuffer.get(0);
177
178 if (par1Str.startsWith("##"))
179 {
180 this.setupTexture(this.unwrapImageByColumns(this.readTextureImage(var6.getResourceAsStream(par1Str.substring(2)))), var3);
181 }
182 else if (par1Str.startsWith("%clamp%"))
183 {
184 this.clampTexture = true;
185 this.setupTexture(this.readTextureImage(var6.getResourceAsStream(par1Str.substring(7))), var3);
186 this.clampTexture = false;
187 }
188 else if (par1Str.startsWith("%blur%"))
189 {
190 this.blurTexture = true;
191 this.setupTexture(this.readTextureImage(var6.getResourceAsStream(par1Str.substring(6))), var3);
192 this.blurTexture = false;
193 }
194 else if (par1Str.startsWith("%blurclamp%"))
195 {
196 this.blurTexture = true;
197 this.clampTexture = true;
198 this.setupTexture(this.readTextureImage(var6.getResourceAsStream(par1Str.substring(11))), var3);
199 this.blurTexture = false;
200 this.clampTexture = false;
201 }
202 else
203 {
204 InputStream var7 = var6.getResourceAsStream(par1Str);
205
206 if (var7 == null)
207 {
208 this.setupTexture(this.missingTextureImage, var3);
209 }
210 else
211 {
212 this.setupTexture(this.readTextureImage(var7), var3);
213 }
214 }
215
216 this.textureMap.put(par1Str, Integer.valueOf(var3));
217 ForgeHooksClient.onTextureLoad(par1Str, var6);
218 return var3;
219 }
220 catch (Exception var5)
221 {
222 var5.printStackTrace();
223 GLAllocation.generateTextureNames(this.singleIntBuffer);
224 int var4 = this.singleIntBuffer.get(0);
225 this.setupTexture(this.missingTextureImage, var4);
226 this.textureMap.put(par1Str, Integer.valueOf(var4));
227 return var4;
228 }
229 }
230 }
231
232 /**
233 * Takes an image with multiple 16-pixel-wide columns and creates a new 16-pixel-wide image where the columns are
234 * stacked vertically
235 */
236 private BufferedImage unwrapImageByColumns(BufferedImage par1BufferedImage)
237 {
238 int var2 = par1BufferedImage.getWidth() / 16;
239 BufferedImage var3 = new BufferedImage(16, par1BufferedImage.getHeight() * var2, 2);
240 Graphics var4 = var3.getGraphics();
241
242 for (int var5 = 0; var5 < var2; ++var5)
243 {
244 var4.drawImage(par1BufferedImage, -var5 * 16, var5 * par1BufferedImage.getHeight(), (ImageObserver)null);
245 }
246
247 var4.dispose();
248 return var3;
249 }
250
251 /**
252 * Copy the supplied image onto a newly-allocated OpenGL texture, returning the allocated texture name
253 */
254 public int allocateAndSetupTexture(BufferedImage par1BufferedImage)
255 {
256 this.singleIntBuffer.clear();
257 GLAllocation.generateTextureNames(this.singleIntBuffer);
258 int var2 = this.singleIntBuffer.get(0);
259 this.setupTexture(par1BufferedImage, var2);
260 this.textureNameToImageMap.addKey(var2, par1BufferedImage);
261 return var2;
262 }
263
264 /**
265 * Copy the supplied image onto the specified OpenGL texture
266 */
267 public void setupTexture(BufferedImage par1BufferedImage, int par2)
268 {
269 GL11.glBindTexture(GL11.GL_TEXTURE_2D, par2);
270 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
271 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
272
273 if (this.blurTexture)
274 {
275 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
276 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
277 }
278
279 if (this.clampTexture)
280 {
281 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
282 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
283 }
284 else
285 {
286 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
287 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
288 }
289
290 int var3 = par1BufferedImage.getWidth();
291 int var4 = par1BufferedImage.getHeight();
292 TextureFXManager.instance().setTextureDimensions(par2, var3, var4, (List<TextureFX>)textureList);
293 int[] var5 = new int[var3 * var4];
294 byte[] var6 = new byte[var3 * var4 * 4];
295 par1BufferedImage.getRGB(0, 0, var3, var4, var5, 0, var3);
296
297 for (int var7 = 0; var7 < var5.length; ++var7)
298 {
299 int var8 = var5[var7] >> 24 & 255;
300 int var9 = var5[var7] >> 16 & 255;
301 int var10 = var5[var7] >> 8 & 255;
302 int var11 = var5[var7] & 255;
303
304 if (this.options != null && this.options.anaglyph)
305 {
306 int var12 = (var9 * 30 + var10 * 59 + var11 * 11) / 100;
307 int var13 = (var9 * 30 + var10 * 70) / 100;
308 int var14 = (var9 * 30 + var11 * 70) / 100;
309 var9 = var12;
310 var10 = var13;
311 var11 = var14;
312 }
313
314 var6[var7 * 4 + 0] = (byte)var9;
315 var6[var7 * 4 + 1] = (byte)var10;
316 var6[var7 * 4 + 2] = (byte)var11;
317 var6[var7 * 4 + 3] = (byte)var8;
318 }
319
320 this.imageData.clear();
321 this.imageData.put(var6);
322 this.imageData.position(0).limit(var6.length);
323 GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, var3, var4, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, this.imageData);
324 }
325
326 public void createTextureFromBytes(int[] par1ArrayOfInteger, int par2, int par3, int par4)
327 {
328 GL11.glBindTexture(GL11.GL_TEXTURE_2D, par4);
329 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
330 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
331
332 if (this.blurTexture)
333 {
334 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
335 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
336 }
337
338 if (this.clampTexture)
339 {
340 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
341 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
342 }
343 else
344 {
345 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
346 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
347 }
348
349 byte[] var5 = new byte[par2 * par3 * 4];
350
351 for (int var6 = 0; var6 < par1ArrayOfInteger.length; ++var6)
352 {
353 int var7 = par1ArrayOfInteger[var6] >> 24 & 255;
354 int var8 = par1ArrayOfInteger[var6] >> 16 & 255;
355 int var9 = par1ArrayOfInteger[var6] >> 8 & 255;
356 int var10 = par1ArrayOfInteger[var6] & 255;
357
358 if (this.options != null && this.options.anaglyph)
359 {
360 int var11 = (var8 * 30 + var9 * 59 + var10 * 11) / 100;
361 int var12 = (var8 * 30 + var9 * 70) / 100;
362 int var13 = (var8 * 30 + var10 * 70) / 100;
363 var8 = var11;
364 var9 = var12;
365 var10 = var13;
366 }
367
368 var5[var6 * 4 + 0] = (byte)var8;
369 var5[var6 * 4 + 1] = (byte)var9;
370 var5[var6 * 4 + 2] = (byte)var10;
371 var5[var6 * 4 + 3] = (byte)var7;
372 }
373
374 this.imageData.clear();
375 this.imageData.put(var5);
376 this.imageData.position(0).limit(var5.length);
377 GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, par2, par3, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, this.imageData);
378 }
379
380 /**
381 * Deletes a single GL texture
382 */
383 public void deleteTexture(int par1)
384 {
385 this.textureNameToImageMap.removeObject(par1);
386 this.singleIntBuffer.clear();
387 this.singleIntBuffer.put(par1);
388 this.singleIntBuffer.flip();
389 GL11.glDeleteTextures(this.singleIntBuffer);
390 }
391
392 /**
393 * Takes a URL of a downloadable image and the name of the local image to be used as a fallback. If the image has
394 * been downloaded, returns the GL texture of the downloaded image, otherwise returns the GL texture of the fallback
395 * image.
396 */
397 public int getTextureForDownloadableImage(String par1Str, String par2Str)
398 {
399 ThreadDownloadImageData var3 = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
400
401 if (var3 != null && var3.image != null && !var3.textureSetupComplete)
402 {
403 if (var3.textureName < 0)
404 {
405 var3.textureName = this.allocateAndSetupTexture(var3.image);
406 }
407 else
408 {
409 this.setupTexture(var3.image, var3.textureName);
410 }
411
412 var3.textureSetupComplete = true;
413 }
414
415 return var3 != null && var3.textureName >= 0 ? var3.textureName : (par2Str == null ? -1 : this.getTexture(par2Str));
416 }
417
418 public boolean func_82773_c(String par1Str)
419 {
420 return this.urlToImageDataMap.containsKey(par1Str);
421 }
422
423 /**
424 * Return a ThreadDownloadImageData instance for the given URL. If it does not already exist, it is created and
425 * uses the passed ImageBuffer. If it does, its reference count is incremented.
426 */
427 public ThreadDownloadImageData obtainImageData(String par1Str, IImageBuffer par2IImageBuffer)
428 {
429 ThreadDownloadImageData var3 = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
430
431 if (var3 == null)
432 {
433 this.urlToImageDataMap.put(par1Str, new ThreadDownloadImageData(par1Str, par2IImageBuffer));
434 }
435 else
436 {
437 ++var3.referenceCount;
438 }
439
440 return var3;
441 }
442
443 /**
444 * Decrements the reference count for a given URL, deleting the image data if the reference count hits 0
445 */
446 public void releaseImageData(String par1Str)
447 {
448 ThreadDownloadImageData var2 = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
449
450 if (var2 != null)
451 {
452 --var2.referenceCount;
453
454 if (var2.referenceCount == 0)
455 {
456 if (var2.textureName >= 0)
457 {
458 this.deleteTexture(var2.textureName);
459 }
460
461 this.urlToImageDataMap.remove(par1Str);
462 }
463 }
464 }
465
466 public void registerTextureFX(TextureFX par1TextureFX)
467 {
468 TextureFXManager.instance().onPreRegisterEffect(par1TextureFX);
469 this.textureList.add(par1TextureFX);
470 par1TextureFX.onTick();
471 }
472
473 public void updateDynamicTextures()
474 {
475 int var1 = -1;
476 TextureFX var3;
477
478 for (Iterator var2 = this.textureList.iterator(); var2.hasNext(); var1 = this.func_82772_a(var3, var1))
479 {
480 var3 = (TextureFX)var2.next();
481 var3.anaglyphEnabled = this.options.anaglyph;
482 if (TextureFXManager.instance().onUpdateTextureEffect(var3))
483 {
484 var1 = this.func_82772_a(var3, var1);
485 }
486 var3.onTick();
487 }
488 }
489
490 public int func_82772_a(TextureFX par1TextureFX, int par2)
491 {
492 Dimension dim = TextureFXManager.instance().getTextureDimensions(par1TextureFX);
493 int tWidth = dim.width >> 4;
494 int tHeight = dim.height >> 4;
495 int tLen = tWidth * tHeight << 2;
496
497 if (par1TextureFX.imageData.length == tLen)
498 {
499 this.imageData.clear();
500 this.imageData.put(par1TextureFX.imageData);
501 this.imageData.position(0).limit(par1TextureFX.imageData.length);
502 }
503 else
504 {
505 TextureFXManager.instance().scaleTextureFXData(par1TextureFX.imageData, imageData, tWidth, tLen);
506 }
507
508 if (par1TextureFX.iconIndex != par2)
509 {
510 par1TextureFX.bindImage(this);
511 par2 = par1TextureFX.iconIndex;
512 }
513
514 for (int var3 = 0; var3 < par1TextureFX.tileSize; ++var3)
515 {
516 int xOffset = par1TextureFX.iconIndex % 16 * tWidth + var3 * tWidth;
517 for (int var4 = 0; var4 < par1TextureFX.tileSize; ++var4)
518 {
519 int yOffset = par1TextureFX.iconIndex / 16 * tHeight + var4 * tHeight;
520 GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, xOffset, yOffset, tWidth, tHeight, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, this.imageData);
521 }
522 }
523
524 return par2;
525 }
526
527 /**
528 * Call setupTexture on all currently-loaded textures again to account for changes in rendering options
529 */
530 public void refreshTextures()
531 {
532 ITexturePack var1 = this.texturePack.getSelectedTexturePack();
533 Iterator var2 = this.textureNameToImageMap.getKeySet().iterator();
534 BufferedImage var4;
535
536 while (var2.hasNext())
537 {
538 int var3 = ((Integer)var2.next()).intValue();
539 var4 = (BufferedImage)this.textureNameToImageMap.lookup(var3);
540 this.setupTexture(var4, var3);
541 }
542
543 ThreadDownloadImageData var8;
544
545 for (var2 = this.urlToImageDataMap.values().iterator(); var2.hasNext(); var8.textureSetupComplete = false)
546 {
547 var8 = (ThreadDownloadImageData)var2.next();
548 }
549
550 var2 = this.textureMap.keySet().iterator();
551 String var9;
552
553 while (var2.hasNext())
554 {
555 var9 = (String)var2.next();
556
557 try
558 {
559 if (var9.startsWith("##"))
560 {
561 var4 = this.unwrapImageByColumns(this.readTextureImage(var1.getResourceAsStream(var9.substring(2))));
562 }
563 else if (var9.startsWith("%clamp%"))
564 {
565 this.clampTexture = true;
566 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(7)));
567 }
568 else if (var9.startsWith("%blur%"))
569 {
570 this.blurTexture = true;
571 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(6)));
572 }
573 else if (var9.startsWith("%blurclamp%"))
574 {
575 this.blurTexture = true;
576 this.clampTexture = true;
577 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(11)));
578 }
579 else
580 {
581 var4 = this.readTextureImage(var1.getResourceAsStream(var9));
582 }
583
584 int var5 = ((Integer)this.textureMap.get(var9)).intValue();
585 this.setupTexture(var4, var5);
586 this.blurTexture = false;
587 this.clampTexture = false;
588 }
589 catch (Exception var7)
590 {
591 log.log(Level.INFO,String.format("An error occured reading texture file %s (refreshTexture)", var9),var7);
592 var7.printStackTrace();
593 }
594 }
595
596 var2 = this.textureContentsMap.keySet().iterator();
597
598 while (var2.hasNext())
599 {
600 var9 = (String)var2.next();
601
602 try
603 {
604 if (var9.startsWith("##"))
605 {
606 var4 = this.unwrapImageByColumns(this.readTextureImage(var1.getResourceAsStream(var9.substring(2))));
607 }
608 else if (var9.startsWith("%clamp%"))
609 {
610 this.clampTexture = true;
611 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(7)));
612 }
613 else if (var9.startsWith("%blur%"))
614 {
615 this.blurTexture = true;
616 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(6)));
617 }
618 else
619 {
620 var4 = this.readTextureImage(var1.getResourceAsStream(var9));
621 }
622
623 this.getImageContents(var4, (int[])this.textureContentsMap.get(var9));
624 this.blurTexture = false;
625 this.clampTexture = false;
626 }
627 catch (Exception var6)
628 {
629 log.log(Level.INFO,String.format("An error occured reading texture file data %s (refreshTexture)", var9),var6);
630 var6.printStackTrace();
631 }
632 }
633 }
634
635 /**
636 * Returns a BufferedImage read off the provided input stream. Args: inputStream
637 */
638 private BufferedImage readTextureImage(InputStream par1InputStream) throws IOException
639 {
640 BufferedImage var2 = ImageIO.read(par1InputStream);
641 par1InputStream.close();
642 return var2;
643 }
644
645 public void bindTexture(int par1)
646 {
647 if (par1 >= 0)
648 {
649 GL11.glBindTexture(GL11.GL_TEXTURE_2D, par1);
650 }
651 }
652 }