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 TexturePackBase 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 TexturePackBase 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 /**
419 * Return a ThreadDownloadImageData instance for the given URL. If it does not already exist, it is created and
420 * uses the passed ImageBuffer. If it does, its reference count is incremented.
421 */
422 public ThreadDownloadImageData obtainImageData(String par1Str, ImageBuffer par2ImageBuffer)
423 {
424 ThreadDownloadImageData var3 = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
425
426 if (var3 == null)
427 {
428 this.urlToImageDataMap.put(par1Str, new ThreadDownloadImageData(par1Str, par2ImageBuffer));
429 }
430 else
431 {
432 ++var3.referenceCount;
433 }
434
435 return var3;
436 }
437
438 /**
439 * Decrements the reference count for a given URL, deleting the image data if the reference count hits 0
440 */
441 public void releaseImageData(String par1Str)
442 {
443 ThreadDownloadImageData var2 = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str);
444
445 if (var2 != null)
446 {
447 --var2.referenceCount;
448
449 if (var2.referenceCount == 0)
450 {
451 if (var2.textureName >= 0)
452 {
453 this.deleteTexture(var2.textureName);
454 }
455
456 this.urlToImageDataMap.remove(par1Str);
457 }
458 }
459 }
460
461 public void registerTextureFX(TextureFX par1TextureFX)
462 {
463 TextureFXManager.instance().onPreRegisterEffect(par1TextureFX);
464 this.textureList.add(par1TextureFX);
465 par1TextureFX.onTick();
466 }
467
468 public void updateDynamicTextures()
469 {
470 int var1 = -1;
471
472 for (int var2 = 0; var2 < this.textureList.size(); ++var2)
473 {
474 TextureFX var3 = (TextureFX)this.textureList.get(var2);
475 var3.anaglyphEnabled = this.options.anaglyph;
476 if (!TextureFXManager.instance().onUpdateTextureEffect(var3))
477 {
478 continue;
479 }
480
481 Dimension dim = TextureFXManager.instance().getTextureDimensions(var3);
482 int tWidth = dim.width >> 4;
483 int tHeight = dim.height >> 4;
484 int tLen = tWidth * tHeight << 2;
485
486 if (var3.imageData.length == tLen)
487 {
488 this.imageData.clear();
489 this.imageData.put(var3.imageData);
490 this.imageData.position(0).limit(var3.imageData.length);
491 }
492 else
493 {
494 TextureFXManager.instance().scaleTextureFXData(var3.imageData, imageData, tWidth, tLen);
495 }
496
497 if (var3.iconIndex != var1)
498 {
499 var3.bindImage(this);
500 var1 = var3.iconIndex;
501 }
502
503 for (int var4 = 0; var4 < var3.tileSize; ++var4)
504 {
505 int xOffset = var3.iconIndex % 16 * tWidth + var4 * tWidth;
506 for (int var5 = 0; var5 < var3.tileSize; ++var5)
507 {
508 int yOffset = var3.iconIndex / 16 * tHeight + var5 * tHeight;
509 GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, xOffset, yOffset, tWidth, tHeight, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, this.imageData);
510 }
511 }
512 }
513 }
514
515 /**
516 * Call setupTexture on all currently-loaded textures again to account for changes in rendering options
517 */
518 public void refreshTextures()
519 {
520 TexturePackBase var1 = this.texturePack.getSelectedTexturePack();
521 Iterator var2 = this.textureNameToImageMap.getKeySet().iterator();
522 BufferedImage var4;
523
524 while (var2.hasNext())
525 {
526 int var3 = ((Integer)var2.next()).intValue();
527 var4 = (BufferedImage)this.textureNameToImageMap.lookup(var3);
528 this.setupTexture(var4, var3);
529 }
530
531 ThreadDownloadImageData var8;
532
533 for (var2 = this.urlToImageDataMap.values().iterator(); var2.hasNext(); var8.textureSetupComplete = false)
534 {
535 var8 = (ThreadDownloadImageData)var2.next();
536 }
537
538 var2 = this.textureMap.keySet().iterator();
539 String var9;
540
541 while (var2.hasNext())
542 {
543 var9 = (String)var2.next();
544
545 try
546 {
547 if (var9.startsWith("##"))
548 {
549 var4 = this.unwrapImageByColumns(this.readTextureImage(var1.getResourceAsStream(var9.substring(2))));
550 }
551 else if (var9.startsWith("%clamp%"))
552 {
553 this.clampTexture = true;
554 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(7)));
555 }
556 else if (var9.startsWith("%blur%"))
557 {
558 this.blurTexture = true;
559 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(6)));
560 }
561 else if (var9.startsWith("%blurclamp%"))
562 {
563 this.blurTexture = true;
564 this.clampTexture = true;
565 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(11)));
566 }
567 else
568 {
569 var4 = this.readTextureImage(var1.getResourceAsStream(var9));
570 }
571
572 int var5 = ((Integer)this.textureMap.get(var9)).intValue();
573 this.setupTexture(var4, var5);
574 this.blurTexture = false;
575 this.clampTexture = false;
576 }
577 catch (Exception var7)
578 {
579 log.log(Level.INFO,String.format("An error occured reading texture file %s (refreshTexture)", var9),var7);
580 var7.printStackTrace();
581 }
582 }
583
584 var2 = this.textureContentsMap.keySet().iterator();
585
586 while (var2.hasNext())
587 {
588 var9 = (String)var2.next();
589
590 try
591 {
592 if (var9.startsWith("##"))
593 {
594 var4 = this.unwrapImageByColumns(this.readTextureImage(var1.getResourceAsStream(var9.substring(2))));
595 }
596 else if (var9.startsWith("%clamp%"))
597 {
598 this.clampTexture = true;
599 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(7)));
600 }
601 else if (var9.startsWith("%blur%"))
602 {
603 this.blurTexture = true;
604 var4 = this.readTextureImage(var1.getResourceAsStream(var9.substring(6)));
605 }
606 else
607 {
608 var4 = this.readTextureImage(var1.getResourceAsStream(var9));
609 }
610
611 this.getImageContents(var4, (int[])this.textureContentsMap.get(var9));
612 this.blurTexture = false;
613 this.clampTexture = false;
614 }
615 catch (Exception var6)
616 {
617 log.log(Level.INFO,String.format("An error occured reading texture file data %s (refreshTexture)", var9),var6);
618 var6.printStackTrace();
619 }
620 }
621 }
622
623 /**
624 * Returns a BufferedImage read off the provided input stream. Args: inputStream
625 */
626 private BufferedImage readTextureImage(InputStream par1InputStream) throws IOException
627 {
628 BufferedImage var2 = ImageIO.read(par1InputStream);
629 par1InputStream.close();
630 return var2;
631 }
632
633 public void bindTexture(int par1)
634 {
635 if (par1 >= 0)
636 {
637 GL11.glBindTexture(GL11.GL_TEXTURE_2D, par1);
638 }
639 }
640 }