#include "..\Minecraft.World\ByteBuffer.h" #include "Rect2i.h" #include "TextureManager.h" #include "Texture.h" #include "Options.h" #include "BufferedImage.h" #define MAX_MIP_LEVELS 5 Texture::Texture(const wstring &name, int mode, int width, int height, int depth, int wrapMode, int format, int minFilter, int magFilter, bool mipMap) { _init(name, mode, width, height, depth, wrapMode, format, minFilter, magFilter, mipMap); } void Texture::_init(const wstring &name, int mode, int width, int height, int depth, int wrapMode, int format, int minFilter, int magFilter, bool mipMap) { this->name = name; this->mode = mode; this->width = width; this->height = height; this->depth = depth; this->format = format; this->minFilter = minFilter; this->magFilter = magFilter; this->wrapMode = wrapMode; immediateUpdate = false; m_bInitialised = false; rect = new Rect2i(0, 0, width, height); type = GL_TEXTURE_2D; mipmapped = mipMap || (minFilter != GL_NEAREST && minFilter != GL_LINEAR) || (magFilter != GL_NEAREST && magFilter != GL_LINEAR); m_iMipLevels=1; if(mipmapped) { // 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< MAX_MIP_LEVELS) m_iMipLevels = MAX_MIP_LEVELS; } for( int i = 0 ; i < m_iMipLevels; i++ ) data[i] = nullptr; if (mode != TM_CONTAINER) { glId = glGenTextures(); glBindTexture(type, glId); glTexParameteri(type, GL_TEXTURE_MIN_FILTER, minFilter); glTexParameteri(type, GL_TEXTURE_MAG_FILTER, magFilter); glTexParameteri(type, GL_TEXTURE_WRAP_S, wrapMode); glTexParameteri(type, GL_TEXTURE_WRAP_T, wrapMode); } else glId = -1; managerId = TextureManager::getInstance()->createTextureID(); } void Texture::_init(const wstring &name, int mode, int width, int height, int depth, int wrapMode, int format, int minFilter, int magFilter, BufferedImage *image, bool mipMap) { _init(name, mode, width, height, depth, wrapMode, format, minFilter, magFilter, mipMap); if (image == nullptr) { if (width <= 0 || height <= 0) { valid = false; return; } int baseSize = width * height * depth * 4; data[0] = ByteBuffer::allocateDirect(baseSize); memset(data[0]->getBuffer(), 0, baseSize); data[0]->position(0)->limit(baseSize); if(mipmapped) { for(unsigned int level = 1; level < m_iMipLevels; ++level) { int ww = std::max(1, width >> level); int hh = std::max(1, height >> level); int mipSize = ww * hh * depth * 4; data[level] = ByteBuffer::allocateDirect(mipSize); memset(data[level]->getBuffer(), 0, mipSize); data[level]->position(0)->limit(mipSize); } } if (immediateUpdate) updateOnGPU(); else updated = false; } else { valid = true; transferFromImage(image); if (mode != TM_CONTAINER) { updateOnGPU(); immediateUpdate = false; } } } Texture::Texture(const wstring &name, int mode, int width, int height, int wrapMode, int format, int minFilter, int magFilter, BufferedImage *image, bool mipMap) { _init(name, mode, width, height, 1, wrapMode, format, minFilter, magFilter, image, mipMap); } Texture::Texture(const wstring &name, int mode, int width, int height, int depth, int wrapMode, int format, int minFilter, int magFilter, BufferedImage *image, bool mipMap) { _init(name, mode, width, height, depth, wrapMode, format, minFilter, magFilter, image, mipMap); } Texture::~Texture() { delete rect; for(int i = 0; i < m_iMipLevels; i++ ) { delete data[i]; data[i] = nullptr; } if(glId >= 0) glDeleteTextures(glId); } const Rect2i *Texture::getRect() { return rect; } void Texture::fill(const Rect2i *rect, int color) { if (data[0] == nullptr || rect == nullptr) return; Rect2i myRect(0, 0, width, height); myRect.intersect(rect); int startX = myRect.getX(); int startY = myRect.getY(); int fillWidth = myRect.getWidth(); int endY = startY + myRect.getHeight(); if (fillWidth <= 0 || startY >= endY) return; BYTE b0 = static_cast((color >> 24) & 0xff); BYTE b1 = static_cast((color >> 16) & 0xff); BYTE b2 = static_cast((color >> 8) & 0xff); BYTE b3 = static_cast((color >> 0) & 0xff); unsigned int finalColor = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; unsigned int* pixels = (unsigned int*)data[0]->getBuffer(); for (int y = startY; y < endY; y++) { unsigned int* rowStart = pixels + (y * width) + startX; std::fill(rowStart, rowStart + fillWidth, finalColor); } if (immediateUpdate) updateOnGPU(); else updated = false; } void Texture::writeAsBMP(const wstring &name) { // 4J Don't need } void Texture::writeAsPNG(const wstring &filename) { // 4J Don't need } void Texture::blit(int x, int y, Texture *source) { blit(x, y, source, false); } void Texture::blit(int x, int y, Texture *source, bool rotated) { if (source == nullptr) return; for(unsigned int level = 0; level < m_iMipLevels; ++level) { ByteBuffer *srcBuffer = source->getData(level); if(srcBuffer == nullptr || data[level] == nullptr) break; int yy = y >> level; int xx = x >> level; int hh = height >> level; int ww = width >> level; int shh = source->getHeight() >> level; int sww = source->getWidth() >> level; unsigned int* dstPixels = (unsigned int*)data[level]->getBuffer(); unsigned int* srcPixels = (unsigned int*)srcBuffer->getBuffer(); if (!rotated) { for (int srcY = 0; srcY < shh; srcY++) { int dstY = yy + srcY; if (dstY < 0 || dstY >= hh) continue; unsigned int* dstRow = dstPixels + (dstY * ww + xx); unsigned int* srcRow = srcPixels + (srcY * sww); memcpy(dstRow, srcRow, sww * sizeof(unsigned int)); } } else { for (int srcY = 0; srcY < shh; srcY++) { int dstY = yy + (shh - srcY); if (dstY < 0 || dstY >= hh) continue; for (int srcX = 0; srcX < sww; srcX++) { int dstIndex = xx + (srcX * ww) + dstY; int srcIndex = (srcY * sww) + srcX; dstPixels[dstIndex] = srcPixels[srcIndex]; } } } data[level]->position(ww * hh * 4); } if (immediateUpdate) updateOnGPU(); else updated = false; } void Texture::transferFromBuffer(intArray buffer) { if (depth == 1) return; data[0]->position(0); int byteRemapRGBA[] = { 0, 1, 2, 3 }; int byteRemapBGRA[] = { 2, 1, 0, 3 }; int *byteRemap = ((format == TFMT_BGRA) ? byteRemapBGRA : byteRemapRGBA); int totalPixels = width * height * depth; for (int i = 0; i < totalPixels; i++) { int byteIndex = i * 4; unsigned int pixel = buffer[i]; data[0]->put(byteIndex + byteRemap[0], static_cast((pixel >> 24) & 0xff)); data[0]->put(byteIndex + byteRemap[1], static_cast((pixel >> 16) & 0xff)); data[0]->put(byteIndex + byteRemap[2], static_cast((pixel >> 8) & 0xff)); data[0]->put(byteIndex + byteRemap[3], static_cast((pixel >> 0) & 0xff)); } data[0]->position(totalPixels * 4); updateOnGPU(); } void Texture::transferFromImage(BufferedImage *image) { int imgWidth = image->getWidth(); int imgHeight = image->getHeight(); if (imgWidth > width || imgHeight > height) { app.DebugPrintf("transferFromImage called with a BufferedImage with dimensions (%d, %d) larger than the Texture dimensions (%d, %d). Ignoring.\n", imgWidth, imgHeight, width, height); return; } int byteRemapRGBA[] = { 3, 0, 1, 2 }; int byteRemapBGRA[] = { 3, 2, 1, 0 }; int *byteRemap = ((format == TFMT_BGRA) ? byteRemapBGRA : byteRemapRGBA); int b0 = byteRemap[0], b1 = byteRemap[1], b2 = byteRemap[2], b3 = byteRemap[3]; int totalPixels = width * height; intArray tempPixels = intArray(totalPixels); int transparency = image->getTransparency(); image->getRGB(0, 0, width, height, tempPixels, 0, imgWidth); byteArray tempBytes = byteArray(totalPixels * 4); BYTE* dst = tempBytes.data; int* src = tempPixels.data; for (int i = 0; i < totalPixels; i++) { // Pull ARGB bytes into either RGBA or BGRA depending on format unsigned int pixel = src[i]; dst[b0] = static_cast((pixel >> 24) & 0xff); dst[b1] = static_cast((pixel >> 16) & 0xff); dst[b2] = static_cast((pixel >> 8) & 0xff); dst[b3] = static_cast((pixel >> 0) & 0xff); dst += 4; } for(int i = 0; i < m_iMipLevels; i++ ) { if(data[i] != nullptr) { delete data[i]; data[i] = nullptr; } } MemSect(51); data[0] = ByteBuffer::allocateDirect(tempBytes.length); MemSect(0); data[0]->clear(); data[0]->put(tempBytes); data[0]->limit(tempBytes.length); delete [] tempBytes.data; if(mipmapped || image->getData(1) != nullptr) { mipmapped = true; int maxMipPixels = (width >> 1) * (height >> 1); unsigned int *tempData = new unsigned int[maxMipPixels]; BYTE* mipBufferData = new BYTE[maxMipPixels * 4]; for(unsigned int level = 1; level < MAX_MIP_LEVELS; ++level) { int ww = width >> level; int hh = height >> level; int mipPixels = ww * hh; if (mipPixels == 0) break; int mipLength = mipPixels * 4; BYTE* mipDst = mipBufferData; if( image->getData( level ) ) { memcpy( tempData, image->getData( level ), mipLength); for (int i = 0; i < mipPixels; i++) { // Pull ARGB bytes into either RGBA or BGRA depending on format unsigned int pixel = tempData[i]; mipDst[b0] = static_cast((pixel >> 24) & 0xff); mipDst[b1] = static_cast((pixel >> 16) & 0xff); mipDst[b2] = static_cast((pixel >> 8) & 0xff); mipDst[b3] = static_cast((pixel >> 0) & 0xff); mipDst += 4; } } else { int ow = width >> (level - 1); for (int y = 0; y < hh; y++) { for (int x = 0; x < ww; x++) { int baseIndex = (x * 2) + (y * 2) * ow; unsigned int c0 = data[level - 1]->getInt((baseIndex) * 4); unsigned int c1 = data[level - 1]->getInt((baseIndex + 1) * 4); unsigned int c2 = data[level - 1]->getInt((baseIndex + 1 + ow) * 4); unsigned int c3 = data[level - 1]->getInt((baseIndex + 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 ); unsigned int col = crispBlend(crispBlend(c0, c1), crispBlend(c2, c3)); // Pull ARGB bytes into either RGBA or BGRA depending on format mipDst[b0] = static_cast((col >> 24) & 0xff); mipDst[b1] = static_cast((col >> 16) & 0xff); mipDst[b2] = static_cast((col >> 8) & 0xff); mipDst[b3] = static_cast((col >> 0) & 0xff); mipDst += 4; } } } MemSect(51); data[level] = ByteBuffer::allocateDirect(mipLength); MemSect(0); data[level]->clear(); byteArray currentMipBytes; currentMipBytes.data = mipBufferData; currentMipBytes.length = mipLength; data[level]->put(currentMipBytes); data[level]->limit(mipLength); } delete [] mipBufferData; delete [] tempData; } delete [] tempPixels.data; if (immediateUpdate) updateOnGPU(); else updated = false; } unsigned int Texture::crispBlend(unsigned int c0, unsigned int c1) { unsigned int a0 = (c0 >> 24) & 0xFF; unsigned int a1 = (c1 >> 24) & 0xFF; if (a0 >= 0xfa || a1 >= 0xfa || !Minecraft::GetInstance()->options->mipmapsBlend) { if ((a0 + a1) < 255) { unsigned int r = (((c0 >> 16) & 0xFF) + ((c1 >> 16) & 0xFF)) / 2; unsigned int g = (((c0 >> 8) & 0xFF) + ((c1 >> 8) & 0xFF)) / 2; unsigned int b = ((c0 & 0xFF) + (c1 & 0xFF)) / 2; return (r << 16) | (g << 8) | b; } unsigned int r0 = (c0 >> 16) & 0xFF; unsigned int g0 = (c0 >> 8) & 0xFF; unsigned int b0 = c0 & 0xFF; unsigned int r1 = (c1 >> 16) & 0xFF; unsigned int g1 = (c1 >> 8) & 0xFF; unsigned int b1 = c1 & 0xFF; unsigned int r, g, b; if (a0 <= a1) { r = (r0 + 255 * r1) / 256; g = (g0 + 255 * g1) / 256; b = (b0 + 255 * b1) / 256; } else { r = (255 * r0 + r1) / 256; g = (255 * g0 + g1) / 256; b = (255 * b0 + b1) / 256; } return 0xFF000000 | (r << 16) | (g << 8) | b; } else // smoothblend if it's transparent return (((a0 + a1) >> 1) << 24) | (((c0 & 0xfefefe) + (c1 & 0xfefefe)) >> 1); } int Texture::getManagerId() { return managerId; } int Texture::getGlId() { return glId; } int Texture::getWidth() { return width; } int Texture::getHeight() { return height; } wstring Texture::getName() { return name; } void Texture::setImmediateUpdate(bool immediateUpdate) { this->immediateUpdate = immediateUpdate; } void Texture::bind(int mipMapIndex) { glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0 + mipMapIndex); glBindTexture(type, glId); if (!updated) updateOnGPU(); } void Texture::updateOnGPU() { if(data[0] == nullptr) return; data[0]->position(0); // 4J Added check so we can differentiate between which RenderManager function to call if(!m_bInitialised) { RenderManager.TextureSetTextureLevels(m_iMipLevels); // 4J added RenderManager.TextureData(width,height,data[0]->getBuffer(),0,C4JRender::TEXTURE_FORMAT_RxGyBzAw); if(mipmapped) { for (int level = 1; level < m_iMipLevels; level++) { if(data[level] == nullptr) break; data[level]->position(0); int levelWidth = width >> level; int levelHeight = height >> level; RenderManager.TextureData(levelWidth,levelHeight,data[level]->getBuffer(),level,C4JRender::TEXTURE_FORMAT_RxGyBzAw); } } m_bInitialised = true; } else { RenderManager.TextureDataUpdate(0, 0,width,height,data[0]->getBuffer(),0); if(mipmapped) { if (RenderManager.TextureGetTextureLevels() > 1) { for (int level = 1; level < m_iMipLevels; level++) { if(data[level] == nullptr) break; data[level]->position(0); int levelWidth = width >> level; int levelHeight = height >> level; RenderManager.TextureDataUpdate(0, 0,levelWidth,levelHeight,data[level]->getBuffer(),level); } } } } updated = true; } ByteBuffer *Texture::getData(unsigned int level) { return data[level]; }