OpenGL2: Add OpenGL ES 2.0+ support
This mainly targets OpenGL ES 2.0 but it also supports compiling GLSL as ESSL 3.00. It's missing support for framebuffer objects which should be possible on ES 2. (Though using renderbuffers instead of textures.) opengl1 cvars that are not supported will display a message and disable the cvar. This has not been reviewed for new opengl2 cvars. Enabling cvars may cause rendering issues. Some of the broken cvars may be possible to support using OpenGL ES 3 features. The game displays okay with the default cvars.
This commit is contained in:
1 parent
1fc83e4845
commit
3b984d2b51
14 files changed
+658
-167
No files matched your search
+175
-13
@@ -1455,6 +1455,106 @@ byte mipBlendColors[16][4] = {
|
||||
{0,0,255,128},
|
||||
};
|
||||
|
||||
/*
|
||||
==================
|
||||
R_ConvertTextureFormat
|
||||
|
||||
Convert RGBA unsigned byte to specified format and type
|
||||
==================
|
||||
*/
|
||||
#define ROW_PADDING( width, bpp, alignment ) PAD( (width) * (bpp), (alignment) ) - (width) * (bpp)
|
||||
void R_ConvertTextureFormat( const byte *in, int width, int height, GLenum format, GLenum type, byte *out )
|
||||
{
|
||||
int x, y, rowPadding;
|
||||
int unpackAlign = 4; // matches GL_UNPACK_ALIGNMENT default
|
||||
|
||||
if ( format == GL_RGB && type == GL_UNSIGNED_BYTE )
|
||||
{
|
||||
rowPadding = ROW_PADDING( width, 3, unpackAlign );
|
||||
|
||||
for ( y = 0; y < height; y++ )
|
||||
{
|
||||
for ( x = 0; x < width; x++ )
|
||||
{
|
||||
*out++ = *in++;
|
||||
*out++ = *in++;
|
||||
*out++ = *in++;
|
||||
in++;
|
||||
}
|
||||
|
||||
out += rowPadding;
|
||||
}
|
||||
}
|
||||
else if ( format == GL_LUMINANCE && type == GL_UNSIGNED_BYTE )
|
||||
{
|
||||
rowPadding = ROW_PADDING( width, 1, unpackAlign );
|
||||
|
||||
for ( y = 0; y < height; y++ )
|
||||
{
|
||||
for ( x = 0; x < width; x++ )
|
||||
{
|
||||
*out++ = *in++; // red
|
||||
in += 3;
|
||||
}
|
||||
|
||||
out += rowPadding;
|
||||
}
|
||||
}
|
||||
else if ( format == GL_LUMINANCE_ALPHA && type == GL_UNSIGNED_BYTE )
|
||||
{
|
||||
rowPadding = ROW_PADDING( width, 2, unpackAlign );
|
||||
|
||||
for ( y = 0; y < height; y++ )
|
||||
{
|
||||
for ( x = 0; x < width; x++ )
|
||||
{
|
||||
*out++ = *in++; // red
|
||||
in += 2;
|
||||
*out++ = *in++; // alpha
|
||||
}
|
||||
|
||||
out += rowPadding;
|
||||
}
|
||||
}
|
||||
else if ( format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5 )
|
||||
{
|
||||
rowPadding = ROW_PADDING( width, 2, unpackAlign );
|
||||
|
||||
for ( y = 0; y < height; y++ )
|
||||
{
|
||||
for ( x = 0; x < width; x++, in += 4, out += 2 )
|
||||
{
|
||||
*((unsigned short*)out) = ( (unsigned short)( in[0] >> 3 ) << 11 )
|
||||
| ( (unsigned short)( in[1] >> 2 ) << 5 )
|
||||
| ( (unsigned short)( in[2] >> 3 ) << 0 );
|
||||
}
|
||||
|
||||
out += rowPadding;
|
||||
}
|
||||
}
|
||||
else if ( format == GL_RGBA && type == GL_UNSIGNED_SHORT_4_4_4_4 )
|
||||
{
|
||||
rowPadding = ROW_PADDING( width, 2, unpackAlign );
|
||||
|
||||
for ( y = 0; y < height; y++ )
|
||||
{
|
||||
for ( x = 0; x < width; x++, in += 4, out += 2 )
|
||||
{
|
||||
*((unsigned short*)out) = ( (unsigned short)( in[0] >> 4 ) << 12 )
|
||||
| ( (unsigned short)( in[1] >> 4 ) << 8 )
|
||||
| ( (unsigned short)( in[2] >> 4 ) << 4 )
|
||||
| ( (unsigned short)( in[3] >> 4 ) << 0 );
|
||||
}
|
||||
|
||||
out += rowPadding;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Error( ERR_DROP, "Unable to convert RGBA image to OpenGL format 0x%X and type 0x%X", format, type );
|
||||
}
|
||||
}
|
||||
|
||||
static void RawImage_SwizzleRA( byte *data, int width, int height )
|
||||
{
|
||||
int i;
|
||||
@@ -1944,18 +2044,20 @@ static GLenum PixelDataFormatFromInternalFormat(GLenum internalFormat)
|
||||
}
|
||||
}
|
||||
|
||||
static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
|
||||
static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int width, int height, GLenum target, GLenum picFormat, GLenum dataFormat, GLenum dataType, int numMips, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture )
|
||||
{
|
||||
GLenum dataFormat, dataType;
|
||||
qboolean rgtc = internalFormat == GL_COMPRESSED_RG_RGTC2;
|
||||
qboolean rgba8 = picFormat == GL_RGBA8 || picFormat == GL_SRGB8_ALPHA8_EXT;
|
||||
qboolean rgba = rgba8 || picFormat == GL_RGBA16;
|
||||
qboolean mipmap = !!(flags & IMGFLAG_MIPMAP);
|
||||
int size, miplevel;
|
||||
qboolean lastMip = qfalse;
|
||||
byte *formatBuffer = NULL;
|
||||
|
||||
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
|
||||
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
|
||||
if (qglesMajorVersion && rgba8 && (dataFormat != GL_RGBA || dataType != GL_UNSIGNED_BYTE))
|
||||
{
|
||||
formatBuffer = ri.Hunk_AllocateTempMemory(4 * width * height);
|
||||
}
|
||||
|
||||
miplevel = 0;
|
||||
do
|
||||
@@ -1974,6 +2076,11 @@ static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int
|
||||
|
||||
if (rgba8 && rgtc)
|
||||
RawImage_UploadToRgtc2Texture(texture, miplevel, x, y, width, height, data);
|
||||
else if (formatBuffer)
|
||||
{
|
||||
R_ConvertTextureFormat(data, width, height, dataFormat, dataType, formatBuffer);
|
||||
qglTextureSubImage2DEXT(texture, target, miplevel, x, y, width, height, dataFormat, dataType, formatBuffer);
|
||||
}
|
||||
else
|
||||
qglTextureSubImage2DEXT(texture, target, miplevel, x, y, width, height, dataFormat, dataType, data);
|
||||
}
|
||||
@@ -2007,6 +2114,9 @@ static void RawImage_UploadTexture(GLuint texture, byte *data, int x, int y, int
|
||||
}
|
||||
}
|
||||
while (!lastMip);
|
||||
|
||||
if (formatBuffer != NULL)
|
||||
ri.Hunk_FreeTempMemory(formatBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -2016,7 +2126,7 @@ Upload32
|
||||
|
||||
===============
|
||||
*/
|
||||
static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, int numMips, image_t *image, qboolean scaled)
|
||||
static void Upload32(byte *data, int x, int y, int width, int height, GLenum picFormat, GLenum dataFormat, GLenum dataType, int numMips, image_t *image, qboolean scaled)
|
||||
{
|
||||
int i, c;
|
||||
byte *scan;
|
||||
@@ -2071,7 +2181,7 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
int w2 = width, h2 = height;
|
||||
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, picFormat, numMips, internalFormat, type, flags, qfalse);
|
||||
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, picFormat, dataFormat, dataType, numMips, internalFormat, type, flags, qfalse);
|
||||
for (c = numMips; c; c--)
|
||||
{
|
||||
data += CalculateMipSize(w2, h2, picFormat);
|
||||
@@ -2082,7 +2192,7 @@ static void Upload32(byte *data, int x, int y, int width, int height, GLenum pic
|
||||
}
|
||||
else
|
||||
{
|
||||
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_2D, picFormat, numMips, internalFormat, type, flags, qfalse);
|
||||
RawImage_UploadTexture(image->texnum, data, x, y, width, height, GL_TEXTURE_2D, picFormat, dataFormat, dataType, numMips, internalFormat, type, flags, qfalse);
|
||||
}
|
||||
|
||||
GL_CheckErrors();
|
||||
@@ -2108,7 +2218,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
|
||||
qboolean picmip = !!(flags & IMGFLAG_PICMIP);
|
||||
qboolean lastMip;
|
||||
GLenum textureTarget = cubemap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
|
||||
GLenum dataFormat;
|
||||
GLenum dataFormat, dataType;
|
||||
|
||||
if (strlen(name) >= MAX_QPATH ) {
|
||||
ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name);
|
||||
@@ -2140,6 +2250,53 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
|
||||
if (!internalFormat)
|
||||
internalFormat = RawImage_GetFormat(pic, width * height, picFormat, isLightmap, image->type, image->flags);
|
||||
|
||||
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
|
||||
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
|
||||
|
||||
// Convert image data format for OpenGL ES, data is converted for each mip level
|
||||
if (qglesMajorVersion)
|
||||
{
|
||||
switch (internalFormat)
|
||||
{
|
||||
case GL_LUMINANCE:
|
||||
case GL_LUMINANCE8:
|
||||
internalFormat = GL_LUMINANCE;
|
||||
dataFormat = GL_LUMINANCE;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case GL_LUMINANCE_ALPHA:
|
||||
case GL_LUMINANCE8_ALPHA8:
|
||||
internalFormat = GL_LUMINANCE_ALPHA;
|
||||
dataFormat = GL_LUMINANCE_ALPHA;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case GL_RGB:
|
||||
case GL_RGB8:
|
||||
internalFormat = GL_RGB;
|
||||
dataFormat = GL_RGB;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case GL_RGB5:
|
||||
internalFormat = GL_RGB;
|
||||
dataFormat = GL_RGB;
|
||||
dataType = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
case GL_RGBA:
|
||||
case GL_RGBA8:
|
||||
internalFormat = GL_RGBA;
|
||||
dataFormat = GL_RGBA;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case GL_RGBA4:
|
||||
internalFormat = GL_RGBA;
|
||||
dataFormat = GL_RGBA;
|
||||
dataType = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
break;
|
||||
default:
|
||||
ri.Error( ERR_DROP, "Missing OpenGL ES support for image '%s' with internal format 0x%X\n", name, internalFormat );
|
||||
}
|
||||
}
|
||||
|
||||
image->internalFormat = internalFormat;
|
||||
|
||||
// Possibly scale image before uploading.
|
||||
@@ -2164,7 +2321,6 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
|
||||
image->uploadHeight = height;
|
||||
|
||||
// Allocate texture storage so we don't have to worry about it later.
|
||||
dataFormat = PixelDataFormatFromInternalFormat(internalFormat);
|
||||
mipWidth = width;
|
||||
mipHeight = height;
|
||||
miplevel = 0;
|
||||
@@ -2176,11 +2332,11 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, GL_UNSIGNED_BYTE, NULL);
|
||||
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, dataType, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, GL_UNSIGNED_BYTE, NULL);
|
||||
qglTextureImage2DEXT(image->texnum, GL_TEXTURE_2D, miplevel, internalFormat, mipWidth, mipHeight, 0, dataFormat, dataType, NULL);
|
||||
}
|
||||
|
||||
mipWidth = MAX(1, mipWidth >> 1);
|
||||
@@ -2191,7 +2347,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe
|
||||
|
||||
// Upload data.
|
||||
if (pic)
|
||||
Upload32(pic, 0, 0, width, height, picFormat, numMips, image, scaled);
|
||||
Upload32(pic, 0, 0, width, height, picFormat, dataFormat, dataType, numMips, image, scaled);
|
||||
|
||||
if (resampledBuffer != NULL)
|
||||
ri.Hunk_FreeTempMemory(resampledBuffer);
|
||||
@@ -2252,7 +2408,13 @@ image_t *R_CreateImage(const char *name, byte *pic, int width, int height, imgTy
|
||||
|
||||
void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height, GLenum picFormat )
|
||||
{
|
||||
Upload32(pic, x, y, width, height, picFormat, 0, image, qfalse);
|
||||
GLenum dataFormat, dataType;
|
||||
|
||||
// TODO: This is fine for lightmaps but (unused) general RGBA images need to store dataFormat / dataType in image_t for OpenGL ES?
|
||||
dataFormat = PixelDataFormatFromInternalFormat(image->internalFormat);
|
||||
dataType = picFormat == GL_RGBA16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
|
||||
|
||||
Upload32(pic, x, y, width, height, picFormat, dataFormat, dataType, 0, image, qfalse);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
Reference in new issue
Block a user