Skip to content

Commit

Permalink
Add OpenGL ES 1.1 support to opengl1 renderer
Browse files Browse the repository at this point in the history
This requires my previous commit to use vertex arrays instead of
glBegin() (loosey based on ptitSeb OpenPandora port).

There isn't software gamma correction so overbright may not work (as is
already the case for windowed mode on GNU/Linux).

Enabling unsupported cvar settings (see README) automatically displays
a warning and disables the cvar.

r_useOpenGLES cvar controls whether OpenGL or OpenGL ES API is used (see
README). It's not achived to avoid it being saved in mods, as there is
no way to have a global cvar.
  • Loading branch information
zturtleman committed Jul 24, 2018
1 parent e4a66f7 commit b1943b8
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 55 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,28 @@ Makefile.local:
The defaults for these variables differ depending on the target platform.


# OpenGL ES support

ioquake3's default renderer (cl_renderer opengl2) does not support OpenGL ES.

If you build ioquake3 on a platform that uses OpenGL ES (such as Raspberry Pi),
run the game using `./build/release-*/ioquake3.* +set cl_renderer opengl1`.

The `r_useOpenGLES` cvar controls whether to use OpenGL or OpenGL ES API.
Set to -1 for auto (default), 0 for OpenGL, and 1 for OpenGL ES. It should be
set using command line arguments, `ioquake3 +set r_useOpenGLES 0`.

ioquake3's opengl1 renderer supports OpenGL 1.2+ and OpenGL ES 1.1 with a few limitations.
When using OpenGL ES the opengl1 renderer does not support `r_flares`[1],
`r_measureOverdraw`[2], `r_anaglyphMode`[3], `r_stereoEnabled`[3], `r_drawBuffer GL_FRONT`[3],
and `r_primitives 1/3`[4].

* [1] Requires glReadPixels GL_DEPTH_COMPONENT
* [2] Requires glReadPixels GL_STENCIL_INDEX
* [3] Requires glDrawBuffer
* [4] Requires glBegin


# Console

## New cvars
Expand Down Expand Up @@ -291,6 +313,9 @@ The defaults for these variables differ depending on the target platform.
cl_aviMotionJpeg is enabled
r_mode -2 - This new video mode automatically uses the
desktop resolution.
r_useOpenGLES - Controls whether to use OpenGL API or
OpenGL ES API. Set to -1 for auto (default),
0 for OpenGL, and 1 for OpenGL ES.
```

## New commands
Expand Down
5 changes: 3 additions & 2 deletions code/client/cl_avi.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,11 +357,12 @@ qboolean CL_OpenAVIForWriting( const char *fileName )
else
afd.motionJpeg = qfalse;

// Buffers only need to store RGB pixels.
// Capture buffer stores RGB pixels or possibly RGBA when using OpenGL ES.
// Encode buffer only needs to store RGB pixels.
// Allocate a bit more space for the capture buffer to account for possible
// padding at the end of pixel lines, and padding for alignment
#define MAX_PACK_LEN 16
afd.cBuffer = Z_Malloc((afd.width * 3 + MAX_PACK_LEN - 1) * afd.height + MAX_PACK_LEN - 1);
afd.cBuffer = Z_Malloc((afd.width * 4 + MAX_PACK_LEN - 1) * afd.height + MAX_PACK_LEN - 1);
// raw avi files have pixel lines start on 4-byte boundaries
afd.eBuffer = Z_Malloc(PAD(afd.width * 3, AVI_LINE_PADDING) * afd.height);

Expand Down
1 change: 1 addition & 0 deletions code/renderercommon/qgl.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void);
#define QGL_1_1_FIXED_FUNCTION_PROCS \
GLE(void, AlphaFunc, GLenum func, GLclampf ref) \
GLE(void, Color4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) \
GLE(void, Color4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) \
GLE(void, ColorPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \
GLE(void, DisableClientState, GLenum cap) \
GLE(void, EnableClientState, GLenum cap) \
Expand Down
1 change: 1 addition & 0 deletions code/renderercommon/tr_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared duri
extern qboolean textureFilterAnisotropic;
extern int maxAnisotropy;
extern float displayAspect;
extern qboolean readFormatAvailable;

//
// cvars
Expand Down
26 changes: 24 additions & 2 deletions code/renderergl1/tr_backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -826,14 +826,27 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *
}

void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) {
byte *buffer;

GL_Bind( tr.scratchImage[client] );

// if the scratchImage isn't in the format we want, specify it as a new texture
if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) {
tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols;
tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows;
qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );

// manually convert RGBA to RGB for OpenGL ES
if ( qglesMajorVersion >= 1 ) {
buffer = ri.Hunk_AllocateTempMemory( 3 * cols * rows );

R_ConvertTextureFormat( data, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, buffer );
qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, cols, rows, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer );

ri.Hunk_FreeTempMemory( buffer );
} else {
qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data );
}

qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
Expand All @@ -842,7 +855,16 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int
if (dirty) {
// otherwise, just subimage upload it so that drivers can tell we are going to be changing
// it and don't try and do a texture compression
qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
if ( qglesMajorVersion >= 1 ) {
buffer = ri.Hunk_AllocateTempMemory( 3 * cols * rows );

R_ConvertTextureFormat( data, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, buffer );
qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, buffer );

ri.Hunk_FreeTempMemory( buffer );
} else {
qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data );
}
}
}
}
Expand Down
16 changes: 15 additions & 1 deletion code/renderergl1/tr_cmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,14 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
//
if ( r_measureOverdraw->integer )
{
if ( glConfig.stencilBits < 4 )
if ( qglesMajorVersion >= 1 )
{
ri.Printf( PRINT_WARNING, "OpenGL ES does not support reading stencil bits to measure overdraw\n" );
// It can be done if GL_NV_read_stencil extension exists but it's for OpenGL ES 2+ contexts.
ri.Cvar_Set( "r_measureOverdraw", "0" );
r_measureOverdraw->modified = qfalse;
}
else if ( glConfig.stencilBits < 4 )
{
ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits );
ri.Cvar_Set( "r_measureOverdraw", "0" );
Expand Down Expand Up @@ -380,6 +387,13 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
}
else
{
if (qglesMajorVersion >= 1 && r_anaglyphMode->integer)
{
ri.Printf( PRINT_WARNING, "OpenGL ES does not support drawing to separate buffer for anaglyph mode\n" );
ri.Cvar_Set( "r_anaglyphMode", "0" );
r_anaglyphMode->modified = qfalse;
}

if(r_anaglyphMode->integer)
{
if(r_anaglyphMode->modified)
Expand Down
9 changes: 9 additions & 0 deletions code/renderergl1/tr_flares.c
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,15 @@ void RB_RenderFlares (void) {
return;
}

if ( r_flares->modified ) {
if ( qglesMajorVersion >= 1 ) {
ri.Printf( PRINT_WARNING, "OpenGL ES does not support reading depth to determine if flares are visible\n" );
// It can be done if GL_NV_read_depth extension exists but it's for OpenGL ES 2+ contexts.
ri.Cvar_Set( "r_flares", "0" );
}
r_flares->modified = qfalse;
}

if(r_flareCoeff->modified)
{
R_SetFlareCoeff();
Expand Down
165 changes: 157 additions & 8 deletions code/renderergl1/tr_image.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,74 @@ byte mipBlendColors[16][4] = {
{0,0,255,128},
};

/*
==================
R_ConvertTextureFormat
Convert RGBA unsigned byte to specified format and type
==================
*/
void R_ConvertTextureFormat( const byte *in, int width, int height, GLenum format, GLenum type, byte *out )
{
int i, numPixels;

numPixels = width * height;

if ( format == GL_RGB && type == GL_UNSIGNED_BYTE )
{
for ( i = 0; i < numPixels; i++ )
{
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
in++;
}
}
else if ( format == GL_LUMINANCE && type == GL_UNSIGNED_BYTE )
{
for ( i = 0; i < numPixels; i++ )
{
*out++ = *in++; // red
in += 3;
}
}
else if ( format == GL_LUMINANCE_ALPHA && type == GL_UNSIGNED_BYTE )
{
for ( i = 0; i < numPixels; i++ )
{
*out++ = *in++; // red
in += 2;
*out++ = *in++; // alpha
}
}
else if ( format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5 )
{
unsigned short *sout = (unsigned short *)out;

for ( i = 0; i < numPixels; i++, in += 4 )
{
*sout++ = ( (unsigned short)( in[0] >> 3 ) << 11 )
| ( (unsigned short)( in[1] >> 2 ) << 5 )
| ( (unsigned short)( in[2] >> 3 ) << 0 );
}
}
else if ( format == GL_RGBA && type == GL_UNSIGNED_SHORT_4_4_4_4 )
{
unsigned short *sout = (unsigned short *)out;

for ( i = 0; i < numPixels; i++, in += 4 )
{
*sout++ = ( (unsigned short)( in[0] >> 4 ) << 12 )
| ( (unsigned short)( in[1] >> 4 ) << 8 )
| ( (unsigned short)( in[2] >> 4 ) << 4 )
| ( (unsigned short)( in[3] >> 4 ) << 0 );
}
}
else
{
ri.Error( ERR_DROP, "Unable to convert RGBA image to OpenGL format 0x%X and type 0x%X", format, type );
}
}

/*
===============
Expand All @@ -556,16 +624,19 @@ static void Upload32( unsigned *data,
qboolean picmip,
qboolean lightMap,
qboolean allowCompression,
int *format,
int *pInternalFormat,
int *pUploadWidth, int *pUploadHeight )
{
int samples;
unsigned *scaledBuffer = NULL;
unsigned *resampledBuffer = NULL;
unsigned *formatBuffer = NULL;
int scaled_width, scaled_height;
int i, c;
byte *scan;
GLenum internalFormat = GL_RGB;
GLenum format = GL_RGBA;
GLenum type = GL_UNSIGNED_BYTE;
float rMax = 0, gMax = 0, bMax = 0;

//
Expand Down Expand Up @@ -689,11 +760,11 @@ static void Upload32( unsigned *data,
}
else
{
if ( allowCompression && glConfig.textureCompression == TC_S3TC_ARB )
if ( !qglesMajorVersion && allowCompression && glConfig.textureCompression == TC_S3TC_ARB )
{
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
}
else if ( allowCompression && glConfig.textureCompression == TC_S3TC )
else if ( !qglesMajorVersion && allowCompression && glConfig.textureCompression == TC_S3TC )
{
internalFormat = GL_RGB4_S3TC;
}
Expand Down Expand Up @@ -738,15 +809,75 @@ static void Upload32( unsigned *data,
}
}

// Convert image data format for OpenGL ES
if ( qglesMajorVersion >= 1 )
{
switch ( internalFormat )
{
case GL_LUMINANCE:
case GL_LUMINANCE8:
internalFormat = GL_LUMINANCE;
format = GL_LUMINANCE;
type = GL_UNSIGNED_BYTE;
break;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE8_ALPHA8:
internalFormat = GL_LUMINANCE_ALPHA;
format = GL_LUMINANCE_ALPHA;
type = GL_UNSIGNED_BYTE;
break;
case GL_RGB:
case GL_RGB8:
internalFormat = GL_RGB;
format = GL_RGB;
type = GL_UNSIGNED_BYTE;
break;
case GL_RGB5:
internalFormat = GL_RGB;
format = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
break;
case GL_RGBA:
case GL_RGBA8:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_BYTE;
break;
case GL_RGBA4:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
break;
default:
internalFormat = GL_RGBA;
format = GL_RGBA;
type = GL_UNSIGNED_BYTE;
break;
}
}

if ( format != GL_RGBA || type != GL_UNSIGNED_BYTE )
{
formatBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height );
}

// copy or resample data as appropriate for first MIP level
if ( ( scaled_width == width ) &&
( scaled_height == height ) ) {
if (!mipmap)
{
qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
if ( formatBuffer )
{
R_ConvertTextureFormat( (byte *)data, scaled_width, scaled_height, format, type, (byte *)formatBuffer );
qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, format, type, formatBuffer);
}
else
{
qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
*pUploadWidth = scaled_width;
*pUploadHeight = scaled_height;
*format = internalFormat;
*pInternalFormat = internalFormat;

goto done;
}
Expand All @@ -773,9 +904,17 @@ static void Upload32( unsigned *data,

*pUploadWidth = scaled_width;
*pUploadHeight = scaled_height;
*format = internalFormat;
*pInternalFormat = internalFormat;

qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
if ( formatBuffer )
{
R_ConvertTextureFormat( (byte *)scaledBuffer, scaled_width, scaled_height, format, type, (byte *)formatBuffer );
qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, format, type, formatBuffer );
}
else
{
qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
}

if (mipmap)
{
Expand All @@ -797,7 +936,15 @@ static void Upload32( unsigned *data,
R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] );
}

qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
if ( formatBuffer )
{
R_ConvertTextureFormat( (byte *)scaledBuffer, scaled_width, scaled_height, format, type, (byte *)formatBuffer );
qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, format, type, formatBuffer );
}
else
{
qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer );
}
}
}
done:
Expand All @@ -822,6 +969,8 @@ static void Upload32( unsigned *data,

GL_CheckErrors();

if ( formatBuffer != 0 )
ri.Hunk_FreeTempMemory( formatBuffer );
if ( scaledBuffer != 0 )
ri.Hunk_FreeTempMemory( scaledBuffer );
if ( resampledBuffer != 0 )
Expand Down
Loading

0 comments on commit b1943b8

Please sign in to comment.