001package net.minecraft.client.renderer; 002 003import cpw.mods.fml.client.TextureFXManager; 004import cpw.mods.fml.common.FMLLog; 005import cpw.mods.fml.relauncher.Side; 006import cpw.mods.fml.relauncher.SideOnly; 007import java.awt.Color; 008import java.awt.Dimension; 009import java.awt.Graphics; 010import java.awt.image.BufferedImage; 011import java.io.IOException; 012import java.io.InputStream; 013import java.nio.IntBuffer; 014import java.util.HashMap; 015import java.util.Iterator; 016import java.util.Map; 017import java.util.logging.Level; 018import java.util.logging.Logger; 019 020import javax.imageio.ImageIO; 021import net.minecraft.client.Minecraft; 022import net.minecraft.client.renderer.texture.TextureMap; 023import net.minecraft.client.settings.GameSettings; 024import net.minecraft.client.texturepacks.ITexturePack; 025import net.minecraft.client.texturepacks.TexturePackList; 026import net.minecraft.util.Icon; 027import net.minecraft.util.IntHashMap; 028import org.lwjgl.opengl.GL11; 029import org.lwjgl.opengl.GL12; 030 031import net.minecraftforge.client.ForgeHooksClient; 032 033@SideOnly(Side.CLIENT) 034public class RenderEngine 035{ 036 private HashMap textureMap = new HashMap(); 037 038 /** Texture contents map (key: texture name, value: int[] contents) */ 039 private HashMap textureContentsMap = new HashMap(); 040 041 /** A mapping from GL texture names (integers) to BufferedImage instances */ 042 private IntHashMap textureNameToImageMap = new IntHashMap(); 043 044 /** Stores the image data for the texture. */ 045 private IntBuffer imageData = GLAllocation.createDirectIntBuffer(4194304); 046 047 /** A mapping from image URLs to ThreadDownloadImageData instances */ 048 private Map urlToImageDataMap = new HashMap(); 049 050 /** Reference to the GameSettings object */ 051 private GameSettings options; 052 053 /** Texture pack */ 054 public TexturePackList texturePack; 055 056 /** Missing texture image */ 057 private BufferedImage missingTextureImage = new BufferedImage(64, 64, 2); 058 public final TextureMap textureMapBlocks; 059 public final TextureMap textureMapItems; 060 private int boundTexture; 061 062 public static Logger log = FMLLog.getLogger(); 063 064 public RenderEngine(TexturePackList par1TexturePackList, GameSettings par2GameSettings) 065 { 066 this.texturePack = par1TexturePackList; 067 this.options = par2GameSettings; 068 Graphics graphics = this.missingTextureImage.getGraphics(); 069 graphics.setColor(Color.WHITE); 070 graphics.fillRect(0, 0, 64, 64); 071 graphics.setColor(Color.BLACK); 072 int i = 10; 073 int j = 0; 074 075 while (i < 64) 076 { 077 String s = j++ % 2 == 0 ? "missing" : "texture"; 078 graphics.drawString(s, 1, i); 079 i += graphics.getFont().getSize(); 080 081 if (j % 2 == 0) 082 { 083 i += 5; 084 } 085 } 086 087 graphics.dispose(); 088 this.textureMapBlocks = new TextureMap(0, "terrain", "textures/blocks/", this.missingTextureImage); 089 this.textureMapItems = new TextureMap(1, "items", "textures/items/", this.missingTextureImage); 090 } 091 092 public int[] getTextureContents(String par1Str) 093 { 094 ITexturePack itexturepack = this.texturePack.getSelectedTexturePack(); 095 int[] aint = (int[])this.textureContentsMap.get(par1Str); 096 097 if (aint != null) 098 { 099 return aint; 100 } 101 else 102 { 103 try 104 { 105 InputStream inputstream = itexturepack.getResourceAsStream(par1Str); 106 int[] aint1; 107 108 if (inputstream == null) 109 { 110 aint1 = this.getImageContentsAndAllocate(this.missingTextureImage); 111 } 112 else 113 { 114 BufferedImage bufferedimage = this.readTextureImage(inputstream); 115 TextureFXManager.instance().fixTransparency(bufferedimage, par1Str); 116 aint1 = this.getImageContentsAndAllocate(bufferedimage); 117 } 118 119 this.textureContentsMap.put(par1Str, aint1); 120 return aint1; 121 } 122 catch (Exception ioexception) 123 { 124 log.log(Level.INFO, String.format("An error occured reading texture file %s (getTexture)", par1Str), ioexception); 125 ioexception.printStackTrace(); 126 int[] aint2 = this.getImageContentsAndAllocate(this.missingTextureImage); 127 this.textureContentsMap.put(par1Str, aint2); 128 return aint2; 129 } 130 } 131 } 132 133 private int[] getImageContentsAndAllocate(BufferedImage par1BufferedImage) 134 { 135 return this.getImageContents(par1BufferedImage, new int[par1BufferedImage.getWidth() * par1BufferedImage.getHeight()]); 136 } 137 138 private int[] getImageContents(BufferedImage par1BufferedImage, int[] par2ArrayOfInteger) 139 { 140 int i = par1BufferedImage.getWidth(); 141 int j = par1BufferedImage.getHeight(); 142 par1BufferedImage.getRGB(0, 0, i, j, par2ArrayOfInteger, 0, i); 143 return par2ArrayOfInteger; 144 } 145 146 public void bindTexture(String par1Str) 147 { 148 this.bindTexture(this.getTexture(par1Str)); 149 } 150 151 private void bindTexture(int par1) 152 { 153 if (par1 != this.boundTexture) 154 { 155 GL11.glBindTexture(GL11.GL_TEXTURE_2D, par1); 156 this.boundTexture = par1; 157 } 158 } 159 160 public void resetBoundTexture() 161 { 162 this.boundTexture = -1; 163 } 164 165 public int getTexture(String par1Str) 166 { 167 if (par1Str.equals("/terrain.png")) 168 { 169 this.textureMapBlocks.getTexture().bindTexture(0); 170 return this.textureMapBlocks.getTexture().getGlTextureId(); 171 } 172 else if (par1Str.equals("/gui/items.png")) 173 { 174 this.textureMapItems.getTexture().bindTexture(0); 175 return this.textureMapItems.getTexture().getGlTextureId(); 176 } 177 else 178 { 179 Integer integer = (Integer)this.textureMap.get(par1Str); 180 181 if (integer != null) 182 { 183 return integer.intValue(); 184 } 185 else 186 { 187 String s1 = par1Str; 188 189 try 190 { 191 ForgeHooksClient.onTextureLoadPre(par1Str); 192 int i = GLAllocation.generateTextureNames(); 193 TextureFXManager.instance().bindTextureToName(par1Str, i); 194 boolean flag = par1Str.startsWith("%blur%"); 195 196 if (flag) 197 { 198 par1Str = par1Str.substring(6); 199 } 200 201 boolean flag1 = par1Str.startsWith("%clamp%"); 202 203 if (flag1) 204 { 205 par1Str = par1Str.substring(7); 206 } 207 208 InputStream inputstream = this.texturePack.getSelectedTexturePack().getResourceAsStream(par1Str); 209 210 if (inputstream == null) 211 { 212 this.setupTextureExt(this.missingTextureImage, i, flag, flag1); 213 } 214 else 215 { 216 BufferedImage bufferedimage = this.readTextureImage(inputstream); 217 TextureFXManager.instance().fixTransparency(bufferedimage, par1Str); 218 this.setupTextureExt(bufferedimage, i, flag, flag1); 219 } 220 221 this.textureMap.put(s1, Integer.valueOf(i)); 222 ForgeHooksClient.onTextureLoad(par1Str, texturePack.getSelectedTexturePack()); 223 return i; 224 } 225 catch (Exception exception) 226 { 227 exception.printStackTrace(); 228 int j = GLAllocation.generateTextureNames(); 229 this.setupTexture(this.missingTextureImage, j); 230 this.textureMap.put(par1Str, Integer.valueOf(j)); 231 return j; 232 } 233 } 234 } 235 } 236 237 /** 238 * Copy the supplied image onto a newly-allocated OpenGL texture, returning the allocated texture name 239 */ 240 public int allocateAndSetupTexture(BufferedImage par1BufferedImage) 241 { 242 int i = GLAllocation.generateTextureNames(); 243 this.setupTexture(par1BufferedImage, i); 244 this.textureNameToImageMap.addKey(i, par1BufferedImage); 245 return i; 246 } 247 248 /** 249 * Copy the supplied image onto the specified OpenGL texture 250 */ 251 public void setupTexture(BufferedImage par1BufferedImage, int par2) 252 { 253 this.setupTextureExt(par1BufferedImage, par2, false, false); 254 } 255 256 public void setupTextureExt(BufferedImage par1BufferedImage, int par2, boolean par3, boolean par4) 257 { 258 this.bindTexture(par2); 259 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); 260 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); 261 262 if (par3) 263 { 264 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); 265 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); 266 } 267 268 if (par4) 269 { 270 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP); 271 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP); 272 } 273 else 274 { 275 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT); 276 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT); 277 } 278 279 int j = par1BufferedImage.getWidth(); 280 int k = par1BufferedImage.getHeight(); 281 TextureFXManager.instance().setTextureDimensions(par2, j, k); 282 int[] aint = new int[j * k]; 283 par1BufferedImage.getRGB(0, 0, j, k, aint, 0, j); 284 285 if (this.options != null && this.options.anaglyph) 286 { 287 aint = this.colorToAnaglyph(aint); 288 } 289 290 this.imageData.clear(); 291 this.imageData.put(aint); 292 this.imageData.position(0).limit(aint.length); 293 GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, j, k, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, this.imageData); 294 } 295 296 private int[] colorToAnaglyph(int[] par1ArrayOfInteger) 297 { 298 int[] aint1 = new int[par1ArrayOfInteger.length]; 299 300 for (int i = 0; i < par1ArrayOfInteger.length; ++i) 301 { 302 int j = par1ArrayOfInteger[i] >> 24 & 255; 303 int k = par1ArrayOfInteger[i] >> 16 & 255; 304 int l = par1ArrayOfInteger[i] >> 8 & 255; 305 int i1 = par1ArrayOfInteger[i] & 255; 306 int j1 = (k * 30 + l * 59 + i1 * 11) / 100; 307 int k1 = (k * 30 + l * 70) / 100; 308 int l1 = (k * 30 + i1 * 70) / 100; 309 aint1[i] = j << 24 | j1 << 16 | k1 << 8 | l1; 310 } 311 312 return aint1; 313 } 314 315 public void createTextureFromBytes(int[] par1ArrayOfInteger, int par2, int par3, int par4) 316 { 317 this.bindTexture(par4); 318 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); 319 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); 320 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT); 321 GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT); 322 323 if (this.options != null && this.options.anaglyph) 324 { 325 par1ArrayOfInteger = this.colorToAnaglyph(par1ArrayOfInteger); 326 } 327 328 this.imageData.clear(); 329 this.imageData.put(par1ArrayOfInteger); 330 this.imageData.position(0).limit(par1ArrayOfInteger.length); 331 GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, par2, par3, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, this.imageData); 332 } 333 334 /** 335 * Deletes a single GL texture 336 */ 337 public void deleteTexture(int par1) 338 { 339 this.textureNameToImageMap.removeObject(par1); 340 GL11.glDeleteTextures(par1); 341 } 342 343 /** 344 * Takes a URL of a downloadable image and the name of the local image to be used as a fallback. If the image has 345 * been downloaded, returns the GL texture of the downloaded image, otherwise returns the GL texture of the fallback 346 * image. 347 */ 348 public int getTextureForDownloadableImage(String par1Str, String par2Str) 349 { 350 ThreadDownloadImageData threaddownloadimagedata = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str); 351 352 if (threaddownloadimagedata != null && threaddownloadimagedata.image != null && !threaddownloadimagedata.textureSetupComplete) 353 { 354 if (threaddownloadimagedata.textureName < 0) 355 { 356 threaddownloadimagedata.textureName = this.allocateAndSetupTexture(threaddownloadimagedata.image); 357 } 358 else 359 { 360 this.setupTexture(threaddownloadimagedata.image, threaddownloadimagedata.textureName); 361 } 362 363 threaddownloadimagedata.textureSetupComplete = true; 364 } 365 366 return threaddownloadimagedata != null && threaddownloadimagedata.textureName >= 0 ? threaddownloadimagedata.textureName : (par2Str == null ? -1 : this.getTexture(par2Str)); 367 } 368 369 /** 370 * Checks if urlToImageDataMap has image data for the given key 371 */ 372 public boolean hasImageData(String par1Str) 373 { 374 return this.urlToImageDataMap.containsKey(par1Str); 375 } 376 377 /** 378 * Return a ThreadDownloadImageData instance for the given URL. If it does not already exist, it is created and 379 * uses the passed ImageBuffer. If it does, its reference count is incremented. 380 */ 381 public ThreadDownloadImageData obtainImageData(String par1Str, IImageBuffer par2IImageBuffer) 382 { 383 ThreadDownloadImageData threaddownloadimagedata = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str); 384 385 if (threaddownloadimagedata == null) 386 { 387 this.urlToImageDataMap.put(par1Str, new ThreadDownloadImageData(par1Str, par2IImageBuffer)); 388 } 389 else 390 { 391 ++threaddownloadimagedata.referenceCount; 392 } 393 394 return threaddownloadimagedata; 395 } 396 397 /** 398 * Decrements the reference count for a given URL, deleting the image data if the reference count hits 0 399 */ 400 public void releaseImageData(String par1Str) 401 { 402 ThreadDownloadImageData threaddownloadimagedata = (ThreadDownloadImageData)this.urlToImageDataMap.get(par1Str); 403 404 if (threaddownloadimagedata != null) 405 { 406 --threaddownloadimagedata.referenceCount; 407 408 if (threaddownloadimagedata.referenceCount == 0) 409 { 410 if (threaddownloadimagedata.textureName >= 0) 411 { 412 this.deleteTexture(threaddownloadimagedata.textureName); 413 } 414 415 this.urlToImageDataMap.remove(par1Str); 416 } 417 } 418 } 419 420 public void updateDynamicTextures() 421 { 422 this.textureMapBlocks.updateAnimations(); 423 this.textureMapItems.updateAnimations(); 424 } 425 426 /** 427 * Call setupTexture on all currently-loaded textures again to account for changes in rendering options 428 */ 429 public void refreshTextures() 430 { 431 ITexturePack itexturepack = this.texturePack.getSelectedTexturePack(); 432 this.refreshTextureMaps(); 433 Iterator iterator = this.textureNameToImageMap.getKeySet().iterator(); 434 BufferedImage bufferedimage; 435 436 while (iterator.hasNext()) 437 { 438 int i = ((Integer)iterator.next()).intValue(); 439 bufferedimage = (BufferedImage)this.textureNameToImageMap.lookup(i); 440 this.setupTexture(bufferedimage, i); 441 } 442 443 ThreadDownloadImageData threaddownloadimagedata; 444 445 for (iterator = this.urlToImageDataMap.values().iterator(); iterator.hasNext(); threaddownloadimagedata.textureSetupComplete = false) 446 { 447 threaddownloadimagedata = (ThreadDownloadImageData)iterator.next(); 448 } 449 450 iterator = this.textureMap.keySet().iterator(); 451 String s; 452 453 while (iterator.hasNext()) 454 { 455 s = (String)iterator.next(); 456 457 try 458 { 459 int j = ((Integer)this.textureMap.get(s)).intValue(); 460 boolean flag = s.startsWith("%blur%"); 461 462 if (flag) 463 { 464 s = s.substring(6); 465 } 466 467 boolean flag1 = s.startsWith("%clamp%"); 468 469 if (flag1) 470 { 471 s = s.substring(7); 472 } 473 474 BufferedImage bufferedimage1 = this.readTextureImage(itexturepack.getResourceAsStream(s)); 475 TextureFXManager.instance().fixTransparency(bufferedimage1, s); 476 this.setupTextureExt(bufferedimage1, j, flag, flag1); 477 } 478 catch (Exception ioexception) 479 { 480 log.log(Level.INFO,String.format("An error occured reading texture file %s (refreshTexture)", s), ioexception); 481 ioexception.printStackTrace(); 482 } 483 } 484 485 iterator = this.textureContentsMap.keySet().iterator(); 486 487 while (iterator.hasNext()) 488 { 489 s = (String)iterator.next(); 490 491 try 492 { 493 bufferedimage = this.readTextureImage(itexturepack.getResourceAsStream(s)); 494 TextureFXManager.instance().fixTransparency(bufferedimage, s); 495 this.getImageContents(bufferedimage, (int[])this.textureContentsMap.get(s)); 496 } 497 catch (Exception ioexception1) 498 { 499 log.log(Level.INFO,String.format("An error occured reading texture file data %s (refreshTexture)", s), ioexception1); 500 ioexception1.printStackTrace(); 501 } 502 } 503 504 Minecraft.getMinecraft().fontRenderer.readFontData(); 505 Minecraft.getMinecraft().standardGalacticFontRenderer.readFontData(); 506 } 507 508 /** 509 * Returns a BufferedImage read off the provided input stream. Args: inputStream 510 */ 511 private BufferedImage readTextureImage(InputStream par1InputStream) throws IOException 512 { 513 BufferedImage bufferedimage = ImageIO.read(par1InputStream); 514 par1InputStream.close(); 515 return bufferedimage; 516 } 517 518 public void refreshTextureMaps() 519 { 520 this.textureMapBlocks.refreshTextures(); 521 this.textureMapItems.refreshTextures(); 522 } 523 524 public Icon getMissingIcon(int par1) 525 { 526 switch (par1) 527 { 528 case 0: 529 return this.textureMapBlocks.getMissingIcon(); 530 case 1: 531 default: 532 return this.textureMapItems.getMissingIcon(); 533 } 534 } 535}