Merge pull request #1124 from protocultor/gl1_buffer

Grouped draw calls for improved performance with GL1
This commit is contained in:
Yamagi 2024-07-20 09:24:23 +02:00 committed by GitHub
commit eef7db91d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 739 additions and 612 deletions

View file

@ -590,6 +590,7 @@ set(GL1-Source
${REF_SRC_DIR}/gl1/gl1_surf.c ${REF_SRC_DIR}/gl1/gl1_surf.c
${REF_SRC_DIR}/gl1/gl1_warp.c ${REF_SRC_DIR}/gl1/gl1_warp.c
${REF_SRC_DIR}/gl1/gl1_sdl.c ${REF_SRC_DIR}/gl1/gl1_sdl.c
${REF_SRC_DIR}/gl1/gl1_buffer.c
${REF_SRC_DIR}/files/models.c ${REF_SRC_DIR}/files/models.c
${REF_SRC_DIR}/files/pcx.c ${REF_SRC_DIR}/files/pcx.c
${REF_SRC_DIR}/files/stb.c ${REF_SRC_DIR}/files/stb.c

View file

@ -959,6 +959,7 @@ REFGL1_OBJS_ := \
src/client/refresh/gl1/gl1_surf.o \ src/client/refresh/gl1/gl1_surf.o \
src/client/refresh/gl1/gl1_warp.o \ src/client/refresh/gl1/gl1_warp.o \
src/client/refresh/gl1/gl1_sdl.o \ src/client/refresh/gl1/gl1_sdl.o \
src/client/refresh/gl1/gl1_buffer.o \
src/client/refresh/files/surf.o \ src/client/refresh/files/surf.o \
src/client/refresh/files/models.o \ src/client/refresh/files/models.o \
src/client/refresh/files/pcx.o \ src/client/refresh/files/pcx.o \

View file

@ -471,10 +471,6 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable`
## Graphics (OpenGL 1.4 only) ## Graphics (OpenGL 1.4 only)
* **gl1_biglightmaps**: Enables lightmaps and scrap to use a bigger
texture size, which means fewer texture switches, improving
performance. Default is `1` (enabled). Requires a `vid_restart`.
* **gl1_intensity**: Sets the color intensity. Must be a floating point * **gl1_intensity**: Sets the color intensity. Must be a floating point
value, at least `1.0` - default is `2.0`. Applied when textures are value, at least `1.0` - default is `2.0`. Applied when textures are
loaded, so it needs a `vid_restart`. loaded, so it needs a `vid_restart`.

View file

@ -0,0 +1,449 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2024 Jaime Moreira
*
* 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.
*
* =======================================================================
*
* Drawing buffer: sort of a "Q3A shader" handler, allows to join multiple
* draw calls into one, by grouping those which share the same
* characteristics (mostly the same texture).
*
* =======================================================================
*/
#include "header/local.h"
#define MAX_VERTICES 16384
#define MAX_INDICES (MAX_VERTICES * 4)
typedef struct // 832k aprox.
{
buffered_draw_t type;
GLfloat
vtx[MAX_VERTICES * 3], // vertexes
tex[MAX_TEXTURE_UNITS][MAX_VERTICES * 2], // texture coords
clr[MAX_VERTICES * 4]; // color components
GLushort
idx[MAX_INDICES], // indices
vtx_ptr, idx_ptr; // pointers for array positions
int texture[MAX_TEXTURE_UNITS];
int flags; // entity flags
float alpha;
} glbuffer_t;
glbuffer_t gl_buf;
GLuint vt, tx, cl; // indices for arrays in gl_buf
extern void R_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
void
R_ApplyGLBuffer(void)
{
// Properties of batched draws here
GLint vtx_size;
qboolean texture, mtex, alpha, color, alias, texenv_set;
float fovy, dist;
if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0)
{
return;
}
// defaults for drawing (mostly buf_singletex features)
vtx_size = 3;
texture = true;
mtex = alpha = color = alias = texenv_set = false;
// choosing features by type
switch (gl_buf.type)
{
case buf_2d:
vtx_size = 2;
break;
case buf_mtex:
mtex = true;
break;
case buf_alpha:
alpha = true;
break;
case buf_alias:
alias = color = true;
break;
case buf_flash:
color = true;
case buf_shadow:
texture = false;
break;
default:
break;
}
R_EnableMultitexture(mtex);
if (alias)
{
if (gl_buf.flags & RF_DEPTHHACK)
{
// hack the depth range to prevent view model from poking into walls
glDepthRange(gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin));
}
if (gl_buf.flags & RF_WEAPONMODEL)
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
if (gl_lefthand->value == 1.0f)
{
glScalef(-1, 1, 1);
}
fovy = (r_gunfov->value < 0) ? r_newrefdef.fov_y : r_gunfov->value;
dist = (r_farsee->value == 0) ? 4096.0f : 8192.0f;
R_MYgluPerspective(fovy, (float)r_newrefdef.width / r_newrefdef.height, 4, dist);
glMatrixMode(GL_MODELVIEW);
if (gl_lefthand->value == 1.0f)
{
glCullFace(GL_BACK);
}
}
glShadeModel(GL_SMOOTH);
R_TexEnv(GL_MODULATE);
if (gl_buf.flags & RF_TRANSLUCENT)
{
glEnable(GL_BLEND);
}
if (gl_buf.flags & (RF_SHELL_RED | RF_SHELL_GREEN |
RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM))
{
texture = false;
glDisable(GL_TEXTURE_2D);
}
}
if (alpha)
{
// the textures are prescaled up for a better
// lighting range, so scale it back down
glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity,
gl_state.inverse_intensity, gl_buf.alpha);
}
else if (gl_buf.flags & SURF_DRAWTURB)
{
texenv_set = true;
// This is a hack ontop of a hack. Warping surfaces like those generated
// by R_EmitWaterPolys() don't have a lightmap. Original Quake II therefore
// negated the global intensity on those surfaces, because otherwise they
// would show up much too bright. When we implemented overbright bits this
// hack modified the global GL state in an incompatible way. So implement
// a new hack, based on overbright bits... Depending on the value set to
// gl1_overbrightbits the result is different:
// 0: Old behaviour.
// 1: No overbright bits on the global scene but correct lighting on
// warping surfaces.
// 2,4: Overbright bits on the global scene but not on warping surfaces.
// They oversaturate otherwise.
if (gl1_overbrightbits->value)
{
R_TexEnv(GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1);
}
else
{
R_TexEnv(GL_MODULATE);
glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity,
gl_state.inverse_intensity, 1.0f);
}
}
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer (vtx_size, GL_FLOAT, 0, gl_buf.vtx);
if (texture)
{
if (mtex)
{
// TMU 1: Lightmap texture
R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + gl_buf.texture[1]);
if (gl1_overbrightbits->value)
{
R_TexEnv(GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value);
}
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, gl_buf.tex[1]);
// TMU 0: Color texture
R_MBind(GL_TEXTURE0, gl_buf.texture[0]);
}
else
{
R_Bind(gl_buf.texture[0]);
}
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, gl_buf.tex[0]);
}
if (color)
{
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, gl_buf.clr);
}
// All set, we can finally draw
glDrawElements(GL_TRIANGLES, gl_buf.idx_ptr, GL_UNSIGNED_SHORT, gl_buf.idx);
// ... and now, turn back everything as it was
if (color)
{
glDisableClientState(GL_COLOR_ARRAY);
}
if (texture)
{
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
}
glDisableClientState( GL_VERTEX_ARRAY );
if (texenv_set)
{
R_TexEnv(GL_REPLACE);
}
if (alias)
{
if (gl_buf.flags & (RF_SHELL_RED | RF_SHELL_GREEN |
RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM))
{
glEnable(GL_TEXTURE_2D);
}
if (gl_buf.flags & RF_TRANSLUCENT)
{
glDisable(GL_BLEND);
}
R_TexEnv(GL_REPLACE);
glShadeModel(GL_FLAT);
if (gl_buf.flags & RF_WEAPONMODEL)
{
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
if (gl_lefthand->value == 1.0F)
{
glCullFace(GL_FRONT);
}
}
if (gl_buf.flags & RF_DEPTHHACK)
{
glDepthRange(gldepthmin, gldepthmax);
}
}
gl_buf.vtx_ptr = gl_buf.idx_ptr = 0;
}
void
R_UpdateGLBuffer(buffered_draw_t type, int colortex, int lighttex, int flags, float alpha)
{
if ( gl_buf.type != type || gl_buf.texture[0] != colortex ||
(gl_config.multitexture && type == buf_mtex && gl_buf.texture[1] != lighttex) ||
((type == buf_singletex || type == buf_alias) && gl_buf.flags != flags) ||
(type == buf_alpha && gl_buf.alpha != alpha))
{
R_ApplyGLBuffer();
gl_buf.type = type;
gl_buf.texture[0] = colortex;
gl_buf.texture[1] = lighttex;
gl_buf.flags = flags;
gl_buf.alpha = alpha;
}
}
void
R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy,
GLfloat ul_tx, GLfloat ul_ty, GLfloat dr_tx, GLfloat dr_ty)
{
static const GLushort idx_max = MAX_INDICES - 7;
static const GLushort vtx_max = MAX_VERTICES - 5;
unsigned int i;
if (gl_buf.idx_ptr > idx_max || gl_buf.vtx_ptr > vtx_max)
{
R_ApplyGLBuffer();
}
i = gl_buf.vtx_ptr * 2; // vertex index
// "Quad" = 2-triangle GL_TRIANGLE_FAN
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+1;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+2;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+2;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+3;
// up left corner coords
gl_buf.vtx[i] = ul_vx;
gl_buf.vtx[i+1] = ul_vy;
// up right
gl_buf.vtx[i+2] = dr_vx;
gl_buf.vtx[i+3] = ul_vy;
// down right
gl_buf.vtx[i+4] = dr_vx;
gl_buf.vtx[i+5] = dr_vy;
// and finally, down left
gl_buf.vtx[i+6] = ul_vx;
gl_buf.vtx[i+7] = dr_vy;
gl_buf.tex[0][i] = ul_tx;
gl_buf.tex[0][i+1] = ul_ty;
gl_buf.tex[0][i+2] = dr_tx;
gl_buf.tex[0][i+3] = ul_ty;
gl_buf.tex[0][i+4] = dr_tx;
gl_buf.tex[0][i+5] = dr_ty;
gl_buf.tex[0][i+6] = ul_tx;
gl_buf.tex[0][i+7] = dr_ty;
gl_buf.vtx_ptr += 4;
}
/*
* Set up indices with the proper shape for the next buffered vertices
*/
void
R_SetBufferIndices(GLenum type, GLuint vertices_num)
{
int i;
if ( gl_buf.vtx_ptr + vertices_num >= MAX_VERTICES ||
gl_buf.idx_ptr + ( (vertices_num - 2) * 3 ) >= MAX_INDICES )
{
R_ApplyGLBuffer();
}
switch (type)
{
case GL_TRIANGLE_FAN:
for (i = 0; i < vertices_num-2; i++)
{
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+1;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+2;
}
break;
case GL_TRIANGLE_STRIP:
for (i = 0; i < vertices_num-2; i++)
{
if (i % 2 == 0)
{
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+1;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+2;
}
else // backwards order
{
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+2;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+1;
gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i;
}
}
break;
default:
R_Printf(PRINT_DEVELOPER, "R_SetBufferIndices: no such type %d\n", type);
return;
}
// These affect the functions that follow in this file
vt = gl_buf.vtx_ptr * 3; // vertex index
tx = gl_buf.vtx_ptr * 2; // texcoord index
cl = gl_buf.vtx_ptr * 4; // color index
// R_BufferVertex() must be called as many times as vertices_num
gl_buf.vtx_ptr += vertices_num;
}
/*
* Adds a single vertex to buffer
*/
void
R_BufferVertex(GLfloat x, GLfloat y, GLfloat z)
{
gl_buf.vtx[vt++] = x;
gl_buf.vtx[vt++] = y;
gl_buf.vtx[vt++] = z;
}
/*
* Adds texture coordinates for color texture (no lightmap coords)
*/
void
R_BufferSingleTex(GLfloat s, GLfloat t)
{
// tx should be set before this is called, by R_SetBufferIndices
gl_buf.tex[0][tx++] = s;
gl_buf.tex[0][tx++] = t;
}
/*
* Adds texture coordinates for color and lightmap
*/
void
R_BufferMultiTex(GLfloat cs, GLfloat ct, GLfloat ls, GLfloat lt)
{
gl_buf.tex[0][tx] = cs;
gl_buf.tex[0][tx+1] = ct;
gl_buf.tex[1][tx] = ls;
gl_buf.tex[1][tx+1] = lt;
tx += 2;
}
/*
* Adds color components of vertex
*/
void
R_BufferColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
gl_buf.clr[cl++] = r;
gl_buf.clr[cl++] = g;
gl_buf.clr[cl++] = b;
gl_buf.clr[cl++] = a;
}

View file

@ -77,31 +77,10 @@ RDraw_CharScaled(int x, int y, int num, float scale)
scaledSize = 8*scale; scaledSize = 8*scale;
R_Bind(draw_chars->texnum); R_UpdateGLBuffer(buf_2d, draw_chars->texnum, 0, 0, 1);
GLfloat vtx[] = { R_Buffer2DQuad(x, y, x + scaledSize, y + scaledSize,
x, y, fcol, frow, fcol + size, frow + size);
x + scaledSize, y,
x + scaledSize, y + scaledSize,
x, y + scaledSize
};
GLfloat tex[] = {
fcol, frow,
fcol + size, frow,
fcol + size, frow + size,
fcol, frow + size
};
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, vtx );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
} }
image_t * image_t *
@ -190,6 +169,14 @@ RDraw_PicScaled(int x, int y, char *pic, float factor)
Scrap_Upload(); Scrap_Upload();
} }
if (gl->texnum == TEXNUM_SCRAPS)
{
R_UpdateGLBuffer(buf_2d, TEXNUM_SCRAPS, 0, 0, 1);
R_Buffer2DQuad(x, y, x + gl->width * factor, y + gl->height * factor,
gl->sl, gl->tl, gl->sh, gl->th);
return;
}
R_Bind(gl->texnum); R_Bind(gl->texnum);
GLfloat vtx[] = { GLfloat vtx[] = {
@ -235,31 +222,10 @@ RDraw_TileClear(int x, int y, int w, int h, char *pic)
return; return;
} }
R_Bind(image->texnum); R_UpdateGLBuffer(buf_2d, image->texnum, 0, 0, 1);
GLfloat vtx[] = { R_Buffer2DQuad(x, y, x + w, y + h, x / 64.0, y / 64.0,
x, y, ( x + w ) / 64.0, ( y + h ) / 64.0);
x + w, y,
x + w, y + h,
x, y + h
};
GLfloat tex[] = {
x / 64.0, y / 64.0,
( x + w ) / 64.0, y / 64.0,
( x + w ) / 64.0, ( y + h ) / 64.0,
x / 64.0, ( y + h ) / 64.0
};
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, vtx );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
} }
/* /*
@ -306,6 +272,7 @@ RDraw_Fill(int x, int y, int w, int h, int c)
void void
RDraw_FadeScreen(void) RDraw_FadeScreen(void)
{ {
R_ApplyGLBuffer(); // draw what needs to be hidden
glEnable(GL_BLEND); glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
glColor4f(0, 0, 0, 0.8); glColor4f(0, 0, 0, 0.8);

View file

@ -147,21 +147,12 @@ R_SetTexturePalette(unsigned palette[256])
void void
R_SelectTexture(GLenum texture) R_SelectTexture(GLenum texture)
{ {
int tmu; if (!gl_config.multitexture || gl_state.currenttarget == texture)
if (!gl_config.multitexture)
{ {
return; return;
} }
tmu = texture - GL_TEXTURE0; gl_state.currenttmu = texture - GL_TEXTURE0;
if (tmu == gl_state.currenttmu)
{
return;
}
gl_state.currenttmu = tmu;
gl_state.currenttarget = texture; gl_state.currenttarget = texture;
qglActiveTexture(texture); qglActiveTexture(texture);
@ -180,7 +171,7 @@ R_TexEnv(GLenum mode)
} }
} }
void qboolean
R_Bind(int texnum) R_Bind(int texnum)
{ {
extern image_t *draw_chars; extern image_t *draw_chars;
@ -192,11 +183,12 @@ R_Bind(int texnum)
if (gl_state.currenttextures[gl_state.currenttmu] == texnum) if (gl_state.currenttextures[gl_state.currenttmu] == texnum)
{ {
return; return false;
} }
gl_state.currenttextures[gl_state.currenttmu] = texnum; gl_state.currenttextures[gl_state.currenttmu] = texnum;
glBindTexture(GL_TEXTURE_2D, texnum); glBindTexture(GL_TEXTURE_2D, texnum);
return true;
} }
void void
@ -308,7 +300,11 @@ R_TextureMode(char *string)
nolerp = true; nolerp = true;
} }
R_Bind(glt->texnum); if ( !R_Bind(glt->texnum) )
{
continue; // don't bother changing anything if texture was already set
}
if ((glt->type != it_pic) && (glt->type != it_sky)) /* mipmapped texture */ if ((glt->type != it_pic) && (glt->type != it_sky)) /* mipmapped texture */
{ {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);

View file

@ -35,53 +35,34 @@ static float s_blocklights[34 * 34 * 3];
void void
R_RenderDlight(dlight_t *light) R_RenderDlight(dlight_t *light)
{ {
const float rad = light->intensity * 0.35;
int i, j; int i, j;
float a; float vtx[3], a;
float rad;
rad = light->intensity * 0.35; R_SetBufferIndices(GL_TRIANGLE_FAN, 18);
GLfloat vtx[3*18];
GLfloat clr[4*18];
unsigned int index_vtx = 3;
unsigned int index_clr = 0;
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
clr[index_clr++] = light->color [ 0 ] * 0.2;
clr[index_clr++] = light->color [ 1 ] * 0.2;
clr[index_clr++] = light->color [ 2 ] * 0.2;
clr[index_clr++] = 1;
for ( i = 0; i < 3; i++ ) for ( i = 0; i < 3; i++ )
{ {
vtx [ i ] = light->origin [ i ] - vpn [ i ] * rad; vtx [ i ] = light->origin [ i ] - vpn [ i ] * rad;
} }
R_BufferVertex( vtx[0], vtx[1], vtx[2] );
R_BufferColor( light->color[0] * 0.2, light->color[1] * 0.2,
light->color[2] * 0.2, 1 );
for ( i = 16; i >= 0; i-- ) for ( i = 16; i >= 0; i-- )
{ {
clr[index_clr++] = 0;
clr[index_clr++] = 0;
clr[index_clr++] = 0;
clr[index_clr++] = 1;
a = i / 16.0 * M_PI * 2; a = i / 16.0 * M_PI * 2;
for ( j = 0; j < 3; j++ ) for ( j = 0; j < 3; j++ )
{ {
vtx[index_vtx++] = light->origin [ j ] + vright [ j ] * cos( a ) * rad vtx[ j ] = light->origin [ j ] + vright [ j ] * cos( a ) * rad
+ vup [ j ] * sin( a ) * rad; + vup [ j ] * sin( a ) * rad;
} }
R_BufferVertex( vtx[0], vtx[1], vtx[2] );
R_BufferColor( 0, 0, 0, 1 );
} }
glVertexPointer( 3, GL_FLOAT, 0, vtx );
glColorPointer( 4, GL_FLOAT, 0, clr );
glDrawArrays( GL_TRIANGLE_FAN, 0, 18 );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
} }
void void
@ -94,6 +75,7 @@ R_RenderDlights(void)
{ {
return; return;
} }
R_UpdateGLBuffer(buf_flash, 0, 0, 0, 1);
/* because the count hasn't advanced yet for this frame */ /* because the count hasn't advanced yet for this frame */
r_dlightframecount = r_framecount + 1; r_dlightframecount = r_framecount + 1;
@ -110,6 +92,7 @@ R_RenderDlights(void)
{ {
R_RenderDlight(l); R_RenderDlight(l);
} }
R_ApplyGLBuffer();
glColor4f(1, 1, 1, 1); glColor4f(1, 1, 1, 1);
glDisable(GL_BLEND); glDisable(GL_BLEND);

View file

@ -91,7 +91,6 @@ cvar_t *gl1_particle_square;
cvar_t *gl1_palettedtexture; cvar_t *gl1_palettedtexture;
cvar_t *gl1_pointparameters; cvar_t *gl1_pointparameters;
cvar_t *gl1_multitexture; cvar_t *gl1_multitexture;
cvar_t *gl1_biglightmaps;
cvar_t *gl_drawbuffer; cvar_t *gl_drawbuffer;
cvar_t *gl_lightmap; cvar_t *gl_lightmap;
@ -1221,7 +1220,6 @@ R_Register(void)
gl1_palettedtexture = ri.Cvar_Get("r_palettedtextures", "0", CVAR_ARCHIVE); gl1_palettedtexture = ri.Cvar_Get("r_palettedtextures", "0", CVAR_ARCHIVE);
gl1_pointparameters = ri.Cvar_Get("gl1_pointparameters", "1", CVAR_ARCHIVE); gl1_pointparameters = ri.Cvar_Get("gl1_pointparameters", "1", CVAR_ARCHIVE);
gl1_multitexture = ri.Cvar_Get("gl1_multitexture", "1", CVAR_ARCHIVE); gl1_multitexture = ri.Cvar_Get("gl1_multitexture", "1", CVAR_ARCHIVE);
gl1_biglightmaps = ri.Cvar_Get("gl1_biglightmaps", "1", CVAR_ARCHIVE);
gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0);
r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE);
@ -1402,7 +1400,7 @@ R_SetMode(void)
qboolean qboolean
RI_Init(void) RI_Init(void)
{ {
int j, max_tex_size; int j;
byte *colormap; byte *colormap;
extern float r_turbsin[256]; extern float r_turbsin[256];
@ -1621,35 +1619,17 @@ RI_Init(void)
// ---- // ----
/* Big lightmaps */ /* Big lightmaps: this used to be fast, but after the implementation of the "GL Buffer", it
R_Printf(PRINT_ALL, " - Big lightmaps: "); * became too evident that the bigger the texture, the slower the call to glTexSubImage2D() is.
* Original logic remains, but it's preferable not to make it visible to the user.
* Let's see if something changes in the future.
*/
gl_state.block_width = BLOCK_WIDTH; gl_state.block_width = BLOCK_WIDTH;
gl_state.block_height = BLOCK_HEIGHT; gl_state.block_height = BLOCK_HEIGHT;
gl_state.max_lightmaps = MAX_LIGHTMAPS; gl_state.max_lightmaps = MAX_LIGHTMAPS;
gl_state.scrap_width = BLOCK_WIDTH; gl_state.scrap_width = BLOCK_WIDTH * 2;
gl_state.scrap_height = BLOCK_HEIGHT; gl_state.scrap_height = BLOCK_HEIGHT * 2;
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_tex_size);
if (max_tex_size > BLOCK_WIDTH)
{
if (gl1_biglightmaps->value)
{
gl_state.block_width = gl_state.block_height = Q_min(max_tex_size, 512);
gl_state.max_lightmaps = (BLOCK_WIDTH * BLOCK_HEIGHT * MAX_LIGHTMAPS)
/ (gl_state.block_width * gl_state.block_height);
gl_state.scrap_width = gl_state.scrap_height =
(gl_config.npottextures)? Q_min(max_tex_size, 384) : Q_min(max_tex_size, 256);
R_Printf(PRINT_ALL, "Okay\n");
}
else
{
R_Printf(PRINT_ALL, "Disabled\n");
}
}
else
{
R_Printf(PRINT_ALL, "Failed, detected texture size = %d\n", max_tex_size);
}
// ---- // ----

View file

@ -86,19 +86,13 @@ R_LerpVerts(entity_t *currententity, int nverts, dtrivertx_t *v, dtrivertx_t *ov
static void static void
R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp) R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp)
{ {
unsigned short total;
GLenum type;
float l;
daliasframe_t *frame, *oldframe; daliasframe_t *frame, *oldframe;
dtrivertx_t *v, *ov, *verts; dtrivertx_t *v, *ov, *verts;
int *order; int *order;
int count; int count, i, index_xyz;
float frontlerp; float tex[2], frontlerp, l, alpha;
float alpha;
vec3_t move, delta, vectors[3]; vec3_t move, delta, vectors[3];
vec3_t frontv, backv; vec3_t frontv, backv;
int i;
int index_xyz;
float *lerp; float *lerp;
frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames
@ -120,13 +114,6 @@ R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp)
alpha = 1.0; alpha = 1.0;
} }
if (currententity->flags &
(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE |
RF_SHELL_HALF_DAM))
{
glDisable(GL_TEXTURE_2D);
}
frontlerp = 1.0 - backlerp; frontlerp = 1.0 - backlerp;
/* move should be the delta back to the previous frame * backlerp */ /* move should be the delta back to the previous frame * backlerp */
@ -154,135 +141,72 @@ R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp)
R_LerpVerts(currententity, paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv); R_LerpVerts(currententity, paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv);
#ifdef _MSC_VER // workaround for lack of VLAs (=> our workaround uses alloca() which is bad in loops)
int maxCount = 0;
const int* tmpOrder = order;
while (1) while (1)
{ {
int c = *tmpOrder++; /* get the vertex count and primitive type */
if (!c) count = *order++;
break;
if ( c < 0 )
c = -c;
if ( c > maxCount )
maxCount = c;
tmpOrder += 3 * c; if (!count)
}
YQ2_VLA( GLfloat, vtx, 3 * maxCount );
YQ2_VLA( GLfloat, tex, 2 * maxCount );
YQ2_VLA( GLfloat, clr, 4 * maxCount );
#endif
while (1)
{ {
/* get the vertex count and primitive type */ break; /* done */
count = *order++;
if (!count)
{
break; /* done */
}
if (count < 0)
{
count = -count;
type = GL_TRIANGLE_FAN;
}
else
{
type = GL_TRIANGLE_STRIP;
}
total = count;
#ifndef _MSC_VER // we have real VLAs, so it's safe to use one in this loop
YQ2_VLA(GLfloat, vtx, 3*total);
YQ2_VLA(GLfloat, tex, 2*total);
YQ2_VLA(GLfloat, clr, 4*total);
#endif
unsigned int index_vtx = 0;
unsigned int index_tex = 0;
unsigned int index_clr = 0;
if (currententity->flags &
(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE))
{
do
{
index_xyz = order[2];
order += 3;
clr[index_clr++] = shadelight[0];
clr[index_clr++] = shadelight[1];
clr[index_clr++] = shadelight[2];
clr[index_clr++] = alpha;
vtx[index_vtx++] = s_lerped[index_xyz][0];
vtx[index_vtx++] = s_lerped[index_xyz][1];
vtx[index_vtx++] = s_lerped[index_xyz][2];
}
while (--count);
}
else
{
do
{
/* texture coordinates come from the draw list */
tex[index_tex++] = ((float *) order)[0];
tex[index_tex++] = ((float *) order)[1];
index_xyz = order[2];
order += 3;
/* normals and vertexes come from the frame list */
l = shadedots[verts[index_xyz].lightnormalindex];
clr[index_clr++] = l * shadelight[0];
clr[index_clr++] = l * shadelight[1];
clr[index_clr++] = l * shadelight[2];
clr[index_clr++] = alpha;
vtx[index_vtx++] = s_lerped[index_xyz][0];
vtx[index_vtx++] = s_lerped[index_xyz][1];
vtx[index_vtx++] = s_lerped[index_xyz][2];
}
while (--count);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vtx);
glTexCoordPointer(2, GL_FLOAT, 0, tex);
glColorPointer(4, GL_FLOAT, 0, clr);
glDrawArrays(type, 0, total);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
} }
YQ2_VLAFREE( vtx ); if (count < 0)
YQ2_VLAFREE( tex ); {
YQ2_VLAFREE( clr ) count = -count;
R_SetBufferIndices(GL_TRIANGLE_FAN, count);
}
else
{
R_SetBufferIndices(GL_TRIANGLE_STRIP, count);
}
if (currententity->flags & if (currententity->flags &
(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE))
RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) {
{ do
glEnable(GL_TEXTURE_2D); {
index_xyz = order[2];
order += 3;
R_BufferVertex(s_lerped[index_xyz][0],
s_lerped[index_xyz][1], s_lerped[index_xyz][2]);
R_BufferColor(shadelight[0], shadelight[1],
shadelight[2], alpha);
}
while (--count);
}
else
{
do
{
/* texture coordinates come from the draw list */
tex[0] = ((float *)order)[0];
tex[1] = ((float *)order)[1];
index_xyz = order[2];
order += 3;
/* normals and vertexes come from the frame list */
l = shadedots[verts[index_xyz].lightnormalindex];
R_BufferVertex(s_lerped[index_xyz][0],
s_lerped[index_xyz][1], s_lerped[index_xyz][2]);
R_BufferSingleTex(tex[0], tex[1]);
R_BufferColor(l * shadelight[0], l * shadelight[1],
l * shadelight[2], alpha);
}
while (--count);
}
} }
} }
static void static void
R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum) R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum)
{ {
unsigned short total;
GLenum type;
int *order; int *order;
vec3_t point; vec3_t point;
float height = 0, lheight; float height = 0, lheight;
@ -292,6 +216,8 @@ R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum)
order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds);
height = -lheight + 0.1f; height = -lheight + 0.1f;
R_UpdateGLBuffer(buf_shadow, 0, 0, 0, 1);
/* stencilbuffer shadows */ /* stencilbuffer shadows */
if (gl_state.stencil && gl1_stencilshadow->value) if (gl_state.stencil && gl1_stencilshadow->value)
{ {
@ -300,25 +226,6 @@ R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum)
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
} }
#ifdef _MSC_VER // workaround for lack of VLAs (=> our workaround uses alloca() which is bad in loops)
int maxCount = 0;
const int* tmpOrder = order;
while (1)
{
int c = *tmpOrder++;
if (!c)
break;
if (c < 0)
c = -c;
if (c > maxCount)
maxCount = c;
tmpOrder += 3 * c;
}
YQ2_VLA(GLfloat, vtx, 3 * maxCount);
#endif
while (1) while (1)
{ {
/* get the vertex count and primitive type */ /* get the vertex count and primitive type */
@ -332,21 +239,13 @@ R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum)
if (count < 0) if (count < 0)
{ {
count = -count; count = -count;
R_SetBufferIndices(GL_TRIANGLE_FAN, count);
type = GL_TRIANGLE_FAN;
} }
else else
{ {
type = GL_TRIANGLE_STRIP; R_SetBufferIndices(GL_TRIANGLE_STRIP, count);
} }
total = count;
#ifndef _MSC_VER // we have real VLAs, so it's safe to use one in this loop
YQ2_VLA(GLfloat, vtx, 3*total);
#endif
unsigned int index_vtx = 0;
do do
{ {
/* normals and vertexes come from the frame list */ /* normals and vertexes come from the frame list */
@ -356,22 +255,14 @@ R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum)
point[1] -= shadevector[1] * (point[2] + lheight); point[1] -= shadevector[1] * (point[2] + lheight);
point[2] = height; point[2] = height;
vtx[index_vtx++] = point [ 0 ]; R_BufferVertex( point[0], point[1], point[2] );
vtx[index_vtx++] = point [ 1 ];
vtx[index_vtx++] = point [ 2 ];
order += 3; order += 3;
} }
while (--count); while (--count);
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, vtx );
glDrawArrays( type, 0, total );
glDisableClientState( GL_VERTEX_ARRAY );
} }
YQ2_VLAFREE(vtx);
R_ApplyGLBuffer();
/* stencilbuffer shadows */ /* stencilbuffer shadows */
if (gl_state.stencil && gl1_stencilshadow->value) if (gl_state.stencil && gl1_stencilshadow->value)
@ -557,7 +448,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel)
} }
} }
R_EnableMultitexture(false);
paliashdr = (dmdl_t *)currentmodel->extradata; paliashdr = (dmdl_t *)currentmodel->extradata;
/* get lighting information */ /* get lighting information */
@ -685,7 +575,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel)
} }
/* ir goggles color override */ /* ir goggles color override */
if (r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & if (r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags &
RF_IR_VISIBLE) RF_IR_VISIBLE)
@ -707,45 +596,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel)
/* locate the proper data */ /* locate the proper data */
c_alias_polys += paliashdr->num_tris; c_alias_polys += paliashdr->num_tris;
/* draw all the triangles */
if (currententity->flags & RF_DEPTHHACK)
{
/* hack the depth range to prevent view model from poking into walls */
glDepthRange(gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin));
}
if (currententity->flags & RF_WEAPONMODEL)
{
extern void R_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
if (gl_lefthand->value == 1.0F)
{
glScalef(-1, 1, 1);
}
float dist = (r_farsee->value == 0) ? 4096.0f : 8192.0f;
if (r_gunfov->value < 0)
{
R_MYgluPerspective(r_newrefdef.fov_y, (float)r_newrefdef.width / r_newrefdef.height, 4, dist);
}
else
{
R_MYgluPerspective(r_gunfov->value, (float)r_newrefdef.width / r_newrefdef.height, 4, dist);
}
glMatrixMode(GL_MODELVIEW);
if (gl_lefthand->value == 1.0F)
{
glCullFace(GL_BACK);
}
}
glPushMatrix(); glPushMatrix();
currententity->angles[PITCH] = -currententity->angles[PITCH]; currententity->angles[PITCH] = -currententity->angles[PITCH];
R_RotateForEntity(currententity); R_RotateForEntity(currententity);
@ -778,18 +628,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel)
skin = r_notexture; /* fallback... */ skin = r_notexture; /* fallback... */
} }
R_Bind(skin->texnum);
/* draw it */
glShadeModel(GL_SMOOTH);
R_TexEnv(GL_MODULATE);
if (currententity->flags & RF_TRANSLUCENT)
{
glEnable(GL_BLEND);
}
if ((currententity->frame >= paliashdr->num_frames) || if ((currententity->frame >= paliashdr->num_frames) ||
(currententity->frame < 0)) (currententity->frame < 0))
{ {
@ -813,7 +651,9 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel)
currententity->backlerp = 0; currententity->backlerp = 0;
} }
R_UpdateGLBuffer(buf_alias, skin->texnum, 0, currententity->flags, 1);
R_DrawAliasFrameLerp(currententity, paliashdr, currententity->backlerp); R_DrawAliasFrameLerp(currententity, paliashdr, currententity->backlerp);
R_ApplyGLBuffer();
R_TexEnv(GL_REPLACE); R_TexEnv(GL_REPLACE);
glShadeModel(GL_FLAT); glShadeModel(GL_FLAT);
@ -836,25 +676,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel)
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
} }
if (currententity->flags & RF_WEAPONMODEL)
{
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
if (gl_lefthand->value == 1.0F)
glCullFace(GL_FRONT);
}
if (currententity->flags & RF_TRANSLUCENT)
{
glDisable(GL_BLEND);
}
if (currententity->flags & RF_DEPTHHACK)
{
glDepthRange(gldepthmin, gldepthmax);
}
if (gl_shadows->value && if (gl_shadows->value &&
!(currententity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL | RF_NOSHADOW))) !(currententity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL | RF_NOSHADOW)))
{ {

View file

@ -46,6 +46,7 @@ static qboolean vsyncActive = false;
void void
RI_EndFrame(void) RI_EndFrame(void)
{ {
R_ApplyGLBuffer(); // to draw buffered 2D text
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
} }

View file

@ -43,63 +43,35 @@ void R_SetCacheState(msurface_t *surf);
void R_BuildLightMap(msurface_t *surf, byte *dest, int stride); void R_BuildLightMap(msurface_t *surf, byte *dest, int stride);
static void static void
R_DrawGLPoly(glpoly_t *p) R_DrawGLPoly(msurface_t *fa)
{ {
float *v; int i, nv;
float *v, scroll;
v = p->verts[0]; v = fa->polys->verts[0];
nv = fa->polys->numverts;
glEnableClientState( GL_VERTEX_ARRAY ); if (fa->texinfo->flags & SURF_FLOWING)
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v );
glTexCoordPointer( 2, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v+3 );
glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
}
static void
R_DrawGLFlowingPoly(msurface_t *fa)
{
int i;
float *v;
glpoly_t *p;
float scroll;
p = fa->polys;
scroll = -64 * ((r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0));
if (scroll == 0.0)
{ {
scroll = -64.0; scroll = -64 * ((r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0));
if (scroll == 0.0)
{
scroll = -64.0;
}
}
else
{
scroll = 0.0;
} }
YQ2_VLA(GLfloat, tex, 2*p->numverts); R_SetBufferIndices(GL_TRIANGLE_FAN, nv);
unsigned int index_tex = 0;
v = p->verts [ 0 ]; for ( i = 0; i < nv; i++, v += VERTEXSIZE )
{
for ( i = 0; i < p->numverts; i++, v += VERTEXSIZE ) R_BufferVertex(v[0], v[1], v[2]);
{ R_BufferSingleTex(v[3] + scroll, v[4]);
tex[index_tex++] = v [ 3 ] + scroll; }
tex[index_tex++] = v [ 4 ];
}
v = p->verts [ 0 ];
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
YQ2_VLAFREE(tex);
} }
static void static void
@ -419,47 +391,11 @@ R_RenderBrushPoly(entity_t *currententity, msurface_t *fa)
if (fa->flags & SURF_DRAWTURB) if (fa->flags & SURF_DRAWTURB)
{ {
/* This is a hack ontop of a hack. Warping surfaces like those generated
by R_EmitWaterPolys() don't have a lightmap. Original Quake II therefore
negated the global intensity on those surfaces, because otherwise they
would show up much too bright. When we implemented overbright bits this
hack modified the global GL state in an incompatible way. So implement
a new hack, based on overbright bits... Depending on the value set to
gl1_overbrightbits the result is different:
0: Old behaviour.
1: No overbright bits on the global scene but correct lighting on
warping surfaces.
2: Overbright bits on the global scene but not on warping surfaces.
They oversaturate otherwise. */
if (gl1_overbrightbits->value)
{
R_TexEnv(GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1);
}
else
{
R_TexEnv(GL_MODULATE);
glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity,
gl_state.inverse_intensity, 1.0f);
}
R_EmitWaterPolys(fa); R_EmitWaterPolys(fa);
R_TexEnv(GL_REPLACE);
return; return;
} }
R_TexEnv(GL_REPLACE); R_DrawGLPoly(fa);
if (fa->texinfo->flags & SURF_FLOWING)
{
R_DrawGLFlowingPoly(fa);
}
else
{
R_DrawGLPoly(fa->polys);
}
if (gl_config.multitexture) if (gl_config.multitexture)
{ {
@ -538,7 +474,7 @@ void
R_DrawAlphaSurfaces(void) R_DrawAlphaSurfaces(void)
{ {
msurface_t *s; msurface_t *s;
float intens; float alpha;
/* go back to the world matrix */ /* go back to the world matrix */
glLoadMatrixf(r_world_matrix); glLoadMatrixf(r_world_matrix);
@ -546,41 +482,35 @@ R_DrawAlphaSurfaces(void)
glEnable(GL_BLEND); glEnable(GL_BLEND);
R_TexEnv(GL_MODULATE); R_TexEnv(GL_MODULATE);
/* the textures are prescaled up for a better
lighting range, so scale it back down */
intens = gl_state.inverse_intensity;
for (s = r_alpha_surfaces; s; s = s->texturechain) for (s = r_alpha_surfaces; s; s = s->texturechain)
{ {
R_Bind(s->texinfo->image->texnum);
c_brush_polys++; c_brush_polys++;
if (s->texinfo->flags & SURF_TRANS33) if (s->texinfo->flags & SURF_TRANS33)
{ {
glColor4f(intens, intens, intens, 0.33); alpha = 0.33f;
} }
else if (s->texinfo->flags & SURF_TRANS66) else if (s->texinfo->flags & SURF_TRANS66)
{ {
glColor4f(intens, intens, intens, 0.66); alpha = 0.66f;
} }
else else
{ {
glColor4f(intens, intens, intens, 1); alpha = 1.0f;
} }
R_UpdateGLBuffer(buf_alpha, s->texinfo->image->texnum, 0, 0, alpha);
if (s->flags & SURF_DRAWTURB) if (s->flags & SURF_DRAWTURB)
{ {
R_EmitWaterPolys(s); R_EmitWaterPolys(s);
} }
else if (s->texinfo->flags & SURF_FLOWING)
{
R_DrawGLFlowingPoly(s);
}
else else
{ {
R_DrawGLPoly(s->polys); R_DrawGLPoly(s);
} }
} }
R_ApplyGLBuffer(); // Flush the last batched array
R_TexEnv(GL_REPLACE); R_TexEnv(GL_REPLACE);
glColor4f(1, 1, 1, 1); glColor4f(1, 1, 1, 1);
@ -643,15 +573,6 @@ R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf)
float scroll; float scroll;
float *v; float *v;
R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + surf->lightmaptexturenum);
// Apply overbrightbits to TMU 1 (lightmap)
if (gl1_overbrightbits->value)
{
R_TexEnv(GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value);
}
c_brush_polys++; c_brush_polys++;
v = surf->polys->verts[0]; v = surf->polys->verts[0];
@ -663,82 +584,19 @@ R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf)
{ {
scroll = -64.0; scroll = -64.0;
} }
YQ2_VLA(GLfloat, tex, 4 * nv);
unsigned int index_tex = 0;
for (i = 0; i < nv; i++, v += VERTEXSIZE)
{
tex[index_tex++] = v[3] + scroll;
tex[index_tex++] = v[4];
tex[index_tex++] = v[5];
tex[index_tex++] = v[6];
}
v = surf->polys->verts[0];
// Polygon
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, VERTEXSIZE * sizeof(GLfloat), v);
// Texture
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), tex);
// Lightmap
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE1);
glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), tex + 2);
// Draw the thing
glDrawArrays(GL_TRIANGLE_FAN, 0, nv);
YQ2_VLAFREE(tex);
} }
else else
{ {
// Polygon scroll = 0.0;
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, VERTEXSIZE * sizeof(GLfloat), v);
// Texture
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE * sizeof(GLfloat), v + 3);
// Lightmap
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE1);
glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE * sizeof(GLfloat), v + 5);
// Draw it
glDrawArrays(GL_TRIANGLE_FAN, 0, nv);
} }
glDisableClientState(GL_VERTEX_ARRAY); R_SetBufferIndices(GL_TRIANGLE_FAN, nv);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
static void for (i = 0; i < nv; i++, v += VERTEXSIZE)
R_UploadDynamicLights(msurface_t *surf)
{
int map, smax, tmax;
if ( !gl_config.multitexture || !R_HasDynamicLights(surf, &map) )
{ {
return; R_BufferVertex( v[0], v[1], v[2] );
R_BufferMultiTex( v[3] + scroll, v[4], v[5], v[6] );
} }
YQ2_VLA(byte, temp, gl_state.block_width * gl_state.block_height);
smax = (surf->extents[0] >> 4) + 1;
tmax = (surf->extents[1] >> 4) + 1;
R_BuildLightMap(surf, (void *) temp, smax * LIGHTMAP_BYTES);
R_UpdateSurfCache(surf, map);
R_Bind(gl_state.lightmap_textures + surf->lightmaptexturenum);
glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax,
tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp);
YQ2_VLAFREE(temp);
} }
/* Upload dynamic lights to each lightmap texture (multitexture path only) */ /* Upload dynamic lights to each lightmap texture (multitexture path only) */
@ -864,17 +722,16 @@ R_DrawTextureChains(entity_t *currententity)
for ( ; s; s = s->texturechain) for ( ; s; s = s->texturechain)
{ {
R_Bind(image->texnum); // may reset because of dynamic lighting in R_RenderBrushPoly R_UpdateGLBuffer(buf_singletex, image->texnum, 0, s->flags, 1);
R_RenderBrushPoly(currententity, s); R_RenderBrushPoly(currententity, s);
} }
image->texturechain = NULL; image->texturechain = NULL;
} }
R_ApplyGLBuffer(); // Flush the last batched array
} }
else // multitexture else // multitexture
{ {
R_EnableMultitexture(true);
for (i = 0, image = gltextures; i < numgltextures; i++, image++) for (i = 0, image = gltextures; i < numgltextures; i++, image++)
{ {
if (!image->registration_sequence || !image->texturechain) if (!image->registration_sequence || !image->texturechain)
@ -882,19 +739,20 @@ R_DrawTextureChains(entity_t *currententity)
continue; continue;
} }
R_MBind(GL_TEXTURE0, image->texnum); // setting it only once
c_visible_textures++; c_visible_textures++;
for (s = image->texturechain; s; s = s->texturechain) for (s = image->texturechain; s; s = s->texturechain)
{ {
if (!(s->flags & SURF_DRAWTURB)) if (!(s->flags & SURF_DRAWTURB))
{ {
R_UpdateGLBuffer(buf_mtex, image->texnum, s->lightmaptexturenum, 0, 1);
R_RenderLightmappedPoly(currententity, s); R_RenderLightmappedPoly(currententity, s);
} }
} }
} }
R_ApplyGLBuffer();
R_EnableMultitexture(false); R_EnableMultitexture(false); // force disabling, SURF_DRAWTURB surfaces may not exist
for (i = 0, image = gltextures; i < numgltextures; i++, image++) for (i = 0, image = gltextures; i < numgltextures; i++, image++)
{ {
@ -907,13 +765,14 @@ R_DrawTextureChains(entity_t *currententity)
{ {
if (s->flags & SURF_DRAWTURB) if (s->flags & SURF_DRAWTURB)
{ {
R_Bind(image->texnum); R_UpdateGLBuffer(buf_singletex, image->texnum, 0, s->flags, 1);
R_RenderBrushPoly(currententity, s); R_RenderBrushPoly(currententity, s);
} }
} }
image->texturechain = NULL; image->texturechain = NULL;
} }
R_ApplyGLBuffer();
} }
} }
@ -928,7 +787,7 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel)
image_t *image; image_t *image;
/* calculate dynamic lighting for bmodel */ /* calculate dynamic lighting for bmodel */
if (!gl1_flashblend->value) if (!gl_config.multitexture && !gl1_flashblend->value)
{ {
lt = r_newrefdef.dlights; lt = r_newrefdef.dlights;
@ -973,20 +832,19 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel)
if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB)) if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB))
{ {
R_UploadDynamicLights(psurf); // Dynamic lighting already generated in R_GetBrushesLighting()
R_EnableMultitexture(true); R_UpdateGLBuffer(buf_mtex, image->texnum, psurf->lightmaptexturenum, 0, 1);
R_MBind(GL_TEXTURE0, image->texnum);
R_RenderLightmappedPoly(currententity, psurf); R_RenderLightmappedPoly(currententity, psurf);
} }
else else
{ {
R_EnableMultitexture(false); R_UpdateGLBuffer(buf_singletex, image->texnum, 0, psurf->flags, 1);
R_Bind(image->texnum);
R_RenderBrushPoly(currententity, psurf); R_RenderBrushPoly(currententity, psurf);
} }
} }
} }
} }
R_ApplyGLBuffer();
if (!(currententity->flags & RF_TRANSLUCENT)) if (!(currententity->flags & RF_TRANSLUCENT))
{ {
@ -1214,6 +1072,96 @@ R_RecursiveWorldNode(entity_t *currententity, mnode_t *node)
R_RecursiveWorldNode(currententity, node->children[!side]); R_RecursiveWorldNode(currententity, node->children[!side]);
} }
/*
* This is for the RegenAllLightmaps() function to be able to regenerate
* lighting not only for the world, but also for the brushes in the entity list.
* Logic extracted from R_DrawBrushModel() & R_DrawInlineBModel().
*/
static void
R_GetBrushesLighting(void)
{
int i, k;
vec3_t mins, maxs;
msurface_t *surf;
cplane_t *pplane;
dlight_t *lt;
float dot;
if (!gl_config.multitexture || !r_drawentities->value || gl1_flashblend->value)
{
return;
}
for (i = 0; i < r_newrefdef.num_entities; i++)
{
entity_t *currententity = &r_newrefdef.entities[i];
if (currententity->flags & RF_BEAM)
{
continue;
}
const model_t *currentmodel = currententity->model;
if (!currentmodel || currentmodel->type != mod_brush || currentmodel->nummodelsurfaces == 0)
{
continue;
}
// from R_DrawBrushModel()
if (currententity->angles[0] || currententity->angles[1] || currententity->angles[2])
{
for (k = 0; k < 3; k++)
{
mins[k] = currententity->origin[k] - currentmodel->radius;
maxs[k] = currententity->origin[k] + currentmodel->radius;
}
}
else
{
VectorAdd(currententity->origin, currentmodel->mins, mins);
VectorAdd(currententity->origin, currentmodel->maxs, maxs);
}
if (r_cull->value && R_CullBox(mins, maxs, frustum))
{
continue;
}
// from R_DrawInlineBModel()
lt = r_newrefdef.dlights;
for (k = 0; k < r_newrefdef.num_dlights; k++, lt++)
{
R_MarkLights(lt, 1 << k,
currentmodel->nodes + currentmodel->firstnode,
r_dlightframecount, R_MarkSurfaceLights);
}
surf = &currentmodel->surfaces[currentmodel->firstmodelsurface];
for (k = 0; k < currentmodel->nummodelsurfaces; k++, surf++)
{
if (surf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)
|| surf->flags & SURF_DRAWTURB)
{
continue;
}
// find which side of the node we are on
pplane = surf->plane;
dot = DotProduct(modelorg, pplane->normal) - pplane->dist;
if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) ||
(!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)))
{
surf->lightmapchain = gl_lms.lightmap_surfaces[surf->lightmaptexturenum];
gl_lms.lightmap_surfaces[surf->lightmaptexturenum] = surf;
}
}
}
}
void void
R_DrawWorld(void) R_DrawWorld(void)
{ {
@ -1242,6 +1190,7 @@ R_DrawWorld(void)
R_ClearSkyBox(); R_ClearSkyBox();
R_RecursiveWorldNode(&ent, r_worldmodel->nodes); R_RecursiveWorldNode(&ent, r_worldmodel->nodes);
R_GetBrushesLighting();
R_RegenAllLightmaps(); R_RegenAllLightmaps();
R_DrawTextureChains(&ent); R_DrawTextureChains(&ent);
R_BlendLightmaps(r_worldmodel); R_BlendLightmaps(r_worldmodel);

View file

@ -282,7 +282,7 @@ R_EmitWaterPolys(msurface_t *fa)
{ {
glpoly_t *p, *bp; glpoly_t *p, *bp;
float *v; float *v;
int i; int i, nv;
float s, t, os, ot; float s, t, os, ot;
float scroll; float scroll;
float rdt = r_newrefdef.time; float rdt = r_newrefdef.time;
@ -296,53 +296,24 @@ R_EmitWaterPolys(msurface_t *fa)
scroll = 0; scroll = 0;
} }
// workaround for lack of VLAs (=> our workaround uses alloca() which is bad in loops)
#ifdef _MSC_VER
int maxNumVerts = 0;
for ( glpoly_t* tmp = fa->polys; tmp; tmp = tmp->next )
{
if (tmp->numverts > maxNumVerts)
maxNumVerts = tmp->numverts;
}
YQ2_VLA( GLfloat, tex, 2 * maxNumVerts );
#endif
for (bp = fa->polys; bp; bp = bp->next) for (bp = fa->polys; bp; bp = bp->next)
{ {
p = bp; p = bp;
#ifndef _MSC_VER // we have real VLAs, so it's safe to use one in this loop nv = p->numverts;
YQ2_VLA(GLfloat, tex, 2*p->numverts); R_SetBufferIndices(GL_TRIANGLE_FAN, nv);
#endif
unsigned int index_tex = 0;
for ( i = 0, v = p->verts [ 0 ]; i < p->numverts; i++, v += VERTEXSIZE ) for ( i = 0, v = p->verts [ 0 ]; i < nv; i++, v += VERTEXSIZE )
{ {
os = v [ 3 ]; os = v [ 3 ];
ot = v [ 4 ]; ot = v [ 4 ];
s = os + r_turbsin [ (int) ( ( ot * 0.125 + r_newrefdef.time ) * TURBSCALE ) & 255 ]; s = os + r_turbsin [ (int) ( ( ot * 0.125 + rdt ) * TURBSCALE ) & 255 ] + scroll;
s += scroll;
tex[index_tex++] = s * ( 1.0 / 64 );
t = ot + r_turbsin [ (int) ( ( os * 0.125 + rdt ) * TURBSCALE ) & 255 ]; t = ot + r_turbsin [ (int) ( ( os * 0.125 + rdt ) * TURBSCALE ) & 255 ];
tex[index_tex++] = t * ( 1.0 / 64 );
R_BufferVertex( v[0], v[1], v[2] );
R_BufferSingleTex( s * ( 1.0 / 64 ), t * ( 1.0 / 64 ) );
} }
v = p->verts [ 0 ];
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v );
glTexCoordPointer( 2, GL_FLOAT, 0, tex );
glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
} }
YQ2_VLAFREE( tex );
} }
void void

View file

@ -39,17 +39,18 @@
#define GL_COLOR_INDEX8_EXT GL_COLOR_INDEX #define GL_COLOR_INDEX8_EXT GL_COLOR_INDEX
#endif #endif
#define TEXNUM_LIGHTMAPS 1024 #define MAX_LIGHTMAPS 128
#define TEXNUM_SCRAPS 1152
#define TEXNUM_IMAGES 1153
#define MAX_GLTEXTURES 1024
#define MAX_SCRAPS 1 #define MAX_SCRAPS 1
#define TEXNUM_LIGHTMAPS 1024
#define TEXNUM_SCRAPS (TEXNUM_LIGHTMAPS + MAX_LIGHTMAPS)
#define TEXNUM_IMAGES (TEXNUM_SCRAPS + MAX_SCRAPS)
#define MAX_GLTEXTURES 1024
#define BLOCK_WIDTH 128 // default values; now defined in glstate_t #define BLOCK_WIDTH 128 // default values; now defined in glstate_t
#define BLOCK_HEIGHT 128 #define BLOCK_HEIGHT 128
#define REF_VERSION "Yamagi Quake II OpenGL Refresher" #define REF_VERSION "Yamagi Quake II OpenGL Refresher"
#define BACKFACE_EPSILON 0.01 #define BACKFACE_EPSILON 0.01
#define LIGHTMAP_BYTES 4 #define LIGHTMAP_BYTES 4
#define MAX_LIGHTMAPS 128 #define MAX_TEXTURE_UNITS 2
#define GL_LIGHTMAP_FORMAT GL_RGBA #define GL_LIGHTMAP_FORMAT GL_RGBA
/* up / down */ /* up / down */
@ -106,22 +107,23 @@ typedef enum
rserr_unknown rserr_unknown
} rserr_t; } rserr_t;
#include "model.h" typedef enum
{
buf_2d,
buf_singletex,
buf_mtex,
buf_alpha,
buf_alias,
buf_flash,
buf_shadow
} buffered_draw_t;
void GL_BeginRendering(int *x, int *y, int *width, int *height); #include "model.h"
void GL_EndRendering(void);
void R_SetDefaultState(void); void R_SetDefaultState(void);
extern float gldepthmin, gldepthmax; extern float gldepthmin, gldepthmax;
typedef struct
{
float x, y, z;
float s, t;
float r, g, b;
} glvert_t;
extern image_t gltextures[MAX_GLTEXTURES]; extern image_t gltextures[MAX_GLTEXTURES];
extern int numgltextures; extern int numgltextures;
@ -163,7 +165,6 @@ extern cvar_t *gl1_overbrightbits;
extern cvar_t *gl1_palettedtexture; extern cvar_t *gl1_palettedtexture;
extern cvar_t *gl1_pointparameters; extern cvar_t *gl1_pointparameters;
extern cvar_t *gl1_multitexture; extern cvar_t *gl1_multitexture;
extern cvar_t *gl1_biglightmaps;
extern cvar_t *gl1_particle_min_size; extern cvar_t *gl1_particle_min_size;
extern cvar_t *gl1_particle_max_size; extern cvar_t *gl1_particle_max_size;
@ -229,7 +230,7 @@ extern int c_visible_textures;
extern float r_world_matrix[16]; extern float r_world_matrix[16];
void R_TranslatePlayerSkin(int playernum); void R_TranslatePlayerSkin(int playernum);
void R_Bind(int texnum); qboolean R_Bind(int texnum);
void R_TexEnv(GLenum value); void R_TexEnv(GLenum value);
void R_SelectTexture(GLenum); void R_SelectTexture(GLenum);
@ -290,12 +291,23 @@ void R_TextureAlphaMode(char *string);
void R_TextureSolidMode(char *string); void R_TextureSolidMode(char *string);
int Scrap_AllocBlock(int w, int h, int *x, int *y); int Scrap_AllocBlock(int w, int h, int *x, int *y);
void R_ApplyGLBuffer(void);
void R_UpdateGLBuffer(buffered_draw_t type, int colortex, int lighttex, int flags, float alpha);
void R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy,
GLfloat ul_tx, GLfloat ul_ty, GLfloat dr_tx, GLfloat dr_ty);
void R_SetBufferIndices(GLenum type, GLuint vertices_num);
void R_BufferVertex(GLfloat x, GLfloat y, GLfloat z);
void R_BufferSingleTex(GLfloat s, GLfloat t);
void R_BufferMultiTex(GLfloat cs, GLfloat ct, GLfloat ls, GLfloat lt);
void R_BufferColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
#ifdef DEBUG #ifdef DEBUG
void glCheckError_(const char *file, const char *function, int line); void glCheckError_(const char *file, const char *function, int line);
// Ideally, the following list should contain all OpenGL calls. // Ideally, the following list should contain all OpenGL calls.
// Either way, errors are caught, since error flags are persisted until the next glGetError() call. // Either way, errors are caught, since error flags are persisted until the next glGetError() call.
// So they show, even if the location of the error is inaccurate. // So they show, even if the location of the error is inaccurate.
#define glDrawArrays(...) glDrawArrays(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDrawArrays(...) glDrawArrays(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__)
#define glDrawElements(...) glDrawElements(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__)
#define glTexImage2D(...) glTexImage2D(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexImage2D(...) glTexImage2D(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__)
#define glTexSubImage2D(...) glTexSubImage2D(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexSubImage2D(...) glTexSubImage2D(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__)
#define glTexEnvf(...) glTexEnvf(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexEnvf(...) glTexEnvf(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__)
@ -382,7 +394,7 @@ typedef struct
int lightmap_textures; int lightmap_textures;
int currenttextures[2]; int currenttextures[MAX_TEXTURE_UNITS];
int currenttmu; int currenttmu;
GLenum currenttarget; GLenum currenttarget;