libretro-tyrquake/NQ/gl_rmisc.c
Tyrann e24ff3553f [PATCH] Use XF86 "Vidmode" gamma extension
This patch sets things up to make use of the XFree86 gamma manipulation
functions.

One issue is that the OpenGL blending substitute we set up is not really the
same as gamma correction because it only brightens pixels according to their
current values. This means black areas are not brightened using this method
(which I think is a good thing), but gamma correction adds "white" to every
area of the screen.

At the moment I've set up the "manual" OpenGL version as a fallback in case
the system gamma functions are not working or not available. I've also set
things up such that the system gamma is not used unless we are in fullscreen
mode since it affects the whole display.

The cvar _gl_allowgammafallback controls whether the fallback is used or not,
but I don't currently have a way to force it to use the "fallback" even if the
other gamma settings work. Most likely I'll separate out the two functions
into gamma and "intensity" similar to Quake 2 and later games.

Signed-off-by: Tyrann <tyrann@disenchant.net>
2006-09-10 19:06:07 +09:30

473 lines
12 KiB
C

/*
Copyright (C) 1996-1997 Id Software, Inc.
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_misc.c
#include "cmd.h"
#include "console.h"
#include "glquake.h"
#include "protocol.h"
#include "quakedef.h"
#include "sys.h"
// FIXME - should only be needed in r_part.c or here, not both.
int particletexture;
/*
==================
R_InitTextures
==================
*/
void
R_InitTextures(void)
{
int x, y, m;
byte *dest;
// create a simple checkerboard texture for the default
r_notexture_mip =
Hunk_AllocName(sizeof(texture_t) + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2,
"notexture");
r_notexture_mip->width = r_notexture_mip->height = 16;
r_notexture_mip->offsets[0] = sizeof(texture_t);
r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16 * 16;
r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8 * 8;
r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4 * 4;
for (m = 0; m < 4; m++) {
dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
for (y = 0; y < (16 >> m); y++) {
for (x = 0; x < (16 >> m); x++) {
if ((y < (8 >> m)) ^ (x < (8 >> m)))
*dest++ = 0;
else
*dest++ = 0xff;
}
}
}
}
static byte dottexture[8][8] = {
{0, 1, 1, 0, 0, 0, 0, 0},
{1, 1, 1, 1, 0, 0, 0, 0},
{1, 1, 1, 1, 0, 0, 0, 0},
{0, 1, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
};
static void
R_InitParticleTexture(void)
{
int x, y;
byte data[8][8][4];
//
// particle texture
//
particletexture = texture_extension_number++;
GL_Bind(particletexture);
for (x = 0; x < 8; x++) {
for (y = 0; y < 8; y++) {
data[y][x][0] = 255;
data[y][x][1] = 255;
data[y][x][2] = 255;
data[y][x][3] = dottexture[x][y] * 255;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA,
GL_UNSIGNED_BYTE, data);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
/*
===============
R_Envmap_f
Grab six views for environment mapping tests
===============
*/
static void
R_Envmap_f(void)
{
byte buffer[256 * 256 * 4];
glDrawBuffer(GL_FRONT);
glReadBuffer(GL_FRONT);
envmap = true;
r_refdef.vrect.x = 0;
r_refdef.vrect.y = 0;
r_refdef.vrect.width = 256;
r_refdef.vrect.height = 256;
r_refdef.viewangles[0] = 0;
r_refdef.viewangles[1] = 0;
r_refdef.viewangles[2] = 0;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env0.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[1] = 90;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env1.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[1] = 180;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env2.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[1] = 270;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env3.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[0] = -90;
r_refdef.viewangles[1] = 0;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env4.rgb", buffer, sizeof(buffer));
r_refdef.viewangles[0] = 90;
r_refdef.viewangles[1] = 0;
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
R_RenderView();
glReadPixels(0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
COM_WriteFile("env5.rgb", buffer, sizeof(buffer));
envmap = false;
glDrawBuffer(GL_BACK);
glReadBuffer(GL_BACK);
GL_EndRendering();
}
// FIXME - locate somewhere else?
cvar_t r_lockpvs = { "r_lockpvs", "0" };
cvar_t r_lockfrustum = { "r_lockfrustum", "0" };
cvar_t r_drawflat = { "r_drawflat", "0" };
/*
===============
R_Init
===============
*/
void
R_Init(void)
{
Cmd_AddCommand("envmap", R_Envmap_f);
Cmd_AddCommand("pointfile", R_ReadPointFile_f);
Cmd_AddCommand("timerefresh", R_TimeRefresh_f);
Cvar_RegisterVariable(&r_speeds);
Cvar_RegisterVariable(&r_fullbright);
Cvar_RegisterVariable(&r_drawentities);
Cvar_RegisterVariable(&r_drawviewmodel);
Cvar_RegisterVariable(&r_drawflat);
Cvar_RegisterVariable(&r_lockpvs);
Cvar_RegisterVariable(&r_lockfrustum);
Cvar_RegisterVariable(&r_norefresh);
Cvar_RegisterVariable(&r_lightmap);
Cvar_RegisterVariable(&r_shadows);
Cvar_RegisterVariable(&r_mirroralpha);
Cvar_RegisterVariable(&r_wateralpha);
Cvar_RegisterVariable(&r_dynamic);
Cvar_RegisterVariable(&r_novis);
Cvar_RegisterVariable(&r_waterwarp);
Cvar_RegisterVariable(&gl_finish);
Cvar_RegisterVariable(&gl_clear);
Cvar_RegisterVariable(&gl_texsort);
Cvar_RegisterVariable(&_gl_lightmap_sort);
Cvar_RegisterVariable(&_gl_sky_mtex);
Cvar_RegisterVariable(&_gl_allowgammafallback);
Cvar_RegisterVariable(&_gl_drawhull);
Cvar_RegisterVariable(&gl_cull);
Cvar_RegisterVariable(&gl_smoothmodels);
Cvar_RegisterVariable(&gl_affinemodels);
Cvar_RegisterVariable(&gl_polyblend);
Cvar_RegisterVariable(&gl_flashblend);
Cvar_RegisterVariable(&gl_playermip);
Cvar_RegisterVariable(&gl_nocolors);
Cvar_RegisterVariable(&gl_keeptjunctions);
Cvar_RegisterVariable(&gl_reporttjunctions);
Cvar_RegisterVariable(&gl_doubleeyes);
if (gl_mtexable)
Cvar_SetValue("gl_texsort", 0.0);
R_InitBubble();
R_InitParticles();
R_InitParticleTexture();
playertextures = texture_extension_number;
texture_extension_number += MAX_CLIENTS;
}
/*
===============
R_TranslatePlayerSkin
Translates a skin texture by the per-player color lookup
===============
*/
void
R_TranslatePlayerSkin(int playernum)
{
int top, bottom;
byte translate[256];
unsigned translate32[256];
int i, j, s;
model_t *model;
aliashdr_t *paliashdr;
byte *original;
unsigned pixels[512 * 256], *out;
unsigned scaled_width, scaled_height;
int inwidth, inheight;
byte *inrow;
unsigned frac, fracstep;
GL_DisableMultitexture();
top = cl.scores[playernum].colors & 0xf0;
bottom = (cl.scores[playernum].colors & 15) << 4;
for (i = 0; i < 256; i++)
translate[i] = i;
for (i = 0; i < 16; i++) {
if (top < 128) // the artists made some backwards ranges. sigh.
translate[TOP_RANGE + i] = top + i;
else
translate[TOP_RANGE + i] = top + 15 - i;
if (bottom < 128)
translate[BOTTOM_RANGE + i] = bottom + i;
else
translate[BOTTOM_RANGE + i] = bottom + 15 - i;
}
//
// locate the original skin pixels
//
currententity = &cl_entities[1 + playernum];
model = currententity->model;
if (!model)
return; // player doesn't have a model yet
if (model->type != mod_alias)
return; // only translate skins on alias models
paliashdr = (aliashdr_t *)Mod_Extradata(model);
s = paliashdr->skinwidth * paliashdr->skinheight;
if (currententity->skinnum < 0
|| currententity->skinnum >= paliashdr->numskins) {
Con_Printf("(%d): Invalid player skin #%d\n", playernum,
currententity->skinnum);
original = (byte *)paliashdr + paliashdr->texels[0];
} else
original =
(byte *)paliashdr + paliashdr->texels[currententity->skinnum];
if (s & 3)
Sys_Error("%s: s&3", __func__);
inwidth = paliashdr->skinwidth;
inheight = paliashdr->skinheight;
// because this happens during gameplay, do it fast
// instead of sending it through gl_upload 8
GL_Bind(playertextures + playernum);
#if 0
byte translated[320 * 200];
for (i = 0; i < s; i += 4) {
translated[i] = translate[original[i]];
translated[i + 1] = translate[original[i + 1]];
translated[i + 2] = translate[original[i + 2]];
translated[i + 3] = translate[original[i + 3]];
}
// don't mipmap these, because it takes too long
GL_Upload8(translated, paliashdr->skinwidth, paliashdr->skinheight,
false, false, true);
#else
// allow users to crunch sizes down
scaled_width = 512 >> (int)gl_playermip.value;
scaled_height = 256 >> (int)gl_playermip.value;
// make sure not still too big
scaled_width = gl_max_size.value < scaled_width
? gl_max_size.value : scaled_width;
scaled_height = gl_max_size.value < scaled_height
? gl_max_size.value : scaled_height;
if (VID_Is8bit()) { // 8bit texture upload
byte *out2;
out2 = (byte *)pixels;
memset(pixels, 0, sizeof(pixels));
fracstep = inwidth * 0x10000 / scaled_width;
for (i = 0; i < scaled_height; i++, out2 += scaled_width) {
inrow = original + inwidth * (i * inheight / scaled_height);
frac = fracstep >> 1;
for (j = 0; j < scaled_width; j += 4) {
out2[j] = translate[inrow[frac >> 16]];
frac += fracstep;
out2[j + 1] = translate[inrow[frac >> 16]];
frac += fracstep;
out2[j + 2] = translate[inrow[frac >> 16]];
frac += fracstep;
out2[j + 3] = translate[inrow[frac >> 16]];
frac += fracstep;
}
}
GL_Upload8_EXT((byte *)pixels, scaled_width, scaled_height, false,
false);
return;
}
for (i = 0; i < 256; i++)
translate32[i] = d_8to24table[translate[i]];
out = pixels;
fracstep = inwidth * 0x10000 / scaled_width;
for (i = 0; i < scaled_height; i++, out += scaled_width) {
inrow = original + inwidth * (i * inheight / scaled_height);
frac = fracstep >> 1;
for (j = 0; j < scaled_width; j += 4) {
out[j] = translate32[inrow[frac >> 16]];
frac += fracstep;
out[j + 1] = translate32[inrow[frac >> 16]];
frac += fracstep;
out[j + 2] = translate32[inrow[frac >> 16]];
frac += fracstep;
out[j + 3] = translate32[inrow[frac >> 16]];
frac += fracstep;
}
}
glTexImage2D(GL_TEXTURE_2D, 0, gl_solid_format, scaled_width,
scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#endif
}
/*
===============
R_NewMap
===============
*/
void
R_NewMap(void)
{
int i;
for (i = 0; i < 256; i++)
d_lightstylevalue[i] = 264; // normal light value
memset(&r_worldentity, 0, sizeof(r_worldentity));
r_worldentity.model = cl.worldmodel;
// clear out efrags in case the level hasn't been reloaded
// FIXME: is this one short?
for (i = 0; i < cl.worldmodel->numleafs; i++)
cl.worldmodel->leafs[i].efrags = NULL;
r_viewleaf = NULL;
R_ClearParticles();
GL_BuildLightmaps();
// identify sky texture
skytexturenum = -1;
mirrortexturenum = -1;
for (i = 0; i < cl.worldmodel->numtextures; i++) {
if (!cl.worldmodel->textures[i])
continue;
if (!strncmp(cl.worldmodel->textures[i]->name, "sky", 3))
skytexturenum = i;
if (!strncmp(cl.worldmodel->textures[i]->name, "window02_1", 10))
mirrortexturenum = i;
cl.worldmodel->textures[i]->texturechain = NULL;
}
}
/*
====================
R_TimeRefresh_f
For program optimization
====================
*/
void
R_TimeRefresh_f(void)
{
int i;
float start, stop, time;
glDrawBuffer(GL_FRONT);
glFinish();
start = Sys_DoubleTime();
for (i = 0; i < 128; i++) {
r_refdef.viewangles[1] = i / 128.0 * 360.0;
R_RenderView();
}
glFinish();
stop = Sys_DoubleTime();
time = stop - start;
Con_Printf("%f seconds (%f fps)\n", time, 128 / time);
glDrawBuffer(GL_BACK);
GL_EndRendering();
}
void
D_FlushCaches(void)
{
}