#include "stdafx.h" #include "Textures.h" #include "TexturePackRepository.h" #include "HttpTexture.h" #include "MemTexture.h" #include "..\Minecraft.World\InputStream.h" #include "..\Minecraft.World\IntBuffer.h" #include "..\Minecraft.World\ByteBuffer.h" #include "TexturePack.h" #include "Options.h" #include "..\Minecraft.Client\MemTextureProcessor.h" #include "MobSkinMemTextureProcessor.h" #include "PreStitchedTextureMap.h" #include "StitchedTexture.h" #include "Texture.h" #include "..\Minecraft.World\net.minecraft.world.h" #include "..\Minecraft.World\net.minecraft.world.level.h" #include "..\Minecraft.World\StringHelpers.h" bool Textures::MIPMAP = true; C4JRender::eTextureFormat Textures::TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_RxGyBzAw; int Textures::preLoadedIdx[TN_COUNT]; wchar_t *Textures::preLoaded[TN_COUNT] = { L"%blur%misc/pumpkinblur", // L"%blur%/misc/vignette", // Not currently used L"%clamp%misc/shadow", // L"/achievement/bg", // Not currently used L"art/kz", L"environment/clouds", L"environment/rain", L"environment/snow", L"gui/gui", L"gui/icons", L"item/arrows", L"item/boat", L"item/cart", L"item/sign", L"misc/mapbg", L"misc/mapicons", L"misc/water", L"misc/footprint", L"mob/saddle", L"mob/sheep_fur", L"mob/spider_eyes", L"particles", L"mob/chicken", L"mob/cow", L"mob/pig", L"mob/sheep", L"mob/squid", L"mob/wolf", L"mob/wolf_tame", L"mob/wolf_angry", L"mob/creeper", L"mob/ghast", L"mob/ghast_fire", L"mob/zombie", L"mob/pigzombie", L"mob/skeleton", L"mob/slime", L"mob/spider", L"mob/char", L"mob/char1", L"mob/char2", L"mob/char3", L"mob/char4", L"mob/char5", L"mob/char6", L"mob/char7", L"terrain/moon", L"terrain/sun", L"armor/power", // 1.8.2 L"mob/cavespider", L"mob/enderman", L"mob/silverfish", L"mob/enderman_eyes", L"misc/explosion", L"item/xporb", L"item/chest", L"item/largechest", // 1.3.2 L"item/enderchest", // 1.0.1 L"mob/redcow", L"mob/snowman", L"mob/enderdragon/ender", L"mob/fire", L"mob/lava", L"mob/villager/villager", L"mob/villager/farmer", L"mob/villager/librarian", L"mob/villager/priest", L"mob/villager/smith", L"mob/villager/butcher", L"mob/enderdragon/crystal", L"mob/enderdragon/shuffle", L"mob/enderdragon/beam", L"mob/enderdragon/ender_eyes", L"%blur%misc/glint", L"item/book", L"misc/tunnel", L"misc/particlefield", L"terrain/moon_phases", // 1.2.3 L"mob/ozelot", L"mob/cat_black", L"mob/cat_red", L"mob/cat_siamese", L"mob/villager_golem", L"mob/skeleton_wither", // TU 14 L"mob/wolf_collar", L"mob/zombie_villager", #ifdef _LARGE_WORLDS L"misc/additionalmapicons", #endif L"font/Default", L"font/alternate", // skin packs /* L"/SP1", L"/SP2", L"/SP3", L"/SPF", // themes L"/ThSt", L"/ThIr", L"/ThGo", L"/ThDi", // gamerpics L"/GPAn", L"/GPCo", L"/GPEn", L"/GPFo", L"/GPTo", L"/GPBA", L"/GPFa", L"/GPME", L"/GPMF", L"/GPMM", L"/GPSE", // avatar items L"/AH_0006", L"/AH_0003", L"/AH_0007", L"/AH_0005", L"/AH_0004", L"/AH_0001", L"/AH_0002", L"/AT_0001", L"/AT_0002", L"/AT_0003", L"/AT_0004", L"/AT_0005", L"/AT_0006", L"/AT_0007", L"/AT_0008", L"/AT_0009", L"/AT_0010", L"/AT_0011", L"/AT_0012", L"/AP_0001", L"/AP_0002", L"/AP_0003", L"/AP_0004", L"/AP_0005", L"/AP_0006", L"/AP_0007", L"/AP_0009", L"/AP_0010", L"/AP_0011", L"/AP_0012", L"/AP_0013", L"/AP_0014", L"/AP_0015", L"/AP_0016", L"/AP_0017", L"/AP_0018", L"/AA_0001", L"/AT_0013", L"/AT_0014", L"/AT_0015", L"/AT_0016", L"/AT_0017", L"/AT_0018", L"/AP_0019", L"/AP_0020", L"/AP_0021", L"/AP_0022", L"/AP_0023", L"/AH_0008", L"/AH_0009",*/ L"gui/items", L"terrain", }; Textures::Textures(TexturePackRepository *skins, Options *options) { // pixels = MemoryTracker::createIntBuffer(2048 * 2048); // 4J removed - now just creating this buffer when we need it missingNo = new BufferedImage(16, 16, BufferedImage::TYPE_INT_ARGB); this->skins = skins; this->options = options; /* 4J - TODO, maybe... Graphics g = missingNo.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, 64, 64); g.setColor(Color.BLACK); int y = 10; int i = 0; while (y < 64) { String text = (i++ % 2 == 0) ? "missing" : "texture"; g.drawString(text, 1, y); y += g.getFont().getSize(); if (i % 2 == 0) y += 5; } g.dispose(); */ // 4J Stu - Changed these to our PreStitchedTextureMap from TextureMap terrain = new PreStitchedTextureMap(Icon::TYPE_TERRAIN, L"terrain", L"textures/blocks/", missingNo, true); items = new PreStitchedTextureMap(Icon::TYPE_ITEM, L"items", L"textures/items/", missingNo, true); // 4J - added - preload a set of commonly used textures that can then be referenced directly be an enumerated type rather by string loadIndexedTextures(); } void Textures::loadIndexedTextures() { // 4J - added - preload a set of commonly used textures that can then be referenced directly be an enumerated type rather by string for( int i = 0; i < TN_COUNT - 2; i++ ) { preLoadedIdx[i] = loadTexture((TEXTURE_NAME)i, wstring(preLoaded[i]) + L".png"); } } intArray Textures::loadTexturePixels(TEXTURE_NAME texId, const wstring& resourceName) { TexturePack *skin = skins->getSelected(); { intArray id = pixelsMap[resourceName]; // 4J - if resourceName isn't in the map, it should add an element and as that will use the default constructor, its // internal data pointer will be NULL if (id.data != NULL) return id; } // 4J - removed try/catch // try { intArray res; //wstring in = skin->getResource(resourceName); if (false)// 4J - removed - was ( in == NULL) { res = loadTexturePixels(missingNo); } else { BufferedImage *bufImage = readImage(texId, resourceName); //in); res = loadTexturePixels(bufImage); delete bufImage; } pixelsMap[resourceName] = res; return res; /* } catch (IOException e) { e.printStackTrace(); int[] res = loadTexturePixels(missingNo); pixelsMap.put(resourceName, res); return res; } */ } intArray Textures::loadTexturePixels(BufferedImage *img) { int w = img->getWidth(); int h = img->getHeight(); intArray pixels(w*h); return loadTexturePixels(img, pixels); } intArray Textures::loadTexturePixels(BufferedImage *img, intArray pixels) { int w = img->getWidth(); int h = img->getHeight(); img->getRGB(0, 0, w, h, pixels, 0, w); return pixels; } int Textures::loadTexture(int idx) { if( idx == -1 ) { return 0; } else { if ( idx == TN_TERRAIN ) { terrain->getStitchedTexture()->bind(0); return terrain->getStitchedTexture()->getGlId(); } if ( idx == TN_GUI_ITEMS) { items->getStitchedTexture()->bind(0); return items->getStitchedTexture()->getGlId(); } return preLoadedIdx[idx]; } } // 4J added - textures default to standard 32-bit RGBA format, but where we can, use an 8-bit format. There's 3 different varieties of these currently // in the renderer that map the single 8-bit channel to RGBA differently. void Textures::setTextureFormat(const wstring& resourceName) { // 4J Stu - These texture formats are not currently in the render header { TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_RxGyBzAw; } } void Textures::bindTexture(const wstring &resourceName) { bind(loadTexture(TN_COUNT,resourceName)); } // 4J Added void Textures::bindTexture(int resourceId) { bind(loadTexture(resourceId)); } void Textures::bind(int id) { //if (id != lastBoundId) { if(id < 0) return; glBindTexture(GL_TEXTURE_2D, id); // lastBoundId = id; } } void Textures::clearLastBoundId() { lastBoundId = -1; } int Textures::loadTexture(TEXTURE_NAME texId, const wstring& resourceName) { // char buf[256]; // wcstombs(buf, resourceName.c_str(), 256); // printf("Textures::loadTexture name - %s\n",buf); //if (resourceName.compare(L"/terrain.png") == 0) //{ // terrain->getStitchedTexture()->bind(0); // return terrain->getStitchedTexture()->getGlId(); //} //if (resourceName.compare(L"/gui/items.png") == 0) //{ // items->getStitchedTexture()->bind(0); // return items->getStitchedTexture()->getGlId(); //} // If the texture is not present in the idMap, load it, otherwise return its id { bool inMap = ( idMap.find(resourceName) != idMap.end() ); int id = idMap[resourceName]; if (inMap) return id; } wstring pathName = resourceName; // 4J - added special cases to avoid mipmapping on clouds & shadows if( (resourceName == L"environment/clouds.png") || (resourceName == L"%clamp%misc/shadow.png") || (resourceName == L"%blur%misc/pumpkinblur.png") || (resourceName == L"%clamp%misc/shadow.png") || (resourceName == L"gui/icons.png" ) || (resourceName == L"gui/gui.png" ) || (resourceName == L"misc/footprint.png") ) { MIPMAP = false; } setTextureFormat(resourceName); // 4J - removed try/catch // try { int id = MemoryTracker::genTextures(); wstring prefix = L"%blur%"; bool blur = resourceName.substr(0, prefix.size()).compare(prefix) == 0; //resourceName.startsWith("%blur%"); if (blur) pathName = resourceName.substr(6); prefix = L"%clamp%"; bool clamp = resourceName.substr(0, prefix.size()).compare(prefix) == 0; //resourceName.startsWith("%clamp%"); if (clamp) pathName = resourceName.substr(7); //wstring in = skins->getSelected()->getResource(pathName); if (false ) // 4J - removed was ( in == NULL) { loadTexture(missingNo, id, blur, clamp); } else { // 4J Stu - Get resource above just returns the name for texture packs BufferedImage *bufImage = readImage(texId, pathName); //in); loadTexture(bufImage, id, blur, clamp); delete bufImage; } idMap[resourceName] = id; MIPMAP = true; // 4J added TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_RxGyBzAw; return id; /* } catch (IOException e) { e.printStackTrace(); MemoryTracker.genTextures(ib); int id = ib.get(0); loadTexture(missingNo, id); idMap.put(resourceName, id); return id; } */ } int Textures::getTexture(BufferedImage *img, C4JRender::eTextureFormat format, bool mipmap) { int id = MemoryTracker::genTextures(); TEXTURE_FORMAT = format; MIPMAP = mipmap; loadTexture(img, id); TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_RxGyBzAw; MIPMAP = true; loadedImages[id] = img; return id; } void Textures::loadTexture(BufferedImage *img, int id) { // printf("Textures::loadTexture BufferedImage %d\n",id); loadTexture(img, id, false, false); } void Textures::loadTexture(BufferedImage *img, int id, bool blur, bool clamp) { // printf("Textures::loadTexture BufferedImage with blur and clamp %d\n",id); int iMipLevels=1; MemSect(33); glBindTexture(GL_TEXTURE_2D, id); if (MIPMAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); */ } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } if (blur) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if (clamp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } int w = img->getWidth(); int h = img->getHeight(); intArray rawPixels(w*h); img->getRGB(0, 0, w, h, rawPixels, 0, w); if (options != NULL && options->anaglyph3d) { rawPixels = anaglyph(rawPixels); } byteArray newPixels(w * h * 4); for (unsigned int i = 0; i < rawPixels.length; i++) { int a = (rawPixels[i] >> 24) & 0xff; int r = (rawPixels[i] >> 16) & 0xff; int g = (rawPixels[i] >> 8) & 0xff; int b = (rawPixels[i]) & 0xff; newPixels[i * 4 + 0] = (byte) r; newPixels[i * 4 + 1] = (byte) g; newPixels[i * 4 + 2] = (byte) b; newPixels[i * 4 + 3] = (byte) a; } // 4J - now creating a buffer of the size we require dynamically ByteBuffer *pixels = MemoryTracker::createByteBuffer(w * h * 4); pixels->clear(); pixels->put(newPixels); pixels->position(0)->limit(newPixels.length); delete[] rawPixels.data; delete[] newPixels.data; if (MIPMAP) { // 4J-PB - In the new XDK, the CreateTexture will fail if the number of mipmaps is higher than the width & height passed in will allow! int iWidthMips=1; int iHeightMips=1; while((8<5)iMipLevels = 5; RenderManager.TextureSetTextureLevels(iMipLevels); // 4J added } RenderManager.TextureData(w,h,pixels->getBuffer(),0,TEXTURE_FORMAT); //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixels); if (MIPMAP) { for (int level = 1; level < iMipLevels; level++) { int ow = w >> (level - 1); // int oh = h >> (level - 1); int ww = w >> level; int hh = h >> level; // 4J - added tempData so we aren't overwriting source data unsigned int *tempData = new unsigned int[ww * hh]; // 4J - added - have we loaded mipmap data for this level? Use that rather than generating if possible if( img->getData( level ) ) { memcpy( tempData, img->getData( level ), ww * hh * 4); // Swap ARGB to RGBA for( int i = 0; i < ww * hh ; i++ ) { tempData[i] = ( tempData[i] >> 24 ) | (tempData[i] << 8 ); } } else { for (int x = 0; x < ww; x++) for (int y = 0; y < hh; y++) { int c0 = pixels->getInt(((x * 2 + 0) + (y * 2 + 0) * ow) * 4); int c1 = pixels->getInt(((x * 2 + 1) + (y * 2 + 0) * ow) * 4); int c2 = pixels->getInt(((x * 2 + 1) + (y * 2 + 1) * ow) * 4); int c3 = pixels->getInt(((x * 2 + 0) + (y * 2 + 1) * ow) * 4); // 4J - convert our RGBA texels to ARGB that crispBlend is expecting c0 = ( ( c0 >> 8 ) & 0x00ffffff ) | ( c0 << 24 ); c1 = ( ( c1 >> 8 ) & 0x00ffffff ) | ( c1 << 24 ); c2 = ( ( c2 >> 8 ) & 0x00ffffff ) | ( c2 << 24 ); c3 = ( ( c3 >> 8 ) & 0x00ffffff ) | ( c3 << 24 ); int col = Texture::crispBlend(Texture::crispBlend(c0, c1), Texture::crispBlend(c2, c3)); // 4J - and back from ARGB -> RGBA col = ( col << 8 ) | (( col >> 24 ) & 0xff); tempData[x + y * ww] = col; } } for (int x = 0; x < ww; x++ ) for (int y = 0; y < hh; y++) { pixels->putInt((x + y * ww) * 4, tempData[x + y * ww]); } delete [] tempData; RenderManager.TextureData(ww,hh,pixels->getBuffer(),level,TEXTURE_FORMAT); } } /* * if (MIPMAP) { GLU.gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, w, h, * GL_RGBA, GL_UNSIGNED_BYTE, pixels); } else { } */ delete pixels; // 4J - now creating this dynamically MemSect(0); } intArray Textures::anaglyph(intArray rawPixels) { intArray result(rawPixels.length); for (unsigned int i = 0; i < rawPixels.length; i++) { int a = (rawPixels[i] >> 24) & 0xff; int r = (rawPixels[i] >> 16) & 0xff; int g = (rawPixels[i] >> 8) & 0xff; int b = (rawPixels[i]) & 0xff; int rr = (r * 30 + g * 59 + b * 11) / 100; int gg = (r * 30 + g * 70) / (100); int bb = (r * 30 + b * 70) / (100); result[i] = a << 24 | rr << 16 | gg << 8 | bb; } delete[] rawPixels.data; return result; } void Textures::replaceTexture(intArray rawPixels, int w, int h, int id) { bind(id); // Removed in Java #if 0 if (MIPMAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); */ } else #endif { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); if (options != NULL && options->anaglyph3d) { rawPixels = anaglyph(rawPixels); } byteArray newPixels(w * h * 4); for (unsigned int i = 0; i < rawPixels.length; i++) { int a = (rawPixels[i] >> 24) & 0xff; int r = (rawPixels[i] >> 16) & 0xff; int g = (rawPixels[i] >> 8) & 0xff; int b = (rawPixels[i]) & 0xff; if (options != NULL && options->anaglyph3d) { int rr = (r * 30 + g * 59 + b * 11) / 100; int gg = (r * 30 + g * 70) / (100); int bb = (r * 30 + b * 70) / (100); r = rr; g = gg; b = bb; } newPixels[i * 4 + 0] = (byte) r; newPixels[i * 4 + 1] = (byte) g; newPixels[i * 4 + 2] = (byte) b; newPixels[i * 4 + 3] = (byte) a; } ByteBuffer *pixels = MemoryTracker::createByteBuffer(w * h * 4); // 4J - now creating dynamically pixels->put(newPixels); pixels->position(0)->limit(newPixels.length); delete [] newPixels.data; // New // glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixels); RenderManager.TextureDataUpdate(0, 0,w,h,pixels->getBuffer(),0); // Old //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); delete pixels; } // 4J - added. This is a more minimal version of replaceTexture that assumes the texture bytes are already in order, and so doesn't do any of the extra copying round // that the original java version does void Textures::replaceTextureDirect(intArray rawPixels, int w, int h, int id) { glBindTexture(GL_TEXTURE_2D, id); // Remove in Java #if 0 if (MIPMAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); */ } else #endif { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); RenderManager.TextureDataUpdate(0, 0, w, h, rawPixels.data, 0); } // 4J - added. This is a more minimal version of replaceTexture that assumes the texture bytes are already in order, and so doesn't do any of the extra copying round // that the original java version does void Textures::replaceTextureDirect(shortArray rawPixels, int w, int h, int id) { glBindTexture(GL_TEXTURE_2D, id); // Remove in Java #if 0 if (MIPMAP) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 4); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); */ } else #endif { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); RenderManager.TextureDataUpdate(0, 0, w, h, rawPixels.data, 0); } void Textures::releaseTexture(int id) { loadedImages.erase(id); glDeleteTextures(id); } int Textures::loadHttpTexture(const wstring& url, const wstring& backup) { HttpTexture *texture = httpTextures[url]; if (texture != NULL) { if (texture->loadedImage != NULL && !texture->isLoaded) { if (texture->id < 0) { texture->id = getTexture(texture->loadedImage); } else { loadTexture(texture->loadedImage, texture->id); } texture->isLoaded = true; } } if (texture == NULL || texture->id < 0) { if (backup.empty() ) return -1; return loadTexture(TN_COUNT, backup); } return texture->id; } int Textures::loadHttpTexture(const wstring& url, int backup) { HttpTexture *texture = httpTextures[url]; if (texture != NULL) { if (texture->loadedImage != NULL && !texture->isLoaded) { if (texture->id < 0) { texture->id = getTexture(texture->loadedImage); } else { loadTexture(texture->loadedImage, texture->id); } texture->isLoaded = true; } } if (texture == NULL || texture->id < 0) { return loadTexture(backup); } return texture->id; } bool Textures::hasHttpTexture(const wstring &url) { return httpTextures.find(url) != httpTextures.end(); } HttpTexture *Textures::addHttpTexture(const wstring& url, HttpTextureProcessor *processor) { HttpTexture *texture = httpTextures[url]; if (texture == NULL) { httpTextures[url] = new HttpTexture(url, processor); } else { texture->count++; } return texture; } void Textures::removeHttpTexture(const wstring& url) { HttpTexture *texture = httpTextures[url]; if (texture != NULL) { texture->count--; if (texture->count == 0) { if (texture->id >= 0) releaseTexture(texture->id); httpTextures.erase(url); } } } // 4J-PB - adding for texture in memory (from global title storage) int Textures::loadMemTexture(const wstring& url, const wstring& backup) { MemTexture *texture = NULL; AUTO_VAR(it, memTextures.find(url) ); if (it != memTextures.end()) { texture = (*it).second; } if(texture == NULL && app.IsFileInMemoryTextures(url)) { // If we haven't loaded it yet, but we have the data for it then add it texture = addMemTexture(url, new MobSkinMemTextureProcessor() ); } if(texture != NULL) { if (texture->loadedImage != NULL && !texture->isLoaded) { // 4J - Disable mipmapping in general for skins & capes. Have seen problems with edge-on polys for some eg mumbo jumbo if( ( url.substr(0,7) == L"dlcskin" ) || ( url.substr(0,7) == L"dlccape" ) ) { MIPMAP = false; } if (texture->id < 0) { texture->id = getTexture(texture->loadedImage, C4JRender::TEXTURE_FORMAT_RxGyBzAw, MIPMAP); } else { loadTexture(texture->loadedImage, texture->id); } texture->isLoaded = true; MIPMAP = true; } } if (texture == NULL || texture->id < 0) { if (backup.empty() ) return -1; return loadTexture(TN_COUNT,backup); } return texture->id; } int Textures::loadMemTexture(const wstring& url, int backup) { MemTexture *texture = NULL; AUTO_VAR(it, memTextures.find(url) ); if (it != memTextures.end()) { texture = (*it).second; } if(texture == NULL && app.IsFileInMemoryTextures(url)) { // If we haven't loaded it yet, but we have the data for it then add it texture = addMemTexture(url, new MobSkinMemTextureProcessor() ); } if(texture != NULL) { texture->ticksSinceLastUse = 0; if (texture->loadedImage != NULL && !texture->isLoaded) { // 4J - Disable mipmapping in general for skins & capes. Have seen problems with edge-on polys for some eg mumbo jumbo if( ( url.substr(0,7) == L"dlcskin" ) || ( url.substr(0,7) == L"dlccape" ) ) { MIPMAP = false; } if (texture->id < 0) { texture->id = getTexture(texture->loadedImage, C4JRender::TEXTURE_FORMAT_RxGyBzAw, MIPMAP); } else { loadTexture(texture->loadedImage, texture->id); } texture->isLoaded = true; MIPMAP = true; } } if (texture == NULL || texture->id < 0) { return loadTexture(backup); } return texture->id; } MemTexture *Textures::addMemTexture(const wstring& name,MemTextureProcessor *processor) { MemTexture *texture = NULL; AUTO_VAR(it, memTextures.find(name) ); if (it != memTextures.end()) { texture = (*it).second; } if(texture == NULL) { // can we find it in the app mem files? PBYTE pbData=NULL; DWORD dwBytes=0; app.GetMemFileDetails(name,&pbData,&dwBytes); if(dwBytes!=0) { texture = new MemTexture(name, pbData, dwBytes, processor); memTextures[name] = texture; } else { // 4J Stu - Make an entry for this anyway and we can populate it later memTextures[name] = NULL; } } else { texture->count++; } delete processor; return texture; } // MemTexture *Textures::getMemTexture(const wstring& url, MemTextureProcessor *processor) // { // MemTexture *texture = memTextures[url]; // if (texture != NULL) // { // texture->count++; // } // return texture; // } void Textures::removeMemTexture(const wstring& url) { MemTexture *texture = NULL; AUTO_VAR(it, memTextures.find(url) ); if (it != memTextures.end()) { texture = (*it).second; // If it's NULL then we should just remove the entry if( texture == NULL ) memTextures.erase(url); } if(texture != NULL) { texture->count--; if (texture->count == 0) { if (texture->id >= 0) releaseTexture(texture->id); memTextures.erase(url); delete texture; } } } void Textures::tick(bool updateTextures, bool tickDynamics) // 4J added updateTextures parameter & tickDynamics { MemSect(22); if(tickDynamics) { // 4J - added - if we aren't updating the final renderer textures, just tick each of the dynamic textures instead. This is used so that in frames were we have multiple // ticks due to framerate compensation, that we don't lock the renderer textures twice needlessly and force the CPU to sync with the GPU. if( !updateTextures ) { MemSect(0); return; } // 4J - added - tell renderer that we're about to do a block of dynamic texture updates, so we can unlock the resources after they are done rather than a series of locks/unlocks //RenderManager.TextureDynamicUpdateStart(); terrain->cycleAnimationFrames(); items->cycleAnimationFrames(); //RenderManager.TextureDynamicUpdateEnd(); // 4J added - see comment above } // 4J - go over all the memory textures once per frame, and free any that haven't been used for a while. Ones that are being used will // have their ticksSinceLastUse reset in Textures::loadMemTexture. for( AUTO_VAR(it, memTextures.begin() ); it != memTextures.end(); ) { MemTexture *tex = it->second; if( tex && ( ++tex->ticksSinceLastUse > MemTexture::UNUSED_TICKS_TO_FREE ) ) { if (tex->id >= 0) releaseTexture(tex->id); delete tex; it = memTextures.erase(it); } else { it++; } } MemSect(0); } void Textures::reloadAll() { TexturePack *skin = skins->getSelected(); for( int i = 0; i < TN_COUNT - 2; i++ ) { releaseTexture(preLoadedIdx[i]); } idMap.clear(); loadedImages.clear(); loadIndexedTextures(); pixelsMap.clear(); // 4J Stu - These are not used any more //WaterColor::init(loadTexturePixels(L"misc/watercolor.png")); //GrassColor::init(loadTexturePixels(L"misc/grasscolor.png")); //FoliageColor::init(loadTexturePixels(L"misc/foliagecolor.png")); stitch(); skins->clearInvalidTexturePacks(); #if 0 AUTO_VAR(itEndLI, loadedImages.end() ); for(unordered_map::iterator it = loadedImages.begin(); it != itEndLI; it++ ) { BufferedImage *image = it->second; loadTexture(image, it->first); } AUTO_VAR(itEndHT, httpTextures.end()); for(unordered_map::iterator it = httpTextures.begin(); it != itEndHT; it++ ) { it->second->isLoaded = false; } AUTO_VAR(itEndMT, memTextures.end()); for(unordered_map::iterator it = memTextures.begin(); it != itEndMT; it++ ) { it->second->isLoaded = false; } AUTO_VAR(itEndIM, idMap.end()); for( unordered_map::iterator it = idMap.begin(); it != itEndIM; it++ ) { wstring name = it->first; int id = idMap[name]; BufferedImage *image; wstring prefix = L"%blur%"; bool blur = name.substr(0, prefix.size()).compare(prefix) == 0; //name.startsWith("%blur%"); if (blur) name = name.substr(6); prefix = L"%clamp%"; bool clamp = name.substr(0, prefix.size()).compare(prefix) == 0; //name.startsWith("%clamp%"); if (clamp) name = name.substr(7); image = readImage(skin->getResource(name)); loadTexture(image, id, blur, clamp); delete image; } AUTO_VAR(itEndPM, pixelsMap.end()); for( unordered_map::iterator it = pixelsMap.begin(); it != itEndPM; it++ ) { wstring name = it->first; BufferedImage *image = readImage(skin->getResource(name)); loadTexturePixels(image, pixelsMap[name]); delete image; } #endif // Recalculate fonts //Minecraft::GetInstance()->font->loadCharacterWidths(); //Minecraft::GetInstance()->altFont->loadCharacterWidths(); } void Textures::stitch() { terrain->stitch(); items->stitch(); } Icon *Textures::getMissingIcon(int type) { switch (type) { case Icon::TYPE_ITEM: default: return items->getMissingIcon(); case Icon::TYPE_TERRAIN: return terrain->getMissingIcon(); } } BufferedImage *Textures::readImage(TEXTURE_NAME texId, const wstring& name) // 4J was InputStream *in { BufferedImage *img=NULL; MemSect(32); // is this image one of the Title Update ones? bool isTu = IsTUImage(texId, name); wstring drive = L""; if(!skins->isUsingDefaultSkin() && skins->getSelected()->hasFile(L"res/" + name,false)) { drive = skins->getSelected()->getPath(isTu); img = skins->getSelected()->getImageResource(name, false, isTu, drive); //new BufferedImage(name,false,isTu,drive); } else { const char *pchName=wstringtofilename(name); #ifdef __PS3__ if(app.GetBootedFromDiscPatch() && app.IsFileInPatchList(pchName)) { char *pchUsrDir = app.GetBDUsrDirPath(pchName); wstring wstr (pchUsrDir, pchUsrDir+strlen(pchUsrDir)); if(isTu) { drive= wstr + L"\\Common\\res\\TitleUpdate\\"; } else { drive= wstr + L"\\Common\\"; } } else #endif { drive = skins->getDefault()->getPath(isTu); } const char *pchDrive=wstringtofilename(drive); if(IsOriginalImage(texId, name) || isTu) { img = skins->getDefault()->getImageResource(name,false,isTu,drive); //new BufferedImage(name,false,isTu,drive); } else { img = skins->getDefault()->getImageResource(L"1_2_2/" + name, false, isTu, drive); //new BufferedImage(L"/1_2_2" + name,false,isTu,drive); } } MemSect(0); return img; } // Match the preload images from their enum to avoid a ton of string comparisons TEXTURE_NAME TUImages[] = { TN_POWERED_CREEPER, TN_MOB_ENDERMAN_EYES, TN_MISC_EXPLOSION, TN_MOB_ZOMBIE, TN_MISC_FOOTSTEP, TN_MOB_RED_COW, TN_MOB_SNOWMAN, TN_MOB_ENDERDRAGON, TN_MOB_VILLAGER_VILLAGER, TN_MOB_VILLAGER_FARMER, TN_MOB_VILLAGER_LIBRARIAN, TN_MOB_VILLAGER_PRIEST, TN_MOB_VILLAGER_SMITH, TN_MOB_VILLAGER_BUTCHER, TN_MOB_ENDERDRAGON_ENDEREYES, TN__BLUR__MISC_GLINT, TN_ITEM_BOOK, TN_MISC_PARTICLEFIELD, // TU9 TN_MISC_TUNNEL, TN_MOB_ENDERDRAGON_BEAM, TN_GUI_ITEMS, TN_TERRAIN, TN_MISC_MAPICONS, // TU12 TN_MOB_WITHER_SKELETON, // TU14 TN_TILE_ENDER_CHEST, TN_ART_KZ, TN_MOB_WOLF_TAME, TN_MOB_WOLF_COLLAR, TN_PARTICLES, TN_MOB_ZOMBIE_VILLAGER, #ifdef _LARGE_WORLDS TN_MISC_ADDITIONALMAPICONS, #endif // TU17 TN_DEFAULT_FONT, // TN_ALT_FONT, // Not in TU yet TN_COUNT // Why is this here? }; // This is for any TU textures that aren't part of our enum indexed preload set wchar_t *TUImagePaths[] = { L"font/Default", L"font/Mojangles_7", L"font/Mojangles_11", // TU12 L"armor/cloth_1.png", L"armor/cloth_1_b.png", L"armor/cloth_2.png", L"armor/cloth_2_b.png", NULL }; bool Textures::IsTUImage(TEXTURE_NAME texId, const wstring& name) { int i = 0; if(texId < TN_COUNT) { while(TUImages[i] < TN_COUNT) { if(texId == TUImages[i]) { return true; } i++; } } i = 0; while(TUImagePaths[i]) { if(name.compare(TUImagePaths[i])==0) { return true; } i++; } return false; } TEXTURE_NAME OriginalImages[] = { TN_MOB_CHAR, TN_MOB_CHAR1, TN_MOB_CHAR2, TN_MOB_CHAR3, TN_MOB_CHAR4, TN_MOB_CHAR5, TN_MOB_CHAR6, TN_MOB_CHAR7, TN_MISC_MAPBG, TN_COUNT }; wchar_t *OriginalImagesPaths[] = { L"misc/watercolor.png", NULL }; bool Textures::IsOriginalImage(TEXTURE_NAME texId, const wstring& name) { int i = 0; if(texId < TN_COUNT) { while(OriginalImages[i] < TN_COUNT) { if(texId == OriginalImages[i]) { return true; } i++; } } i = 0; while(OriginalImagesPaths[i]) { if(name.compare(OriginalImagesPaths[i])==0) { return true; } i++; } return false; }