mirror of
https://github.com/ioquake/ioq3.git
synced 2025-04-21 16:00:47 +00:00
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:
parent
1fc83e4845
commit
3b984d2b51
14 changed files with 658 additions and 167 deletions
15
README.md
15
README.md
|
@ -160,6 +160,21 @@ Makefile.local:
|
|||
The defaults for these variables differ depending on the target platform.
|
||||
|
||||
|
||||
# OpenGL ES support
|
||||
|
||||
The opengl2 renderer (the default) supports OpenGL ES 2+. Though there
|
||||
are many missing features and the performance may not be sufficient for
|
||||
embedded System-on-a-Chip and mobile platforms.
|
||||
|
||||
The opengl1 renderer does not have OpenGL ES support.
|
||||
|
||||
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 cl_renderer opengl2 +set r_useOpenGLES 1
|
||||
|
||||
|
||||
# Console
|
||||
|
||||
## New cvars
|
||||
|
|
|
@ -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 but OpenGL ES reads RGBA and converts to RGB in-place.
|
||||
// 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);
|
||||
|
||||
|
|
|
@ -732,6 +732,7 @@ 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;
|
||||
GLuint texture;
|
||||
|
||||
if (!tr.scratchImage[client])
|
||||
|
@ -746,7 +747,18 @@ void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int
|
|||
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;
|
||||
qglTextureImage2DEXT(texture, GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, 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 );
|
||||
qglTextureImage2DEXT(texture, GL_TEXTURE_2D, 0, GL_RGB, cols, rows, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
ri.Hunk_FreeTempMemory( buffer );
|
||||
} else {
|
||||
qglTextureImage2DEXT(texture, GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
|
||||
qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
||||
qglTextureParameterfEXT(texture, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
|
@ -755,7 +767,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
|
||||
qglTextureSubImage2DEXT(texture, 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 );
|
||||
qglTextureSubImage2DEXT(texture, GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGB, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
ri.Hunk_FreeTempMemory( buffer );
|
||||
} else {
|
||||
qglTextureSubImage2DEXT(texture, GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1140,14 +1161,14 @@ const void *RB_DrawSurfs( const void *data ) {
|
|||
if (glRefConfig.occlusionQuery)
|
||||
{
|
||||
tr.sunFlareQueryActive[tr.sunFlareQueryIndex] = qtrue;
|
||||
qglBeginQuery(GL_SAMPLES_PASSED, tr.sunFlareQuery[tr.sunFlareQueryIndex]);
|
||||
qglBeginQuery(glRefConfig.occlusionQueryTarget, tr.sunFlareQuery[tr.sunFlareQueryIndex]);
|
||||
}
|
||||
|
||||
RB_DrawSun(0.3, tr.sunFlareShader);
|
||||
|
||||
if (glRefConfig.occlusionQuery)
|
||||
{
|
||||
qglEndQuery(GL_SAMPLES_PASSED);
|
||||
qglEndQuery(glRefConfig.occlusionQueryTarget);
|
||||
}
|
||||
|
||||
FBO_Bind(oldFbo);
|
||||
|
|
|
@ -276,7 +276,7 @@ static void R_LoadLightmaps( lump_t *l, lump_t *surfs ) {
|
|||
tr.deluxemaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low );
|
||||
|
||||
textureInternalFormat = GL_RGBA8;
|
||||
if (r_hdr->integer)
|
||||
if (r_hdr->integer && !qglesMajorVersion)
|
||||
{
|
||||
// Check for the first hdr lightmap, if it exists, use GL_RGBA16 for textures.
|
||||
char filename[MAX_QPATH];
|
||||
|
|
|
@ -348,7 +348,13 @@ void RE_BeginFrame( stereoFrame_t stereoFrame ) {
|
|||
//
|
||||
if ( r_measureOverdraw->integer )
|
||||
{
|
||||
if ( glConfig.stencilBits < 4 )
|
||||
if ( qglesMajorVersion >= 1 && !glRefConfig.readStencil )
|
||||
{
|
||||
ri.Printf( PRINT_WARNING, "OpenGL ES needs GL_NV_read_stencil to read stencil bits to measure overdraw\n" );
|
||||
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" );
|
||||
|
@ -426,6 +432,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)
|
||||
|
|
|
@ -53,8 +53,83 @@ void GLimp_InitExtraExtensions(void)
|
|||
// GL function loader, based on https://gist.github.com/rygorous/16796a0c876cf8a5f542caddb55bce8a
|
||||
#define GLE(ret, name, ...) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name);
|
||||
|
||||
//
|
||||
// OpenGL ES extensions
|
||||
//
|
||||
if (qglesMajorVersion)
|
||||
{
|
||||
if (!r_allowExtensions->integer)
|
||||
goto done;
|
||||
|
||||
extension = "GL_EXT_occlusion_query_boolean";
|
||||
if (SDL_GL_ExtensionSupported(extension))
|
||||
{
|
||||
glRefConfig.occlusionQuery = qtrue;
|
||||
glRefConfig.occlusionQueryTarget = GL_ANY_SAMPLES_PASSED;
|
||||
|
||||
QGL_ARB_occlusion_query_PROCS;
|
||||
|
||||
ri.Printf(PRINT_ALL, result[glRefConfig.occlusionQuery], extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf(PRINT_ALL, result[2], extension);
|
||||
}
|
||||
|
||||
// GL_NV_read_depth
|
||||
extension = "GL_NV_read_depth";
|
||||
if (SDL_GL_ExtensionSupported(extension))
|
||||
{
|
||||
glRefConfig.readDepth = qtrue;
|
||||
ri.Printf(PRINT_ALL, result[glRefConfig.readDepth], extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf(PRINT_ALL, result[2], extension);
|
||||
}
|
||||
|
||||
// GL_NV_read_stencil
|
||||
extension = "GL_NV_read_stencil";
|
||||
if (SDL_GL_ExtensionSupported(extension))
|
||||
{
|
||||
glRefConfig.readStencil = qtrue;
|
||||
ri.Printf(PRINT_ALL, result[glRefConfig.readStencil], extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf(PRINT_ALL, result[2], extension);
|
||||
}
|
||||
|
||||
// GL_EXT_shadow_samplers
|
||||
extension = "GL_EXT_shadow_samplers";
|
||||
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
|
||||
{
|
||||
glRefConfig.shadowSamplers = qtrue;
|
||||
ri.Printf(PRINT_ALL, result[glRefConfig.shadowSamplers], extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf(PRINT_ALL, result[2], extension);
|
||||
}
|
||||
|
||||
// GL_OES_standard_derivatives
|
||||
extension = "GL_OES_standard_derivatives";
|
||||
if (qglesMajorVersion >= 3 || SDL_GL_ExtensionSupported(extension))
|
||||
{
|
||||
glRefConfig.standardDerivatives = qtrue;
|
||||
ri.Printf(PRINT_ALL, result[glRefConfig.standardDerivatives], extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf(PRINT_ALL, result[2], extension);
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
// OpenGL 1.5 - GL_ARB_occlusion_query
|
||||
glRefConfig.occlusionQuery = qtrue;
|
||||
glRefConfig.occlusionQueryTarget = GL_SAMPLES_PASSED;
|
||||
QGL_ARB_occlusion_query_PROCS;
|
||||
|
||||
// OpenGL 3.0 - GL_ARB_framebuffer_object
|
||||
|
@ -146,18 +221,6 @@ void GLimp_InitExtraExtensions(void)
|
|||
ri.Printf(PRINT_ALL, result[2], extension);
|
||||
}
|
||||
|
||||
// Determine GLSL version
|
||||
if (1)
|
||||
{
|
||||
char version[256];
|
||||
|
||||
Q_strncpyz(version, (char *)qglGetString(GL_SHADING_LANGUAGE_VERSION), sizeof(version));
|
||||
|
||||
sscanf(version, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion);
|
||||
|
||||
ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version);
|
||||
}
|
||||
|
||||
glRefConfig.memInfo = MI_NONE;
|
||||
|
||||
// GL_NVX_gpu_memory_info
|
||||
|
@ -249,5 +312,26 @@ void GLimp_InitExtraExtensions(void)
|
|||
ri.Printf(PRINT_ALL, result[2], extension);
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
// Determine GLSL version
|
||||
if (1)
|
||||
{
|
||||
char version[256], *version_p;
|
||||
|
||||
Q_strncpyz(version, (char *)qglGetString(GL_SHADING_LANGUAGE_VERSION), sizeof(version));
|
||||
|
||||
// Skip leading text such as "OpenGL ES GLSL ES "
|
||||
version_p = version;
|
||||
while ( *version_p && !isdigit( *version_p ) )
|
||||
{
|
||||
version_p++;
|
||||
}
|
||||
|
||||
sscanf(version_p, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion);
|
||||
|
||||
ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version);
|
||||
}
|
||||
|
||||
#undef GLE
|
||||
}
|
||||
|
|
|
@ -478,6 +478,14 @@ void RB_RenderFlares (void) {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( r_flares->modified ) {
|
||||
if ( qglesMajorVersion >= 1 && !glRefConfig.readDepth ) {
|
||||
ri.Printf( PRINT_WARNING, "OpenGL ES needs GL_NV_read_depth to read depth to determine if flares are visible\n" );
|
||||
ri.Cvar_Set( "r_flares", "0" );
|
||||
}
|
||||
r_flares->modified = qfalse;
|
||||
}
|
||||
|
||||
if(r_flareCoeff->modified)
|
||||
{
|
||||
R_SetFlareCoeff();
|
||||
|
|
|
@ -249,11 +249,25 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
|
|||
// HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones
|
||||
if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30))
|
||||
{
|
||||
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 50))
|
||||
if (qglesMajorVersion >= 3 && glRefConfig.glslMajorVersion >= 3)
|
||||
Q_strcat(dest, size, "#version 300 es\n");
|
||||
else if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 50))
|
||||
Q_strcat(dest, size, "#version 150\n");
|
||||
else
|
||||
Q_strcat(dest, size, "#version 130\n");
|
||||
|
||||
// `extra' may contain #extension which must be directly after #version
|
||||
if (extra)
|
||||
{
|
||||
Q_strcat(dest, size, extra);
|
||||
}
|
||||
|
||||
if (qglesMajorVersion >= 2)
|
||||
{
|
||||
Q_strcat(dest, size, "precision mediump float;\n");
|
||||
Q_strcat(dest, size, "precision mediump sampler2DShadow;\n");
|
||||
}
|
||||
|
||||
if(shaderType == GL_VERTEX_SHADER)
|
||||
{
|
||||
Q_strcat(dest, size, "#define attribute in\n");
|
||||
|
@ -272,8 +286,34 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
|
|||
}
|
||||
else
|
||||
{
|
||||
Q_strcat(dest, size, "#version 120\n");
|
||||
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2D(a,b).r \n");
|
||||
if (qglesMajorVersion >= 2)
|
||||
{
|
||||
Q_strcat(dest, size, "#version 100\n");
|
||||
|
||||
if (extra)
|
||||
{
|
||||
Q_strcat(dest, size, extra);
|
||||
}
|
||||
|
||||
Q_strcat(dest, size, "precision mediump float;\n");
|
||||
|
||||
if (glRefConfig.shadowSamplers)
|
||||
{
|
||||
Q_strcat(dest, size, "precision mediump sampler2DShadow;\n");
|
||||
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2DEXT(a,b)\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strcat(dest, size, "#version 120\n");
|
||||
|
||||
if (extra)
|
||||
{
|
||||
Q_strcat(dest, size, extra);
|
||||
}
|
||||
|
||||
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2D(a,b).r\n");
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: add some macros to avoid extra uniforms and save speed and code maintenance
|
||||
|
@ -361,11 +401,6 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *
|
|||
Q_strcat(dest, size, va("#define ROUGHNESS_MIPS float(%d)\n", numRoughnessMips));
|
||||
}
|
||||
|
||||
if (extra)
|
||||
{
|
||||
Q_strcat(dest, size, extra);
|
||||
}
|
||||
|
||||
// OK we added a lot of stuff but if we do something bad in the GLSL shaders then we want the proper line
|
||||
// so we have to reset the line counting
|
||||
Q_strcat(dest, size, "#line 0\n");
|
||||
|
@ -1350,86 +1385,110 @@ void GLSL_InitGPUShaders(void)
|
|||
}
|
||||
|
||||
|
||||
attribs = ATTR_POSITION | ATTR_TEXCOORD;
|
||||
extradefines[0] = '\0';
|
||||
|
||||
if (r_shadowFilter->integer >= 1)
|
||||
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER\n");
|
||||
|
||||
if (r_shadowFilter->integer >= 2)
|
||||
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER2\n");
|
||||
|
||||
if (r_shadowCascadeZFar->integer != 0)
|
||||
Q_strcat(extradefines, 1024, "#define USE_SHADOW_CASCADE\n");
|
||||
|
||||
Q_strcat(extradefines, 1024, va("#define r_shadowMapSize %f\n", r_shadowMapSize->value));
|
||||
Q_strcat(extradefines, 1024, va("#define r_shadowCascadeZFar %f\n", r_shadowCascadeZFar->value));
|
||||
|
||||
|
||||
if (!GLSL_InitGPUShader(&tr.shadowmaskShader, "shadowmask", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowmask_vp, fallbackShader_shadowmask_fp))
|
||||
{
|
||||
ri.Error(ERR_FATAL, "Could not load shadowmask shader!");
|
||||
}
|
||||
|
||||
GLSL_InitUniforms(&tr.shadowmaskShader);
|
||||
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP, TB_SHADOWMAP);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP2, TB_SHADOWMAP2);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP3, TB_SHADOWMAP3);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP4, TB_SHADOWMAP4);
|
||||
|
||||
GLSL_FinishGPUShader(&tr.shadowmaskShader);
|
||||
|
||||
numEtcShaders++;
|
||||
|
||||
|
||||
attribs = ATTR_POSITION | ATTR_TEXCOORD;
|
||||
extradefines[0] = '\0';
|
||||
|
||||
if (!GLSL_InitGPUShader(&tr.ssaoShader, "ssao", attribs, qtrue, extradefines, qtrue, fallbackShader_ssao_vp, fallbackShader_ssao_fp))
|
||||
{
|
||||
ri.Error(ERR_FATAL, "Could not load ssao shader!");
|
||||
}
|
||||
|
||||
GLSL_InitUniforms(&tr.ssaoShader);
|
||||
|
||||
GLSL_SetUniformInt(&tr.ssaoShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
|
||||
|
||||
GLSL_FinishGPUShader(&tr.ssaoShader);
|
||||
|
||||
numEtcShaders++;
|
||||
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
// GLSL 1.10+ or GL_EXT_shadow_samplers extension are required for sampler2DShadow type
|
||||
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 10)
|
||||
|| glRefConfig.shadowSamplers)
|
||||
{
|
||||
attribs = ATTR_POSITION | ATTR_TEXCOORD;
|
||||
extradefines[0] = '\0';
|
||||
|
||||
if (i & 1)
|
||||
Q_strcat(extradefines, 1024, "#define USE_VERTICAL_BLUR\n");
|
||||
else
|
||||
Q_strcat(extradefines, 1024, "#define USE_HORIZONTAL_BLUR\n");
|
||||
|
||||
if (!(i & 2))
|
||||
Q_strcat(extradefines, 1024, "#define USE_DEPTH\n");
|
||||
|
||||
|
||||
if (!GLSL_InitGPUShader(&tr.depthBlurShader[i], "depthBlur", attribs, qtrue, extradefines, qtrue, fallbackShader_depthblur_vp, fallbackShader_depthblur_fp))
|
||||
if (qglesMajorVersion < 3 && glRefConfig.shadowSamplers)
|
||||
{
|
||||
ri.Error(ERR_FATAL, "Could not load depthBlur shader!");
|
||||
Q_strcat(extradefines, 1024, "#extension GL_EXT_shadow_samplers : enable\n");
|
||||
}
|
||||
|
||||
GLSL_InitUniforms(&tr.depthBlurShader[i]);
|
||||
|
||||
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENIMAGEMAP, TB_COLORMAP);
|
||||
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP);
|
||||
if (r_shadowFilter->integer >= 1)
|
||||
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER\n");
|
||||
|
||||
GLSL_FinishGPUShader(&tr.depthBlurShader[i]);
|
||||
if (r_shadowFilter->integer >= 2)
|
||||
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER2\n");
|
||||
|
||||
if (r_shadowCascadeZFar->integer != 0)
|
||||
Q_strcat(extradefines, 1024, "#define USE_SHADOW_CASCADE\n");
|
||||
|
||||
Q_strcat(extradefines, 1024, va("#define r_shadowMapSize %f\n", r_shadowMapSize->value));
|
||||
Q_strcat(extradefines, 1024, va("#define r_shadowCascadeZFar %f\n", r_shadowCascadeZFar->value));
|
||||
|
||||
if (!GLSL_InitGPUShader(&tr.shadowmaskShader, "shadowmask", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowmask_vp, fallbackShader_shadowmask_fp))
|
||||
{
|
||||
ri.Error(ERR_FATAL, "Could not load shadowmask shader!");
|
||||
}
|
||||
|
||||
GLSL_InitUniforms(&tr.shadowmaskShader);
|
||||
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP, TB_SHADOWMAP);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP2, TB_SHADOWMAP2);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP3, TB_SHADOWMAP3);
|
||||
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP4, TB_SHADOWMAP4);
|
||||
|
||||
GLSL_FinishGPUShader(&tr.shadowmaskShader);
|
||||
|
||||
numEtcShaders++;
|
||||
}
|
||||
|
||||
|
||||
// GLSL 1.10+ or GL_OES_standard_derivatives extension are required for dFdx() and dFdy() GLSL functions
|
||||
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 10)
|
||||
|| glRefConfig.standardDerivatives)
|
||||
{
|
||||
attribs = ATTR_POSITION | ATTR_TEXCOORD;
|
||||
extradefines[0] = '\0';
|
||||
|
||||
if (qglesMajorVersion < 3 && glRefConfig.standardDerivatives)
|
||||
{
|
||||
Q_strcat(extradefines, 1024, "#extension GL_OES_standard_derivatives : enable\n");
|
||||
}
|
||||
|
||||
if (!GLSL_InitGPUShader(&tr.ssaoShader, "ssao", attribs, qtrue, extradefines, qtrue, fallbackShader_ssao_vp, fallbackShader_ssao_fp))
|
||||
{
|
||||
ri.Error(ERR_FATAL, "Could not load ssao shader!");
|
||||
}
|
||||
|
||||
GLSL_InitUniforms(&tr.ssaoShader);
|
||||
|
||||
GLSL_SetUniformInt(&tr.ssaoShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
|
||||
|
||||
GLSL_FinishGPUShader(&tr.ssaoShader);
|
||||
|
||||
numEtcShaders++;
|
||||
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
attribs = ATTR_POSITION | ATTR_TEXCOORD;
|
||||
extradefines[0] = '\0';
|
||||
|
||||
if (qglesMajorVersion < 3 && glRefConfig.standardDerivatives)
|
||||
{
|
||||
Q_strcat(extradefines, 1024, "#extension GL_OES_standard_derivatives : enable\n");
|
||||
}
|
||||
|
||||
if (i & 1)
|
||||
Q_strcat(extradefines, 1024, "#define USE_VERTICAL_BLUR\n");
|
||||
else
|
||||
Q_strcat(extradefines, 1024, "#define USE_HORIZONTAL_BLUR\n");
|
||||
|
||||
if (!(i & 2))
|
||||
Q_strcat(extradefines, 1024, "#define USE_DEPTH\n");
|
||||
|
||||
|
||||
if (!GLSL_InitGPUShader(&tr.depthBlurShader[i], "depthBlur", attribs, qtrue, extradefines, qtrue, fallbackShader_depthblur_vp, fallbackShader_depthblur_fp))
|
||||
{
|
||||
ri.Error(ERR_FATAL, "Could not load depthBlur shader!");
|
||||
}
|
||||
|
||||
GLSL_InitUniforms(&tr.depthBlurShader[i]);
|
||||
|
||||
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENIMAGEMAP, TB_COLORMAP);
|
||||
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP);
|
||||
|
||||
GLSL_FinishGPUShader(&tr.depthBlurShader[i]);
|
||||
|
||||
numEtcShaders++;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
attribs = ATTR_POSITION | ATTR_TEXCOORD;
|
||||
extradefines[0] = '\0';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
|
|
@ -451,21 +451,43 @@ Return value must be freed with ri.Hunk_FreeTempMemory()
|
|||
byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen)
|
||||
{
|
||||
byte *buffer, *bufstart;
|
||||
int padwidth, linelen;
|
||||
GLint packAlign;
|
||||
|
||||
int padwidth, linelen, bytesPerPixel;
|
||||
int yin, xin, xout;
|
||||
GLint packAlign, format;
|
||||
|
||||
// OpenGL ES is only required to support reading GL_RGBA
|
||||
if (qglesMajorVersion >= 1) {
|
||||
format = GL_RGBA;
|
||||
bytesPerPixel = 4;
|
||||
} else {
|
||||
format = GL_RGB;
|
||||
bytesPerPixel = 3;
|
||||
}
|
||||
|
||||
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
|
||||
|
||||
linelen = width * 3;
|
||||
|
||||
linelen = width * bytesPerPixel;
|
||||
padwidth = PAD(linelen, packAlign);
|
||||
|
||||
|
||||
// Allocate a few more bytes so that we can choose an alignment we like
|
||||
buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1);
|
||||
|
||||
bufstart = PADP((intptr_t) buffer + *offset, packAlign);
|
||||
|
||||
qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart);
|
||||
|
||||
bufstart = PADP((intptr_t) buffer + *offset, packAlign);
|
||||
qglReadPixels(x, y, width, height, format, GL_UNSIGNED_BYTE, bufstart);
|
||||
|
||||
linelen = width * 3;
|
||||
|
||||
// Convert RGBA to RGB, in place, line by line
|
||||
if (format == GL_RGBA) {
|
||||
for (yin = 0; yin < height; yin++) {
|
||||
for (xin = 0, xout = 0; xout < linelen; xin += 4, xout += 3) {
|
||||
bufstart[yin*padwidth + xout + 0] = bufstart[yin*padwidth + xin + 0];
|
||||
bufstart[yin*padwidth + xout + 1] = bufstart[yin*padwidth + xin + 1];
|
||||
bufstart[yin*padwidth + xout + 2] = bufstart[yin*padwidth + xin + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*offset = bufstart - buffer;
|
||||
*padlen = padwidth - linelen;
|
||||
|
||||
|
@ -877,9 +899,10 @@ const void *RB_TakeVideoFrameCmd( const void *data )
|
|||
{
|
||||
const videoFrameCommand_t *cmd;
|
||||
byte *cBuf;
|
||||
size_t memcount, linelen;
|
||||
size_t memcount, bytesPerPixel, linelen, avilinelen;
|
||||
int padwidth, avipadwidth, padlen, avipadlen;
|
||||
GLint packAlign;
|
||||
int yin, xin, xout;
|
||||
GLint packAlign, format;
|
||||
|
||||
// finish any 2D drawing if needed
|
||||
if(tess.numIndexes)
|
||||
|
@ -887,20 +910,32 @@ const void *RB_TakeVideoFrameCmd( const void *data )
|
|||
|
||||
cmd = (const videoFrameCommand_t *)data;
|
||||
|
||||
// OpenGL ES is only required to support reading GL_RGBA
|
||||
if (qglesMajorVersion >= 1) {
|
||||
format = GL_RGBA;
|
||||
bytesPerPixel = 4;
|
||||
} else {
|
||||
format = GL_RGB;
|
||||
bytesPerPixel = 3;
|
||||
}
|
||||
|
||||
qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign);
|
||||
|
||||
linelen = cmd->width * 3;
|
||||
linelen = cmd->width * bytesPerPixel;
|
||||
|
||||
// Alignment stuff for glReadPixels
|
||||
padwidth = PAD(linelen, packAlign);
|
||||
padlen = padwidth - linelen;
|
||||
|
||||
avilinelen = cmd->width * 3;
|
||||
|
||||
// AVI line padding
|
||||
avipadwidth = PAD(linelen, AVI_LINE_PADDING);
|
||||
avipadlen = avipadwidth - linelen;
|
||||
avipadwidth = PAD(avilinelen, AVI_LINE_PADDING);
|
||||
avipadlen = avipadwidth - avilinelen;
|
||||
|
||||
cBuf = PADP(cmd->captureBuffer, packAlign);
|
||||
|
||||
qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB,
|
||||
qglReadPixels(0, 0, cmd->width, cmd->height, format,
|
||||
GL_UNSIGNED_BYTE, cBuf);
|
||||
|
||||
memcount = padwidth * cmd->height;
|
||||
|
@ -911,7 +946,21 @@ const void *RB_TakeVideoFrameCmd( const void *data )
|
|||
|
||||
if(cmd->motionJpeg)
|
||||
{
|
||||
memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height,
|
||||
// Convert RGBA to RGB, in place, line by line
|
||||
if (format == GL_RGBA) {
|
||||
linelen = cmd->width * 3;
|
||||
padlen = padwidth - linelen;
|
||||
|
||||
for (yin = 0; yin < cmd->height; yin++) {
|
||||
for (xin = 0, xout = 0; xout < linelen; xin += 4, xout += 3) {
|
||||
cBuf[yin*padwidth + xout + 0] = cBuf[yin*padwidth + xin + 0];
|
||||
cBuf[yin*padwidth + xout + 1] = cBuf[yin*padwidth + xin + 1];
|
||||
cBuf[yin*padwidth + xout + 2] = cBuf[yin*padwidth + xin + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, avilinelen * cmd->height,
|
||||
r_aviMotionJpegQuality->integer,
|
||||
cmd->width, cmd->height, cBuf, padlen);
|
||||
ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount);
|
||||
|
@ -934,7 +983,7 @@ const void *RB_TakeVideoFrameCmd( const void *data )
|
|||
*destptr++ = srcptr[2];
|
||||
*destptr++ = srcptr[1];
|
||||
*destptr++ = srcptr[0];
|
||||
srcptr += 3;
|
||||
srcptr += bytesPerPixel;
|
||||
}
|
||||
|
||||
Com_Memset(destptr, '\0', avipadlen);
|
||||
|
|
|
@ -1406,6 +1406,7 @@ typedef struct {
|
|||
qboolean intelGraphics;
|
||||
|
||||
qboolean occlusionQuery;
|
||||
GLenum occlusionQueryTarget;
|
||||
|
||||
int glslMajorVersion;
|
||||
int glslMinorVersion;
|
||||
|
@ -1429,6 +1430,12 @@ typedef struct {
|
|||
|
||||
qboolean vertexArrayObject;
|
||||
qboolean directStateAccess;
|
||||
|
||||
// OpenGL ES extensions
|
||||
qboolean readDepth;
|
||||
qboolean readStencil;
|
||||
qboolean shadowSamplers;
|
||||
qboolean standardDerivatives;
|
||||
} glRefConfig_t;
|
||||
|
||||
|
||||
|
@ -2502,5 +2509,7 @@ size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
|
|||
void RE_TakeVideoFrame( int width, int height,
|
||||
byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg );
|
||||
|
||||
void R_ConvertTextureFormat( const byte *in, int width, int height, GLenum format, GLenum type, byte *out );
|
||||
|
||||
|
||||
#endif //TR_LOCAL_H
|
||||
|
|
|
@ -290,6 +290,8 @@ static qboolean RB_UpdateSunFlareVis(void)
|
|||
ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter);
|
||||
}
|
||||
|
||||
// Note: On desktop OpenGL this is a sample count (glRefConfig.occlusionQueryTarget == GL_SAMPLES_PASSED)
|
||||
// but on OpenGL ES this is a boolean (glRefConfig.occlusionQueryTarget == GL_ANY_SAMPLES_PASSED)
|
||||
qglGetQueryObjectuiv(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT, &sampleCount);
|
||||
return sampleCount > 0;
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ cvar_t *r_allowSoftwareGL; // Don't abort out if a hardware visual can't be obta
|
|||
cvar_t *r_allowResize; // make window resizable
|
||||
cvar_t *r_centerWindow;
|
||||
cvar_t *r_sdlDriver;
|
||||
cvar_t *r_useOpenGLES;
|
||||
|
||||
int qglMajorVersion, qglMinorVersion;
|
||||
int qglesMajorVersion, qglesMinorVersion;
|
||||
|
@ -230,6 +231,27 @@ static void GLimp_DetectAvailableModes(void)
|
|||
SDL_free( modes );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
OpenGL ES compatibility
|
||||
===============
|
||||
*/
|
||||
static void APIENTRY GLimp_GLES_ClearDepth( GLclampd depth ) {
|
||||
qglClearDepthf( depth );
|
||||
}
|
||||
|
||||
static void APIENTRY GLimp_GLES_DepthRange( GLclampd near_val, GLclampd far_val ) {
|
||||
qglDepthRangef( near_val, far_val );
|
||||
}
|
||||
|
||||
static void APIENTRY GLimp_GLES_DrawBuffer( GLenum mode ) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
static void APIENTRY GLimp_GLES_PolygonMode( GLenum face, GLenum mode ) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
GLimp_GetProcAddresses
|
||||
|
@ -306,8 +328,11 @@ static qboolean GLimp_GetProcAddresses( qboolean fixedFunction ) {
|
|||
QGL_1_3_PROCS;
|
||||
QGL_1_5_PROCS;
|
||||
QGL_2_0_PROCS;
|
||||
// error so this doesn't segfault due to NULL desktop GL functions being used
|
||||
Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s", version );
|
||||
|
||||
qglClearDepth = GLimp_GLES_ClearDepth;
|
||||
qglDepthRange = GLimp_GLES_DepthRange;
|
||||
qglDrawBuffer = GLimp_GLES_DrawBuffer;
|
||||
qglPolygonMode = GLimp_GLES_PolygonMode;
|
||||
} else {
|
||||
Com_Error( ERR_FATAL, "Unsupported OpenGL Version (%s), OpenGL 2.0 is required", version );
|
||||
}
|
||||
|
@ -633,57 +658,91 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool
|
|||
|
||||
if (!fixedFunction)
|
||||
{
|
||||
int profileMask, majorVersion, minorVersion;
|
||||
SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profileMask);
|
||||
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &majorVersion);
|
||||
SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minorVersion);
|
||||
int profileMask;
|
||||
|
||||
ri.Printf(PRINT_ALL, "Trying to get an OpenGL 3.2 core context\n");
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
if ((SDL_glContext = SDL_GL_CreateContext(SDL_window)) == NULL)
|
||||
SDL_GL_GetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, &profileMask );
|
||||
|
||||
if ( r_useOpenGLES->integer == 1 || ( r_useOpenGLES->integer == -1 && profileMask == SDL_GL_CONTEXT_PROFILE_ES ) )
|
||||
{
|
||||
ri.Printf(PRINT_ALL, "SDL_GL_CreateContext failed: %s\n", SDL_GetError());
|
||||
ri.Printf(PRINT_ALL, "Reverting to default context\n");
|
||||
ri.Printf( PRINT_ALL, "Trying to get an OpenGL ES 2.0 context\n" );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 );
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profileMask);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorVersion);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *renderer;
|
||||
|
||||
ri.Printf(PRINT_ALL, "SDL_GL_CreateContext succeeded.\n");
|
||||
|
||||
if ( GLimp_GetProcAddresses( fixedFunction ) )
|
||||
SDL_glContext = SDL_GL_CreateContext( SDL_window );
|
||||
if ( !SDL_glContext )
|
||||
{
|
||||
renderer = (const char *)qglGetString(GL_RENDERER);
|
||||
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed for OpenGL 3.2 core context\n" );
|
||||
renderer = NULL;
|
||||
}
|
||||
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext succeeded.\n" );
|
||||
|
||||
if (!renderer || (strstr(renderer, "Software Renderer") || strstr(renderer, "Software Rasterizer")))
|
||||
if ( !GLimp_GetProcAddresses( fixedFunction ) )
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed for OpenGL ES 2.0 context\n" );
|
||||
GLimp_ClearProcAddresses();
|
||||
SDL_GL_DeleteContext( SDL_glContext );
|
||||
SDL_glContext = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !SDL_glContext )
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "Trying to get an OpenGL 3.2 core context\n" );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 );
|
||||
|
||||
SDL_glContext = SDL_GL_CreateContext( SDL_window );
|
||||
if ( !SDL_glContext )
|
||||
{
|
||||
if ( renderer )
|
||||
ri.Printf(PRINT_ALL, "GL_RENDERER is %s, rejecting context\n", renderer);
|
||||
|
||||
GLimp_ClearProcAddresses();
|
||||
SDL_GL_DeleteContext(SDL_glContext);
|
||||
SDL_glContext = NULL;
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profileMask);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorVersion);
|
||||
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext failed: %s\n", SDL_GetError() );
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *renderer;
|
||||
|
||||
ri.Printf( PRINT_ALL, "SDL_GL_CreateContext succeeded.\n" );
|
||||
|
||||
if ( GLimp_GetProcAddresses( fixedFunction ) )
|
||||
{
|
||||
renderer = (const char *)qglGetString( GL_RENDERER );
|
||||
}
|
||||
else
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed for OpenGL 3.2 core context\n" );
|
||||
renderer = NULL;
|
||||
}
|
||||
|
||||
if ( !renderer || strstr( renderer, "Software Renderer" ) || strstr( renderer, "Software Rasterizer" ) )
|
||||
{
|
||||
if ( renderer )
|
||||
ri.Printf(PRINT_ALL, "GL_RENDERER is %s, rejecting context\n", renderer);
|
||||
|
||||
GLimp_ClearProcAddresses();
|
||||
SDL_GL_DeleteContext( SDL_glContext );
|
||||
SDL_glContext = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !SDL_glContext )
|
||||
{
|
||||
ri.Printf( PRINT_ALL, "Trying to get an OpenGL 2.0 context\n" );
|
||||
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, 0 );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 2 );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, 0 );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1 );
|
||||
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
|
||||
|
||||
SDL_glContext = NULL;
|
||||
}
|
||||
|
||||
|
@ -815,7 +874,7 @@ static void GLimp_InitExtensions( qboolean fixedFunction )
|
|||
glConfig.textureCompression = TC_NONE;
|
||||
|
||||
// GL_EXT_texture_compression_s3tc
|
||||
if ( SDL_GL_ExtensionSupported( "GL_ARB_texture_compression" ) &&
|
||||
if ( ( QGLES_VERSION_ATLEAST( 2, 0 ) || SDL_GL_ExtensionSupported( "GL_ARB_texture_compression" ) ) &&
|
||||
SDL_GL_ExtensionSupported( "GL_EXT_texture_compression_s3tc" ) )
|
||||
{
|
||||
if ( r_ext_compressed_textures->value )
|
||||
|
@ -996,6 +1055,7 @@ void GLimp_Init( qboolean fixedFunction )
|
|||
r_sdlDriver = ri.Cvar_Get( "r_sdlDriver", "", CVAR_ROM );
|
||||
r_allowResize = ri.Cvar_Get( "r_allowResize", "0", CVAR_ARCHIVE | CVAR_LATCH );
|
||||
r_centerWindow = ri.Cvar_Get( "r_centerWindow", "0", CVAR_ARCHIVE | CVAR_LATCH );
|
||||
r_useOpenGLES = ri.Cvar_Get( "r_useOpenGLES", "-1", CVAR_ARCHIVE | CVAR_LATCH );
|
||||
|
||||
if( ri.Cvar_VariableIntegerValue( "com_abnormalExit" ) )
|
||||
{
|
||||
|
|
|
@ -63,6 +63,14 @@ For Win32:
|
|||
CVARS
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Cvars for API:
|
||||
|
||||
* `r_useOpenGLES` - This enables using OpenGL ES 2+.
|
||||
Many features are not supported such as sun shadows and HDR.
|
||||
1 - Use OpenGL ES.
|
||||
0 - Use desktop OpenGL.
|
||||
-1 - Automatically pick (default).
|
||||
|
||||
Cvars for simple rendering features:
|
||||
|
||||
* `r_ext_compressed_textures` - Automatically compress textures.
|
||||
|
|
Loading…
Reference in a new issue