mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-29 17:15:40 +00:00
396 lines
9.4 KiB
C++
396 lines
9.4 KiB
C++
#include "Texture.h"
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <algorithm>
|
|
|
|
namespace New3D {
|
|
|
|
Texture::Texture()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
Texture::~Texture()
|
|
{
|
|
DeleteTexture(); // make sure to have valid context before destroying
|
|
}
|
|
|
|
void Texture::DeleteTexture()
|
|
{
|
|
if (m_textureID) {
|
|
glDeleteTextures(1, &m_textureID);
|
|
Reset();
|
|
}
|
|
}
|
|
|
|
void Texture::Reset()
|
|
{
|
|
m_x = 0;
|
|
m_y = 0;
|
|
m_width = 0;
|
|
m_height = 0;
|
|
m_format = 0;
|
|
m_textureID = 0;
|
|
m_mirrorU = false;
|
|
m_mirrorV = false;
|
|
}
|
|
|
|
void Texture::BindTexture()
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, m_textureID);
|
|
}
|
|
|
|
void Texture::GetCoordinates(UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
|
|
{
|
|
uOut = (uIn*uvScale) / m_width;
|
|
vOut = (vIn*uvScale) / m_height;
|
|
}
|
|
|
|
void Texture::GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
|
|
{
|
|
uOut = (uIn*uvScale) / width;
|
|
vOut = (vIn*uvScale) / height;
|
|
}
|
|
|
|
void Texture::SetWrapMode(bool mirrorU, bool mirrorV)
|
|
{
|
|
if (mirrorU != m_mirrorU) {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mirrorU ? GL_MIRRORED_REPEAT : GL_REPEAT);
|
|
m_mirrorU = mirrorU;
|
|
}
|
|
|
|
if (mirrorV != m_mirrorV) {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mirrorV ? GL_MIRRORED_REPEAT : GL_REPEAT);
|
|
m_mirrorV = mirrorV;
|
|
}
|
|
}
|
|
|
|
void Texture::UploadTextureMip(int level, const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height)
|
|
{
|
|
int xi, yi, i;
|
|
int subWidth, subHeight;
|
|
GLubyte texel;
|
|
GLubyte c, a;
|
|
|
|
i = 0;
|
|
|
|
subWidth = width;
|
|
subHeight = height;
|
|
|
|
if (subWidth + x > 2048) {
|
|
subWidth = 2048 - x;
|
|
}
|
|
|
|
if (subHeight + y > 2048) {
|
|
subHeight = 2048 - y;
|
|
}
|
|
|
|
switch (format)
|
|
{
|
|
default: // Debug texture
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
scratch[i++] = 255; // R
|
|
scratch[i++] = 0; // G
|
|
scratch[i++] = 0; // B
|
|
scratch[i++] = 255; // A
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0: // T1RGB5 <- correct
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
scratch[i++] = ((src[yi * 2048 + xi] >> 10) & 0x1F) * 255 / 0x1F; // R
|
|
scratch[i++] = ((src[yi * 2048 + xi] >> 5) & 0x1F) * 255 / 0x1F; // G
|
|
scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0x1F) * 255 / 0x1F; // B
|
|
scratch[i++] = ((src[yi * 2048 + xi] & 0x8000) ? 0 : 255); // T
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: // Interleaved A4L4 (low byte)
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
// Interpret as A4L4
|
|
texel = src[yi * 2048 + xi] & 0xFF;
|
|
c = (texel & 0xF) * 17;
|
|
a = (texel >> 4) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = a;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2: // luminance alpha texture <- this one is correct
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] & 0xFF;
|
|
c = ((texel >> 4) & 0xF) * 17;
|
|
a = (texel & 0xF) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = a;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3: // 8-bit, A4L4 (high byte)
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] >> 8;
|
|
c = (texel & 0xF) * 17;
|
|
a = (texel >> 4) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = a;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 4: // 8-bit, L4A4 (high byte)
|
|
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] >> 8;
|
|
c = ((texel >> 4) & 0xF) * 17;
|
|
a = (texel & 0xF) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = a;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 5: // 8-bit grayscale
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] & 0xFF;
|
|
scratch[i++] = texel;
|
|
scratch[i++] = texel;
|
|
scratch[i++] = texel;
|
|
scratch[i++] = (texel == 255 ? 0 : 255);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 6: // 8-bit grayscale <-- this one is correct
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] >> 8;
|
|
scratch[i++] = texel;
|
|
scratch[i++] = texel;
|
|
scratch[i++] = texel;
|
|
scratch[i++] = (texel == 255 ? 0 : 255);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 7: // RGBA4
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
scratch[i++] = ((src[yi * 2048 + xi] >> 12) & 0xF) * 17;// R
|
|
scratch[i++] = ((src[yi * 2048 + xi] >> 8) & 0xF) * 17; // G
|
|
scratch[i++] = ((src[yi * 2048 + xi] >> 4) & 0xF) * 17; // B
|
|
scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0xF) * 17; // A
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// 4 bit texture types - all luminance textures (no alpha), only seem to be enabled when contour is enabled ( white = contour value )
|
|
//
|
|
|
|
case 8: // low byte, low nibble
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] & 0xFF;
|
|
c = (texel & 0xF) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = (c == 255 ? 0 : 255);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 9: // low byte, high nibble
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] & 0xFF;
|
|
c = ((texel >> 4) & 0xF) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = (c == 255 ? 0 : 255);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 10: // high byte, low nibble
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] >> 8;
|
|
c = (texel & 0xF) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = (c == 255 ? 0 : 255);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 11: // high byte, high nibble
|
|
|
|
for (yi = y; yi < (y + subHeight); yi++)
|
|
{
|
|
for (xi = x; xi < (x + subWidth); xi++)
|
|
{
|
|
texel = src[yi * 2048 + xi] >> 8;
|
|
c = ((texel >> 4) & 0xF) * 17;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = c;
|
|
scratch[i++] = (c == 255 ? 0 : 255);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, subWidth, subHeight, GL_RGBA, GL_UNSIGNED_BYTE, scratch);
|
|
}
|
|
|
|
UINT32 Texture::UploadTexture(const UINT16* src, UINT8* scratch, int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height)
|
|
{
|
|
const int mipXBase[] = { 0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 };
|
|
const int mipYBase[] = { 0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023 };
|
|
const int mipDivisor[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };
|
|
|
|
if (!src || !scratch) {
|
|
return 0; // sanity checking
|
|
}
|
|
|
|
DeleteTexture(); // free any existing texture
|
|
CreateTextureObject(format, mirrorU, mirrorV, x, y, width, height);
|
|
|
|
int page = y / 1024;
|
|
|
|
y -= (page * 1024); // remove page from tex y
|
|
|
|
for (int i = 0; width > 0 && height > 0; i++) {
|
|
|
|
int xPos = mipXBase[i] + (x / mipDivisor[i]);
|
|
int yPos = mipYBase[i] + (y / mipDivisor[i]);
|
|
|
|
UploadTextureMip(i, src, scratch, format, xPos, yPos + (page * 1024), width, height);
|
|
|
|
width /= 2;
|
|
height /= 2;
|
|
}
|
|
|
|
return m_textureID;
|
|
}
|
|
|
|
void Texture::GetDetails(int& x, int&y, int& width, int& height, int& format)
|
|
{
|
|
x = m_x;
|
|
y = m_y;
|
|
width = m_width;
|
|
height = m_height;
|
|
format = m_format;
|
|
}
|
|
|
|
bool Texture::Compare(int x, int y, int width, int height, int format)
|
|
{
|
|
if (m_x == x && m_y == y && m_width == width && m_height == height && m_format == format) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Texture::CheckMapPos(int ax1, int ax2, int ay1, int ay2)
|
|
{
|
|
int bx1 = m_x;
|
|
int bx2 = m_x + m_width;
|
|
int by1 = m_y;
|
|
int by2 = m_y + m_height;
|
|
|
|
if (ax1<bx2 && ax2>bx1 &&
|
|
ay1<by2 && ay2>by1) {
|
|
return true; // rectangles overlap
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Texture::CreateTextureObject(int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height)
|
|
{
|
|
GLfloat maxAnistrophy;
|
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnistrophy);
|
|
|
|
if (maxAnistrophy > 8) {
|
|
maxAnistrophy = 8.0f; //anymore than 8 can get expensive for little gain
|
|
}
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // rgba is always 4 byte aligned
|
|
glGenTextures(1, &m_textureID);
|
|
glBindTexture(GL_TEXTURE_2D, m_textureID);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mirrorU ? GL_MIRRORED_REPEAT : GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mirrorV ? GL_MIRRORED_REPEAT : GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnistrophy);
|
|
|
|
int minD = std::min(width, height);
|
|
int count = 0;
|
|
|
|
while (minD > 1) {
|
|
minD /= 2;
|
|
count++;
|
|
}
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count);
|
|
|
|
m_x = x;
|
|
m_y = y;
|
|
m_width = width;
|
|
m_height = height;
|
|
m_format = format;
|
|
m_mirrorU = mirrorU;
|
|
m_mirrorV = mirrorV;
|
|
}
|
|
|
|
} // New3D
|