internal format is then automatically deduced. Only a few 8-bit RGB or RGBA formats are supported for now.legacy
@@ -42,6 +42,7 @@ class TextureData | |||
friend class Texture; | |||
ivec2 m_size; | |||
PixelFormat m_format; | |||
#if defined USE_D3D9 | |||
IDirect3DTexture9 *m_tex; | |||
@@ -49,6 +50,8 @@ class TextureData | |||
D3DTexture *m_tex; | |||
#else | |||
GLuint m_texid; | |||
GLint m_internal_format; | |||
GLenum m_gl_format, m_gl_type; | |||
#endif | |||
}; | |||
@@ -57,37 +60,85 @@ class TextureData | |||
// ----------------- | |||
// | |||
/* FIXME: this is all hardcoded over the place */ | |||
#if __CELLOS_LV2__ | |||
static GLint const INTERNAL_FORMAT = GL_ARGB_SCE; | |||
static GLenum const TEXTURE_FORMAT = GL_BGRA; | |||
static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV; | |||
#elif defined __native_client__ || defined HAVE_GLES_2X | |||
static GLint const INTERNAL_FORMAT = GL_RGBA; | |||
static GLenum const TEXTURE_FORMAT = GL_RGBA; | |||
static GLenum const TEXTURE_TYPE = GL_UNSIGNED_BYTE; | |||
#elif !defined USE_D3D9 && !defined _XBOX | |||
/* Seems efficient for little endian textures */ | |||
static GLint const INTERNAL_FORMAT = GL_RGBA; | |||
static GLenum const TEXTURE_FORMAT = GL_BGRA; | |||
static GLenum const TEXTURE_TYPE = GL_UNSIGNED_INT_8_8_8_8_REV; | |||
#endif | |||
#define GET_CLAMPED(array, index) \ | |||
array[std::max(0, std::min((int)(index), \ | |||
(int)sizeof(array) / (int)sizeof(*array)))] | |||
Texture::Texture(ivec2 size) | |||
Texture::Texture(ivec2 size, PixelFormat format) | |||
: m_data(new TextureData) | |||
{ | |||
m_data->m_size = size; | |||
m_data->m_format = format; | |||
#if defined USE_D3D9 || defined _XBOX | |||
static int const d3d_formats[] = | |||
{ | |||
/* Unknown */ | |||
D3DFMT_UNKNOWN, | |||
/* R8G8B8 */ | |||
D3DFMT_R8G8B8, | |||
/* A8R8G8B8 */ | |||
# if defined USE_D3D9 | |||
D3DFMT_A8R8G8B8, | |||
# else | |||
/* By default the X360 will swizzle the texture. Ask for linear. */ | |||
D3DFMT_LIN_A8R8G8B8, | |||
# endif | |||
}; | |||
int d3d_format = GET_CLAMPED(d3d_formats, format); | |||
# if defined USE_D3D9 | |||
int d3d_usage = D3DUSAGE_DYNAMIC; | |||
# else | |||
int d3d_usage = D3DUSAGE_WRITEONLY; | |||
# endif | |||
#if defined USE_D3D9 | |||
g_d3ddevice->CreateTexture(m_data->m_size.x, m_data->m_size.y, 1, | |||
D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, | |||
D3DPOOL_DEFAULT, &m_data->m_tex, NULL); | |||
#elif defined _XBOX | |||
/* By default the X360 will swizzle the texture. Ask for linear. */ | |||
g_d3ddevice->CreateTexture(m_data->m_size.x, m_data->m_size.y, 1, | |||
D3DUSAGE_WRITEONLY, D3DFMT_LIN_A8R8G8B8, | |||
d3d_usage, d3d_format, | |||
D3DPOOL_DEFAULT, &m_data->m_tex, NULL); | |||
#else | |||
static struct | |||
{ | |||
GLint internal_format; | |||
GLenum format, type; | |||
} | |||
const gl_formats[] = | |||
{ | |||
/* Unknown */ | |||
{ 0, 0, 0 }, | |||
/* R8G8B8 */ | |||
{ GL_RGB, GL_RGB, GL_UNSIGNED_BYTE }, | |||
/* A8R8G8B8 */ | |||
#if __CELLOS_LV2__ | |||
{ GL_ARGB_SCE, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV }, | |||
#elif defined __native_client__ || defined HAVE_GLES_2X | |||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, | |||
#else | |||
/* Seems efficient for little endian textures */ | |||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV }, | |||
#endif | |||
/* A8B8G8R8 */ | |||
#if __CELLOS_LV2__ | |||
{ GL_ARGB_SCE, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV }, | |||
#elif defined __native_client__ || defined HAVE_GLES_2X | |||
/* FIXME: if GL_RGBA is not available, we should advertise | |||
* this format as "not available" on this platform. */ | |||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, | |||
#else | |||
/* Seems efficient for little endian textures */ | |||
{ GL_RGBA, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV }, | |||
#endif | |||
}; | |||
m_data->m_internal_format = GET_CLAMPED(gl_formats, format).internal_format; | |||
m_data->m_gl_format = GET_CLAMPED(gl_formats, format).format; | |||
m_data->m_gl_type = GET_CLAMPED(gl_formats, format).type; | |||
glGenTextures(1, &m_data->m_texid); | |||
glBindTexture(GL_TEXTURE_2D, m_data->m_texid); | |||
@@ -129,9 +180,9 @@ void Texture::SetData(void *data) | |||
m_data->m_tex->UnlockRect(0); | |||
#else | |||
glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, | |||
glTexImage2D(GL_TEXTURE_2D, 0, m_data->m_internal_format, | |||
m_data->m_size.x, m_data->m_size.y, 0, | |||
TEXTURE_FORMAT, TEXTURE_TYPE, data); | |||
m_data->m_gl_format, m_data->m_gl_type, data); | |||
#endif | |||
} | |||
@@ -153,7 +204,7 @@ void Texture::SetSubData(ivec2 origin, ivec2 size, void *data) | |||
#else | |||
glTexSubImage2D(GL_TEXTURE_2D, 0, origin.x, origin.y, size.x, size.y, | |||
TEXTURE_FORMAT, TEXTURE_TYPE, data); | |||
m_data->m_gl_format, m_data->m_gl_type, data); | |||
#endif | |||
} | |||
@@ -19,10 +19,27 @@ | |||
namespace lol | |||
{ | |||
struct PixelFormat | |||
{ | |||
/* XXX: make sure to update texture.cpp when this changes */ | |||
enum Value | |||
{ | |||
Unknown = 0, | |||
R8G8B8, | |||
A8R8G8B8, | |||
A8B8G8R8, | |||
} | |||
m_value; | |||
inline PixelFormat() : m_value(Unknown) {} | |||
inline PixelFormat(Value v) : m_value(v) {} | |||
inline operator Value() { return m_value; } | |||
}; | |||
class Texture | |||
{ | |||
public: | |||
Texture(ivec2 size); | |||
Texture(ivec2 size, PixelFormat format); | |||
~Texture(); | |||
void Bind(); | |||
@@ -56,13 +56,7 @@ private: | |||
float tx, ty; | |||
Image *img; | |||
#if defined USE_D3D9 | |||
IDirect3DTexture9 *m_tex; | |||
#elif defined _XBOX | |||
D3DTexture *m_tex; | |||
#else | |||
GLuint m_tex; | |||
#endif | |||
Texture *m_texture; | |||
}; | |||
/* | |||
@@ -77,7 +71,7 @@ TileSet::TileSet(char const *path, ivec2 size, ivec2 count) | |||
sprintf(data->name, "<tileset> %s", path); | |||
data->tiles = NULL; | |||
data->m_tex = 0; | |||
data->m_texture = 0; | |||
data->img = new Image(path); | |||
data->isize = data->img->GetSize(); | |||
@@ -119,46 +113,24 @@ void TileSet::TickDraw(float seconds) | |||
if (data->img) | |||
delete data->img; | |||
else | |||
#if defined USE_D3D9 || defined _XBOX | |||
/* FIXME: is it really the correct call? */ | |||
data->m_tex->Release(); | |||
#else | |||
glDeleteTextures(1, &data->m_tex); | |||
#endif | |||
delete data->m_texture; | |||
} | |||
else if (data->img) | |||
{ | |||
#if defined USE_D3D9 || defined _XBOX | |||
D3DFORMAT format; | |||
#else | |||
GLuint format; | |||
#endif | |||
int planes; | |||
PixelFormat format = PixelFormat::Unknown; | |||
switch (data->img->GetFormat()) | |||
{ | |||
case Image::FORMAT_RGB: | |||
#if defined USE_D3D9 | |||
format = D3DFMT_R8G8B8; | |||
#elif defined _XBOX | |||
format = D3DFMT_LIN_A8R8G8B8; /* FIXME */ | |||
#else | |||
format = GL_RGB; | |||
#endif | |||
planes = 3; | |||
break; | |||
format = PixelFormat::R8G8B8; | |||
planes = 3; | |||
break; | |||
case Image::FORMAT_RGBA: | |||
default: | |||
#if defined USE_D3D9 | |||
format = D3DFMT_A8R8G8B8; | |||
#elif defined _XBOX | |||
/* By default the X360 will swizzle the texture. Ask for linear. */ | |||
format = D3DFMT_LIN_A8R8G8B8; | |||
#else | |||
format = GL_RGBA; | |||
#endif | |||
planes = 4; | |||
break; | |||
format = PixelFormat::A8R8G8B8; | |||
planes = 4; | |||
break; | |||
} | |||
int w = PotUp(data->isize.x); | |||
@@ -175,41 +147,8 @@ void TileSet::TickDraw(float seconds) | |||
pixels = tmp; | |||
} | |||
#if defined USE_D3D9 || defined _XBOX | |||
D3DLOCKED_RECT rect; | |||
HRESULT hr; | |||
# if defined USE_D3D9 | |||
hr = g_d3ddevice->CreateTexture(w, h, 1, D3DUSAGE_DYNAMIC, format, | |||
D3DPOOL_DEFAULT, &data->m_tex, NULL); | |||
# elif defined _XBOX | |||
hr = g_d3ddevice->CreateTexture(w, h, 1, D3DUSAGE_WRITEONLY, format, | |||
D3DPOOL_DEFAULT, &data->m_tex, NULL); | |||
# endif | |||
if (FAILED(hr)) | |||
Abort(); | |||
# if defined USE_D3D9 | |||
hr = data->m_tex->LockRect(0, &rect, NULL, D3DLOCK_DISCARD); | |||
# else | |||
hr = data->m_tex->LockRect(0, &rect, NULL, 0); | |||
# endif | |||
if (FAILED(hr)) | |||
Abort(); | |||
for (int j = 0; j < h; j++) | |||
memcpy((uint8_t *)rect.pBits + j * rect.Pitch, pixels + w * j * 4, w * 4); | |||
hr = data->m_tex->UnlockRect(0); | |||
if (FAILED(hr)) | |||
Abort(); | |||
#else | |||
glGenTextures(1, &data->m_tex); | |||
glEnable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, data->m_tex); | |||
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, | |||
format, GL_UNSIGNED_BYTE, pixels); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |||
#endif | |||
data->m_texture = new Texture(ivec2(w, h), format); | |||
data->m_texture->SetData(pixels); | |||
if (pixels != data->img->GetData()) | |||
free(pixels); | |||
@@ -237,32 +176,13 @@ ivec2 TileSet::GetSize(int tileid) const | |||
void TileSet::Bind() | |||
{ | |||
if (!data->img && data->m_tex) | |||
{ | |||
#if defined USE_D3D9 || defined _XBOX | |||
HRESULT hr = g_d3ddevice->SetTexture(0, data->m_tex); | |||
if (FAILED(hr)) | |||
Abort(); | |||
#else | |||
glActiveTexture(GL_TEXTURE0); | |||
glBindTexture(GL_TEXTURE_2D, data->m_tex); | |||
#endif | |||
} | |||
if (!data->img && data->m_texture) | |||
data->m_texture->Bind(); | |||
} | |||
void TileSet::Unbind() | |||
{ | |||
if (!data->img && data->m_tex) | |||
{ | |||
#if defined USE_D3D9 || defined _XBOX | |||
HRESULT hr = g_d3ddevice->SetTexture(0, NULL); | |||
if (FAILED(hr)) | |||
Abort(); | |||
#else | |||
glActiveTexture(GL_TEXTURE0); | |||
glBindTexture(GL_TEXTURE_2D, 0); | |||
#endif | |||
} | |||
; | |||
} | |||
void TileSet::BlitTile(uint32_t id, vec3 pos, int o, vec2 scale, | |||
@@ -287,7 +207,7 @@ void TileSet::BlitTile(uint32_t id, vec3 pos, int o, vec2 scale, | |||
dtx = -dtx; | |||
} | |||
if (!data->img && data->m_tex) | |||
if (!data->img && data->m_texture) | |||
{ | |||
float tmp[10]; | |||
@@ -445,7 +445,8 @@ public: | |||
{ | |||
/* Create a texture of half the width and twice the height | |||
* so that we can upload four different subimages each frame. */ | |||
m_texture = new Texture(ivec2(m_size.x / 2, m_size.y * 2)); | |||
m_texture = new Texture(ivec2(m_size.x / 2, m_size.y * 2), | |||
PixelFormat::A8B8G8R8); | |||
/* Ensure the texture data is complete at least once, otherwise | |||
* uploading subimages will not work. */ | |||