mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-12-02 00:11:58 +00:00
95fbb18fa1
Fix going to previous browser source in q3_ui Limit ui_smallFont/ui_bigFont/cg_noTaunt cvars to missionpack Fix team chat box for spectators Don't draw crosshair 0 in Team Arena setup menu Make client for Windows x86_64 use OpenAL64.dll by default Fix loading renderer DLLs on Windows x86 Add Windows application manifest Disable DPI scaling on Windows ignore window resize event on fullscreen Don't reload arenas.txt/*.arena files in Team Arena UI Fix crash when out of memory in Team Arena's String_Alloc Fix in_nograb not releasing the mouse cursor Update UI player animation handling to match CGame Fix specifying minimum mac os version in make-macosx.sh Fix listen server sending snapshots each client frame Statically link libgcc on Windows Fix hit accuracy stats for lightning gun and shotgun kills Don't link to libGL at compile time Add common OpenGL version parsing + OpenGL 3 fixes Support parsing OpenGL ES version strings Fix setting cflags/libs from sdl2-config Load OpenGL ES 1.1 function procs [qcommon] Use unsigned types where wrapping arithmetic is intended OpenGL2: Fix brightness when r_autoExposure is disabled OpenGL2: Fix MD3 surface with zero shaders dividing by zero [botlib/be_aas_def.h] Change array size from MAX_PATH to MAX_QPATH Don't redefine MAX_PATH in bot code Fix memory leak in (unused) AAS_FloodAreas() Fix compiling GLSL shaders under Windows. Only draw cm_patch/bot debug polygons in world scenes Fix reading crash log when log wraps around buffer Don't send team overlay info to bots Fix a race condition in the makedirs target Fix shader corruption on OpenBSD
483 lines
13 KiB
C
483 lines
13 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete
|
|
|
|
This file is part of Reaction source code.
|
|
|
|
Reaction source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Reaction source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Reaction source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "tr_local.h"
|
|
|
|
void RB_ToneMap(FBO_t *hdrFbo, ivec4_t hdrBox, FBO_t *ldrFbo, ivec4_t ldrBox, int autoExposure)
|
|
{
|
|
ivec4_t srcBox, dstBox;
|
|
vec4_t color;
|
|
static int lastFrameCount = 0;
|
|
|
|
if (autoExposure)
|
|
{
|
|
if (lastFrameCount == 0 || tr.frameCount < lastFrameCount || tr.frameCount - lastFrameCount > 5)
|
|
{
|
|
// determine average log luminance
|
|
FBO_t *srcFbo, *dstFbo, *tmp;
|
|
int size = 256;
|
|
|
|
lastFrameCount = tr.frameCount;
|
|
|
|
VectorSet4(dstBox, 0, 0, size, size);
|
|
|
|
FBO_Blit(hdrFbo, hdrBox, NULL, tr.textureScratchFbo[0], dstBox, &tr.calclevels4xShader[0], NULL, 0);
|
|
|
|
srcFbo = tr.textureScratchFbo[0];
|
|
dstFbo = tr.textureScratchFbo[1];
|
|
|
|
// downscale to 1x1 texture
|
|
while (size > 1)
|
|
{
|
|
VectorSet4(srcBox, 0, 0, size, size);
|
|
//size >>= 2;
|
|
size >>= 1;
|
|
VectorSet4(dstBox, 0, 0, size, size);
|
|
|
|
if (size == 1)
|
|
dstFbo = tr.targetLevelsFbo;
|
|
|
|
//FBO_Blit(targetFbo, srcBox, NULL, tr.textureScratchFbo[nextScratch], dstBox, &tr.calclevels4xShader[1], NULL, 0);
|
|
FBO_FastBlit(srcFbo, srcBox, dstFbo, dstBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
tmp = srcFbo;
|
|
srcFbo = dstFbo;
|
|
dstFbo = tmp;
|
|
}
|
|
}
|
|
|
|
// blend with old log luminance for gradual change
|
|
VectorSet4(srcBox, 0, 0, 0, 0);
|
|
|
|
color[0] =
|
|
color[1] =
|
|
color[2] = 1.0f;
|
|
if (glRefConfig.textureFloat)
|
|
color[3] = 0.03f;
|
|
else
|
|
color[3] = 0.1f;
|
|
|
|
FBO_Blit(tr.targetLevelsFbo, srcBox, NULL, tr.calcLevelsFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
// tonemap
|
|
color[0] =
|
|
color[1] =
|
|
color[2] = pow(2, r_cameraExposure->value - autoExposure); //exp2(r_cameraExposure->value);
|
|
color[3] = 1.0f;
|
|
|
|
if (autoExposure)
|
|
GL_BindToTMU(tr.calcLevelsImage, TB_LEVELSMAP);
|
|
else
|
|
GL_BindToTMU(tr.fixedLevelsImage, TB_LEVELSMAP);
|
|
|
|
FBO_Blit(hdrFbo, hdrBox, NULL, ldrFbo, ldrBox, &tr.tonemapShader, color, 0);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
RB_BokehBlur
|
|
|
|
|
|
Blurs a part of one framebuffer to another.
|
|
|
|
Framebuffers can be identical.
|
|
=============
|
|
*/
|
|
void RB_BokehBlur(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, float blur)
|
|
{
|
|
// ivec4_t srcBox, dstBox;
|
|
vec4_t color;
|
|
|
|
blur *= 10.0f;
|
|
|
|
if (blur < 0.004f)
|
|
return;
|
|
|
|
if (glRefConfig.framebufferObject)
|
|
{
|
|
// bokeh blur
|
|
if (blur > 0.0f)
|
|
{
|
|
ivec4_t quarterBox;
|
|
|
|
quarterBox[0] = 0;
|
|
quarterBox[1] = tr.quarterFbo[0]->height;
|
|
quarterBox[2] = tr.quarterFbo[0]->width;
|
|
quarterBox[3] = -tr.quarterFbo[0]->height;
|
|
|
|
// create a quarter texture
|
|
//FBO_Blit(NULL, NULL, NULL, tr.quarterFbo[0], NULL, NULL, NULL, 0);
|
|
FBO_FastBlit(src, srcBox, tr.quarterFbo[0], quarterBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
}
|
|
|
|
#ifndef HQ_BLUR
|
|
if (blur > 1.0f)
|
|
{
|
|
// create a 1/16th texture
|
|
//FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.textureScratchFbo[0], NULL, NULL, NULL, 0);
|
|
FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
}
|
|
#endif
|
|
|
|
if (blur > 0.0f && blur <= 1.0f)
|
|
{
|
|
// Crossfade original with quarter texture
|
|
VectorSet4(color, 1, 1, 1, blur);
|
|
|
|
FBO_Blit(tr.quarterFbo[0], NULL, NULL, dst, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
#ifndef HQ_BLUR
|
|
// ok blur, but can see some pixelization
|
|
else if (blur > 1.0f && blur <= 2.0f)
|
|
{
|
|
// crossfade quarter texture with 1/16th texture
|
|
FBO_Blit(tr.quarterFbo[0], NULL, NULL, dst, dstBox, NULL, NULL, 0);
|
|
|
|
VectorSet4(color, 1, 1, 1, blur - 1.0f);
|
|
|
|
FBO_Blit(tr.textureScratchFbo[0], NULL, NULL, dst, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
else if (blur > 2.0f)
|
|
{
|
|
// blur 1/16th texture then replace
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
vec2_t blurTexScale;
|
|
float subblur;
|
|
|
|
subblur = ((blur - 2.0f) / 2.0f) / 3.0f * (float)(i + 1);
|
|
|
|
blurTexScale[0] =
|
|
blurTexScale[1] = subblur;
|
|
|
|
color[0] =
|
|
color[1] =
|
|
color[2] = 0.5f;
|
|
color[3] = 1.0f;
|
|
|
|
if (i != 0)
|
|
FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
|
|
else
|
|
FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, 0);
|
|
}
|
|
|
|
FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, dst, dstBox, NULL, NULL, 0);
|
|
}
|
|
#else // higher quality blur, but slower
|
|
else if (blur > 1.0f)
|
|
{
|
|
// blur quarter texture then replace
|
|
int i;
|
|
|
|
src = tr.quarterFbo[0];
|
|
dst = tr.quarterFbo[1];
|
|
|
|
VectorSet4(color, 0.5f, 0.5f, 0.5f, 1);
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
vec2_t blurTexScale;
|
|
float subblur;
|
|
|
|
subblur = (blur - 1.0f) / 2.0f * (float)(i + 1);
|
|
|
|
blurTexScale[0] =
|
|
blurTexScale[1] = subblur;
|
|
|
|
color[0] =
|
|
color[1] =
|
|
color[2] = 1.0f;
|
|
if (i != 0)
|
|
color[3] = 1.0f;
|
|
else
|
|
color[3] = 0.5f;
|
|
|
|
FBO_Blit(tr.quarterFbo[0], NULL, blurTexScale, tr.quarterFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
FBO_Blit(tr.quarterFbo[1], NULL, NULL, dst, dstBox, NULL, NULL, 0);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
static void RB_RadialBlur(FBO_t *srcFbo, FBO_t *dstFbo, int passes, float stretch, float x, float y, float w, float h, float xcenter, float ycenter, float alpha)
|
|
{
|
|
ivec4_t srcBox, dstBox;
|
|
int srcWidth, srcHeight;
|
|
vec4_t color;
|
|
const float inc = 1.f / passes;
|
|
const float mul = powf(stretch, inc);
|
|
float scale;
|
|
|
|
alpha *= inc;
|
|
VectorSet4(color, alpha, alpha, alpha, 1.0f);
|
|
|
|
srcWidth = srcFbo ? srcFbo->width : glConfig.vidWidth;
|
|
srcHeight = srcFbo ? srcFbo->height : glConfig.vidHeight;
|
|
|
|
VectorSet4(srcBox, 0, 0, srcWidth, srcHeight);
|
|
|
|
VectorSet4(dstBox, x, y, w, h);
|
|
FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, 0);
|
|
|
|
--passes;
|
|
scale = mul;
|
|
while (passes > 0)
|
|
{
|
|
float iscale = 1.f / scale;
|
|
float s0 = xcenter * (1.f - iscale);
|
|
float t0 = (1.0f - ycenter) * (1.f - iscale);
|
|
|
|
srcBox[0] = s0 * srcWidth;
|
|
srcBox[1] = t0 * srcHeight;
|
|
srcBox[2] = iscale * srcWidth;
|
|
srcBox[3] = iscale * srcHeight;
|
|
|
|
FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
|
|
|
|
scale *= mul;
|
|
--passes;
|
|
}
|
|
}
|
|
|
|
|
|
static qboolean RB_UpdateSunFlareVis(void)
|
|
{
|
|
GLuint sampleCount = 0;
|
|
if (!glRefConfig.occlusionQuery)
|
|
return qtrue;
|
|
|
|
tr.sunFlareQueryIndex ^= 1;
|
|
if (!tr.sunFlareQueryActive[tr.sunFlareQueryIndex])
|
|
return qtrue;
|
|
|
|
/* debug code */
|
|
if (0)
|
|
{
|
|
int iter;
|
|
for (iter=0 ; ; ++iter)
|
|
{
|
|
GLint available = 0;
|
|
qglGetQueryObjectiv(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_AVAILABLE, &available);
|
|
if (available)
|
|
break;
|
|
}
|
|
|
|
ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter);
|
|
}
|
|
|
|
qglGetQueryObjectuiv(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT, &sampleCount);
|
|
return sampleCount > 0;
|
|
}
|
|
|
|
void RB_SunRays(FBO_t *srcFbo, ivec4_t srcBox, FBO_t *dstFbo, ivec4_t dstBox)
|
|
{
|
|
vec4_t color;
|
|
float dot;
|
|
const float cutoff = 0.25f;
|
|
qboolean colorize = qtrue;
|
|
|
|
// float w, h, w2, h2;
|
|
mat4_t mvp;
|
|
vec4_t pos, hpos;
|
|
|
|
dot = DotProduct(tr.sunDirection, backEnd.viewParms.or.axis[0]);
|
|
if (dot < cutoff)
|
|
return;
|
|
|
|
if (!RB_UpdateSunFlareVis())
|
|
return;
|
|
|
|
// From RB_DrawSun()
|
|
{
|
|
float dist;
|
|
mat4_t trans, model;
|
|
|
|
Mat4Translation( backEnd.viewParms.or.origin, trans );
|
|
Mat4Multiply( backEnd.viewParms.world.modelMatrix, trans, model );
|
|
Mat4Multiply(backEnd.viewParms.projectionMatrix, model, mvp);
|
|
|
|
dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3)
|
|
|
|
VectorScale( tr.sunDirection, dist, pos );
|
|
}
|
|
|
|
// project sun point
|
|
//Mat4Multiply(backEnd.viewParms.projectionMatrix, backEnd.viewParms.world.modelMatrix, mvp);
|
|
Mat4Transform(mvp, pos, hpos);
|
|
|
|
// transform to UV coords
|
|
hpos[3] = 0.5f / hpos[3];
|
|
|
|
pos[0] = 0.5f + hpos[0] * hpos[3];
|
|
pos[1] = 0.5f + hpos[1] * hpos[3];
|
|
|
|
// initialize quarter buffers
|
|
{
|
|
float mul = 1.f;
|
|
ivec4_t rayBox, quarterBox;
|
|
int srcWidth = srcFbo ? srcFbo->width : glConfig.vidWidth;
|
|
int srcHeight = srcFbo ? srcFbo->height : glConfig.vidHeight;
|
|
|
|
VectorSet4(color, mul, mul, mul, 1);
|
|
|
|
rayBox[0] = srcBox[0] * tr.sunRaysFbo->width / srcWidth;
|
|
rayBox[1] = srcBox[1] * tr.sunRaysFbo->height / srcHeight;
|
|
rayBox[2] = srcBox[2] * tr.sunRaysFbo->width / srcWidth;
|
|
rayBox[3] = srcBox[3] * tr.sunRaysFbo->height / srcHeight;
|
|
|
|
quarterBox[0] = 0;
|
|
quarterBox[1] = tr.quarterFbo[0]->height;
|
|
quarterBox[2] = tr.quarterFbo[0]->width;
|
|
quarterBox[3] = -tr.quarterFbo[0]->height;
|
|
|
|
// first, downsample the framebuffer
|
|
if (colorize)
|
|
{
|
|
FBO_FastBlit(srcFbo, srcBox, tr.quarterFbo[0], quarterBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
FBO_Blit(tr.sunRaysFbo, rayBox, NULL, tr.quarterFbo[0], quarterBox, NULL, color, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO);
|
|
}
|
|
else
|
|
{
|
|
FBO_FastBlit(tr.sunRaysFbo, rayBox, tr.quarterFbo[0], quarterBox, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
}
|
|
}
|
|
|
|
// radial blur passes, ping-ponging between the two quarter-size buffers
|
|
{
|
|
const float stretch_add = 2.f/3.f;
|
|
float stretch = 1.f + stretch_add;
|
|
int i;
|
|
for (i=0; i<2; ++i)
|
|
{
|
|
RB_RadialBlur(tr.quarterFbo[i&1], tr.quarterFbo[(~i) & 1], 5, stretch, 0.f, 0.f, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height, pos[0], pos[1], 1.125f);
|
|
stretch += stretch_add;
|
|
}
|
|
}
|
|
|
|
// add result back on top of the main buffer
|
|
{
|
|
float mul = 1.f;
|
|
|
|
VectorSet4(color, mul, mul, mul, 1);
|
|
|
|
FBO_Blit(tr.quarterFbo[0], NULL, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
|
|
}
|
|
}
|
|
|
|
static void RB_BlurAxis(FBO_t *srcFbo, FBO_t *dstFbo, float strength, qboolean horizontal)
|
|
{
|
|
float dx, dy;
|
|
float xmul, ymul;
|
|
float weights[3] = {
|
|
0.227027027f,
|
|
0.316216216f,
|
|
0.070270270f,
|
|
};
|
|
float offsets[3] = {
|
|
0.f,
|
|
1.3846153846f,
|
|
3.2307692308f,
|
|
};
|
|
|
|
xmul = horizontal;
|
|
ymul = 1.f - xmul;
|
|
|
|
xmul *= strength;
|
|
ymul *= strength;
|
|
|
|
{
|
|
ivec4_t srcBox, dstBox;
|
|
vec4_t color;
|
|
|
|
VectorSet4(color, weights[0], weights[0], weights[0], 1.0f);
|
|
VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height);
|
|
VectorSet4(dstBox, 0, 0, dstFbo->width, dstFbo->height);
|
|
FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, 0);
|
|
|
|
VectorSet4(color, weights[1], weights[1], weights[1], 1.0f);
|
|
dx = offsets[1] * xmul;
|
|
dy = offsets[1] * ymul;
|
|
VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height);
|
|
FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
|
|
VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height);
|
|
FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
|
|
|
|
VectorSet4(color, weights[2], weights[2], weights[2], 1.0f);
|
|
dx = offsets[2] * xmul;
|
|
dy = offsets[2] * ymul;
|
|
VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height);
|
|
FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
|
|
VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height);
|
|
FBO_Blit(srcFbo, srcBox, NULL, dstFbo, dstBox, NULL, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE);
|
|
}
|
|
}
|
|
|
|
static void RB_HBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength)
|
|
{
|
|
RB_BlurAxis(srcFbo, dstFbo, strength, qtrue);
|
|
}
|
|
|
|
static void RB_VBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength)
|
|
{
|
|
RB_BlurAxis(srcFbo, dstFbo, strength, qfalse);
|
|
}
|
|
|
|
void RB_GaussianBlur(float blur)
|
|
{
|
|
//float mul = 1.f;
|
|
float factor = Com_Clamp(0.f, 1.f, blur);
|
|
|
|
if (factor <= 0.f)
|
|
return;
|
|
|
|
{
|
|
ivec4_t srcBox, dstBox;
|
|
vec4_t color;
|
|
|
|
VectorSet4(color, 1, 1, 1, 1);
|
|
|
|
// first, downsample the framebuffer
|
|
FBO_FastBlit(NULL, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
|
|
|
// set the alpha channel
|
|
qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
|
FBO_BlitFromTexture(tr.whiteImage, NULL, NULL, tr.textureScratchFbo[0], NULL, NULL, color, GLS_DEPTHTEST_DISABLE);
|
|
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
// blur the tiny buffer horizontally and vertically
|
|
RB_HBlur(tr.textureScratchFbo[0], tr.textureScratchFbo[1], factor);
|
|
RB_VBlur(tr.textureScratchFbo[1], tr.textureScratchFbo[0], factor);
|
|
|
|
// finally, merge back to framebuffer
|
|
VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height);
|
|
VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight);
|
|
color[3] = factor;
|
|
FBO_Blit(tr.textureScratchFbo[0], srcBox, NULL, NULL, dstBox, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
}
|