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 }