mirror of
https://github.com/GabsPuNs/Project-Zenith-Main.git
synced 2026-05-30 06:35:24 +00:00
595 lines
14 KiB
C++
595 lines
14 KiB
C++
#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<<iWidthMips)<width) iWidthMips++;
|
|
while((8<<iHeightMips)<height) iHeightMips++;
|
|
|
|
m_iMipLevels=(iWidthMips<iHeightMips)?iWidthMips:iHeightMips;
|
|
|
|
// TODO - The render libs currently limit max mip map levels to 5
|
|
if(m_iMipLevels > 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<BYTE>((color >> 24) & 0xff);
|
|
BYTE b1 = static_cast<BYTE>((color >> 16) & 0xff);
|
|
BYTE b2 = static_cast<BYTE>((color >> 8) & 0xff);
|
|
BYTE b3 = static_cast<BYTE>((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<BYTE>((pixel >> 24) & 0xff));
|
|
data[0]->put(byteIndex + byteRemap[1], static_cast<BYTE>((pixel >> 16) & 0xff));
|
|
data[0]->put(byteIndex + byteRemap[2], static_cast<BYTE>((pixel >> 8) & 0xff));
|
|
data[0]->put(byteIndex + byteRemap[3], static_cast<BYTE>((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<BYTE>((pixel >> 24) & 0xff);
|
|
dst[b1] = static_cast<BYTE>((pixel >> 16) & 0xff);
|
|
dst[b2] = static_cast<BYTE>((pixel >> 8) & 0xff);
|
|
dst[b3] = static_cast<BYTE>((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<BYTE>((pixel >> 24) & 0xff);
|
|
mipDst[b1] = static_cast<BYTE>((pixel >> 16) & 0xff);
|
|
mipDst[b2] = static_cast<BYTE>((pixel >> 8) & 0xff);
|
|
mipDst[b3] = static_cast<BYTE>((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<BYTE>((col >> 24) & 0xff);
|
|
mipDst[b1] = static_cast<BYTE>((col >> 16) & 0xff);
|
|
mipDst[b2] = static_cast<BYTE>((col >> 8) & 0xff);
|
|
mipDst[b3] = static_cast<BYTE>((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];
|
|
}
|