diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ff24654..72377b13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -590,6 +590,7 @@ set(GL1-Source ${REF_SRC_DIR}/gl1/gl1_surf.c ${REF_SRC_DIR}/gl1/gl1_warp.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/pcx.c ${REF_SRC_DIR}/files/stb.c diff --git a/Makefile b/Makefile index 58407ceb..898f5623 100644 --- a/Makefile +++ b/Makefile @@ -959,6 +959,7 @@ REFGL1_OBJS_ := \ src/client/refresh/gl1/gl1_surf.o \ src/client/refresh/gl1/gl1_warp.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/models.o \ src/client/refresh/files/pcx.o \ diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 19c77042..db4bab1c 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -471,10 +471,6 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable` ## 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 value, at least `1.0` - default is `2.0`. Applied when textures are loaded, so it needs a `vid_restart`. diff --git a/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c new file mode 100644 index 00000000..161ff500 --- /dev/null +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -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; +} diff --git a/src/client/refresh/gl1/gl1_draw.c b/src/client/refresh/gl1/gl1_draw.c index c8093301..6fe553da 100644 --- a/src/client/refresh/gl1/gl1_draw.c +++ b/src/client/refresh/gl1/gl1_draw.c @@ -77,31 +77,10 @@ RDraw_CharScaled(int x, int y, int num, float scale) scaledSize = 8*scale; - R_Bind(draw_chars->texnum); + R_UpdateGLBuffer(buf_2d, draw_chars->texnum, 0, 0, 1); - GLfloat vtx[] = { - x, y, - 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 ); + R_Buffer2DQuad(x, y, x + scaledSize, y + scaledSize, + fcol, frow, fcol + size, frow + size); } image_t * @@ -190,6 +169,14 @@ RDraw_PicScaled(int x, int y, char *pic, float factor) 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); GLfloat vtx[] = { @@ -235,31 +222,10 @@ RDraw_TileClear(int x, int y, int w, int h, char *pic) return; } - R_Bind(image->texnum); + R_UpdateGLBuffer(buf_2d, image->texnum, 0, 0, 1); - GLfloat vtx[] = { - x, y, - 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 ); + R_Buffer2DQuad(x, y, x + w, y + h, x / 64.0, y / 64.0, + ( x + w ) / 64.0, ( y + h ) / 64.0); } /* @@ -306,6 +272,7 @@ RDraw_Fill(int x, int y, int w, int h, int c) void RDraw_FadeScreen(void) { + R_ApplyGLBuffer(); // draw what needs to be hidden glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); glColor4f(0, 0, 0, 0.8); diff --git a/src/client/refresh/gl1/gl1_image.c b/src/client/refresh/gl1/gl1_image.c index 1be62d1e..dd748899 100644 --- a/src/client/refresh/gl1/gl1_image.c +++ b/src/client/refresh/gl1/gl1_image.c @@ -147,21 +147,12 @@ R_SetTexturePalette(unsigned palette[256]) void R_SelectTexture(GLenum texture) { - int tmu; - - if (!gl_config.multitexture) + if (!gl_config.multitexture || gl_state.currenttarget == texture) { return; } - tmu = texture - GL_TEXTURE0; - - if (tmu == gl_state.currenttmu) - { - return; - } - - gl_state.currenttmu = tmu; + gl_state.currenttmu = texture - GL_TEXTURE0; gl_state.currenttarget = texture; qglActiveTexture(texture); @@ -180,7 +171,7 @@ R_TexEnv(GLenum mode) } } -void +qboolean R_Bind(int texnum) { extern image_t *draw_chars; @@ -192,11 +183,12 @@ R_Bind(int texnum) if (gl_state.currenttextures[gl_state.currenttmu] == texnum) { - return; + return false; } gl_state.currenttextures[gl_state.currenttmu] = texnum; glBindTexture(GL_TEXTURE_2D, texnum); + return true; } void @@ -308,7 +300,11 @@ R_TextureMode(char *string) 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 */ { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); diff --git a/src/client/refresh/gl1/gl1_light.c b/src/client/refresh/gl1/gl1_light.c index 88b46106..c2251e9b 100644 --- a/src/client/refresh/gl1/gl1_light.c +++ b/src/client/refresh/gl1/gl1_light.c @@ -35,53 +35,34 @@ static float s_blocklights[34 * 34 * 3]; void R_RenderDlight(dlight_t *light) { + const float rad = light->intensity * 0.35; int i, j; - float a; - float rad; + float vtx[3], a; - rad = light->intensity * 0.35; - - 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; + R_SetBufferIndices(GL_TRIANGLE_FAN, 18); for ( i = 0; i < 3; i++ ) { 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-- ) { - clr[index_clr++] = 0; - clr[index_clr++] = 0; - clr[index_clr++] = 0; - clr[index_clr++] = 1; - a = i / 16.0 * M_PI * 2; 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; } + + 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 @@ -94,6 +75,7 @@ R_RenderDlights(void) { return; } + R_UpdateGLBuffer(buf_flash, 0, 0, 0, 1); /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; @@ -110,6 +92,7 @@ R_RenderDlights(void) { R_RenderDlight(l); } + R_ApplyGLBuffer(); glColor4f(1, 1, 1, 1); glDisable(GL_BLEND); diff --git a/src/client/refresh/gl1/gl1_main.c b/src/client/refresh/gl1/gl1_main.c index 1afef160..4edfe024 100644 --- a/src/client/refresh/gl1/gl1_main.c +++ b/src/client/refresh/gl1/gl1_main.c @@ -91,7 +91,6 @@ cvar_t *gl1_particle_square; cvar_t *gl1_palettedtexture; cvar_t *gl1_pointparameters; cvar_t *gl1_multitexture; -cvar_t *gl1_biglightmaps; cvar_t *gl_drawbuffer; cvar_t *gl_lightmap; @@ -1221,7 +1220,6 @@ R_Register(void) gl1_palettedtexture = ri.Cvar_Get("r_palettedtextures", "0", CVAR_ARCHIVE); gl1_pointparameters = ri.Cvar_Get("gl1_pointparameters", "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); r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); @@ -1402,7 +1400,7 @@ R_SetMode(void) qboolean RI_Init(void) { - int j, max_tex_size; + int j; byte *colormap; extern float r_turbsin[256]; @@ -1621,35 +1619,17 @@ RI_Init(void) // ---- - /* Big lightmaps */ - R_Printf(PRINT_ALL, " - Big lightmaps: "); + /* Big lightmaps: this used to be fast, but after the implementation of the "GL Buffer", it + * 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_height = BLOCK_HEIGHT; gl_state.max_lightmaps = MAX_LIGHTMAPS; - gl_state.scrap_width = BLOCK_WIDTH; - gl_state.scrap_height = BLOCK_HEIGHT; - 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); - } + gl_state.scrap_width = BLOCK_WIDTH * 2; + gl_state.scrap_height = BLOCK_HEIGHT * 2; // ---- diff --git a/src/client/refresh/gl1/gl1_mesh.c b/src/client/refresh/gl1/gl1_mesh.c index e4ab4f8c..f6ec39c2 100644 --- a/src/client/refresh/gl1/gl1_mesh.c +++ b/src/client/refresh/gl1/gl1_mesh.c @@ -86,19 +86,13 @@ R_LerpVerts(entity_t *currententity, int nverts, dtrivertx_t *v, dtrivertx_t *ov static void R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp) { - unsigned short total; - GLenum type; - float l; daliasframe_t *frame, *oldframe; dtrivertx_t *v, *ov, *verts; int *order; - int count; - float frontlerp; - float alpha; + int count, i, index_xyz; + float tex[2], frontlerp, l, alpha; vec3_t move, delta, vectors[3]; vec3_t frontv, backv; - int i; - int index_xyz; float *lerp; 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; } - 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; /* 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); -#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; + /* get the vertex count and primitive type */ + count = *order++; - tmpOrder += 3 * c; - } - - YQ2_VLA( GLfloat, vtx, 3 * maxCount ); - YQ2_VLA( GLfloat, tex, 2 * maxCount ); - YQ2_VLA( GLfloat, clr, 4 * maxCount ); -#endif - - while (1) + if (!count) { - /* get the vertex count and primitive type */ - 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); + break; /* done */ } - YQ2_VLAFREE( vtx ); - YQ2_VLAFREE( tex ); - YQ2_VLAFREE( clr ) + if (count < 0) + { + count = -count; + R_SetBufferIndices(GL_TRIANGLE_FAN, count); + } + else + { + R_SetBufferIndices(GL_TRIANGLE_STRIP, count); + } - if (currententity->flags & - (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | - RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) - { - glEnable(GL_TEXTURE_2D); + if (currententity->flags & + (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)) + { + do + { + 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 R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum) { - unsigned short total; - GLenum type; int *order; vec3_t point; 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); height = -lheight + 0.1f; + R_UpdateGLBuffer(buf_shadow, 0, 0, 0, 1); + /* stencilbuffer shadows */ 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); } -#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) { /* get the vertex count and primitive type */ @@ -332,21 +239,13 @@ R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum) if (count < 0) { count = -count; - - type = GL_TRIANGLE_FAN; + R_SetBufferIndices(GL_TRIANGLE_FAN, count); } 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 { /* 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[2] = height; - vtx[index_vtx++] = point [ 0 ]; - vtx[index_vtx++] = point [ 1 ]; - vtx[index_vtx++] = point [ 2 ]; + R_BufferVertex( point[0], point[1], point[2] ); order += 3; } 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 */ 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; /* get lighting information */ @@ -685,7 +575,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) } - /* ir goggles color override */ if (r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE) @@ -707,45 +596,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) /* locate the proper data */ 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(); currententity->angles[PITCH] = -currententity->angles[PITCH]; R_RotateForEntity(currententity); @@ -778,18 +628,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) 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) || (currententity->frame < 0)) { @@ -813,7 +651,9 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) currententity->backlerp = 0; } + R_UpdateGLBuffer(buf_alias, skin->texnum, 0, currententity->flags, 1); R_DrawAliasFrameLerp(currententity, paliashdr, currententity->backlerp); + R_ApplyGLBuffer(); R_TexEnv(GL_REPLACE); glShadeModel(GL_FLAT); @@ -836,25 +676,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) 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 && !(currententity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL | RF_NOSHADOW))) { diff --git a/src/client/refresh/gl1/gl1_sdl.c b/src/client/refresh/gl1/gl1_sdl.c index 7623ea32..707b9154 100644 --- a/src/client/refresh/gl1/gl1_sdl.c +++ b/src/client/refresh/gl1/gl1_sdl.c @@ -46,6 +46,7 @@ static qboolean vsyncActive = false; void RI_EndFrame(void) { + R_ApplyGLBuffer(); // to draw buffered 2D text SDL_GL_SwapWindow(window); } diff --git a/src/client/refresh/gl1/gl1_surf.c b/src/client/refresh/gl1/gl1_surf.c index 5283d3fb..25050188 100644 --- a/src/client/refresh/gl1/gl1_surf.c +++ b/src/client/refresh/gl1/gl1_surf.c @@ -43,63 +43,35 @@ void R_SetCacheState(msurface_t *surf); void R_BuildLightMap(msurface_t *surf, byte *dest, int stride); 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 ); - 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) + if (fa->texinfo->flags & SURF_FLOWING) { - 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); - unsigned int index_tex = 0; + R_SetBufferIndices(GL_TRIANGLE_FAN, nv); - v = p->verts [ 0 ]; - - for ( i = 0; i < p->numverts; i++, v += VERTEXSIZE ) - { - 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); + for ( i = 0; i < nv; i++, v += VERTEXSIZE ) + { + R_BufferVertex(v[0], v[1], v[2]); + R_BufferSingleTex(v[3] + scroll, v[4]); + } } static void @@ -419,47 +391,11 @@ R_RenderBrushPoly(entity_t *currententity, msurface_t *fa) 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_TexEnv(GL_REPLACE); - return; } - R_TexEnv(GL_REPLACE); - - if (fa->texinfo->flags & SURF_FLOWING) - { - R_DrawGLFlowingPoly(fa); - } - else - { - R_DrawGLPoly(fa->polys); - } + R_DrawGLPoly(fa); if (gl_config.multitexture) { @@ -538,7 +474,7 @@ void R_DrawAlphaSurfaces(void) { msurface_t *s; - float intens; + float alpha; /* go back to the world matrix */ glLoadMatrixf(r_world_matrix); @@ -546,41 +482,35 @@ R_DrawAlphaSurfaces(void) glEnable(GL_BLEND); 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) { - R_Bind(s->texinfo->image->texnum); c_brush_polys++; if (s->texinfo->flags & SURF_TRANS33) { - glColor4f(intens, intens, intens, 0.33); + alpha = 0.33f; } else if (s->texinfo->flags & SURF_TRANS66) { - glColor4f(intens, intens, intens, 0.66); + alpha = 0.66f; } 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) { R_EmitWaterPolys(s); } - else if (s->texinfo->flags & SURF_FLOWING) - { - R_DrawGLFlowingPoly(s); - } else { - R_DrawGLPoly(s->polys); + R_DrawGLPoly(s); } } + R_ApplyGLBuffer(); // Flush the last batched array R_TexEnv(GL_REPLACE); glColor4f(1, 1, 1, 1); @@ -643,15 +573,6 @@ R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) float scroll; 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++; v = surf->polys->verts[0]; @@ -663,82 +584,19 @@ R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) { 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 { - // 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, 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); + scroll = 0.0; } - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} + R_SetBufferIndices(GL_TRIANGLE_FAN, nv); -static void -R_UploadDynamicLights(msurface_t *surf) -{ - int map, smax, tmax; - - if ( !gl_config.multitexture || !R_HasDynamicLights(surf, &map) ) + for (i = 0; i < nv; i++, v += VERTEXSIZE) { - 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) */ @@ -864,17 +722,16 @@ R_DrawTextureChains(entity_t *currententity) 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); } image->texturechain = NULL; } + R_ApplyGLBuffer(); // Flush the last batched array } else // multitexture { - R_EnableMultitexture(true); - for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence || !image->texturechain) @@ -882,19 +739,20 @@ R_DrawTextureChains(entity_t *currententity) continue; } - R_MBind(GL_TEXTURE0, image->texnum); // setting it only once c_visible_textures++; for (s = image->texturechain; s; s = s->texturechain) { if (!(s->flags & SURF_DRAWTURB)) { + R_UpdateGLBuffer(buf_mtex, image->texnum, s->lightmaptexturenum, 0, 1); 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++) { @@ -907,13 +765,14 @@ R_DrawTextureChains(entity_t *currententity) { if (s->flags & SURF_DRAWTURB) { - R_Bind(image->texnum); + R_UpdateGLBuffer(buf_singletex, image->texnum, 0, s->flags, 1); R_RenderBrushPoly(currententity, s); } } image->texturechain = NULL; } + R_ApplyGLBuffer(); } } @@ -928,7 +787,7 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) image_t *image; /* calculate dynamic lighting for bmodel */ - if (!gl1_flashblend->value) + if (!gl_config.multitexture && !gl1_flashblend->value) { 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)) { - R_UploadDynamicLights(psurf); - R_EnableMultitexture(true); - R_MBind(GL_TEXTURE0, image->texnum); + // Dynamic lighting already generated in R_GetBrushesLighting() + R_UpdateGLBuffer(buf_mtex, image->texnum, psurf->lightmaptexturenum, 0, 1); R_RenderLightmappedPoly(currententity, psurf); } else { - R_EnableMultitexture(false); - R_Bind(image->texnum); + R_UpdateGLBuffer(buf_singletex, image->texnum, 0, psurf->flags, 1); R_RenderBrushPoly(currententity, psurf); } } } } + R_ApplyGLBuffer(); if (!(currententity->flags & RF_TRANSLUCENT)) { @@ -1214,6 +1072,96 @@ R_RecursiveWorldNode(entity_t *currententity, mnode_t *node) 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 = ¤tmodel->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 R_DrawWorld(void) { @@ -1242,6 +1190,7 @@ R_DrawWorld(void) R_ClearSkyBox(); R_RecursiveWorldNode(&ent, r_worldmodel->nodes); + R_GetBrushesLighting(); R_RegenAllLightmaps(); R_DrawTextureChains(&ent); R_BlendLightmaps(r_worldmodel); diff --git a/src/client/refresh/gl1/gl1_warp.c b/src/client/refresh/gl1/gl1_warp.c index ba96a4a3..d0ca9f7a 100644 --- a/src/client/refresh/gl1/gl1_warp.c +++ b/src/client/refresh/gl1/gl1_warp.c @@ -282,7 +282,7 @@ R_EmitWaterPolys(msurface_t *fa) { glpoly_t *p, *bp; float *v; - int i; + int i, nv; float s, t, os, ot; float scroll; float rdt = r_newrefdef.time; @@ -296,53 +296,24 @@ R_EmitWaterPolys(msurface_t *fa) 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) { p = bp; -#ifndef _MSC_VER // we have real VLAs, so it's safe to use one in this loop - YQ2_VLA(GLfloat, tex, 2*p->numverts); -#endif - unsigned int index_tex = 0; + nv = p->numverts; + R_SetBufferIndices(GL_TRIANGLE_FAN, nv); - 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 ]; ot = v [ 4 ]; - s = os + r_turbsin [ (int) ( ( ot * 0.125 + r_newrefdef.time ) * TURBSCALE ) & 255 ]; - s += scroll; - tex[index_tex++] = s * ( 1.0 / 64 ); - + s = os + r_turbsin [ (int) ( ( ot * 0.125 + rdt ) * TURBSCALE ) & 255 ] + scroll; 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 diff --git a/src/client/refresh/gl1/header/local.h b/src/client/refresh/gl1/header/local.h index 7422f633..ada739b9 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -39,17 +39,18 @@ #define GL_COLOR_INDEX8_EXT GL_COLOR_INDEX #endif -#define TEXNUM_LIGHTMAPS 1024 -#define TEXNUM_SCRAPS 1152 -#define TEXNUM_IMAGES 1153 -#define MAX_GLTEXTURES 1024 +#define MAX_LIGHTMAPS 128 #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_HEIGHT 128 #define REF_VERSION "Yamagi Quake II OpenGL Refresher" #define BACKFACE_EPSILON 0.01 #define LIGHTMAP_BYTES 4 -#define MAX_LIGHTMAPS 128 +#define MAX_TEXTURE_UNITS 2 #define GL_LIGHTMAP_FORMAT GL_RGBA /* up / down */ @@ -106,22 +107,23 @@ typedef enum rserr_unknown } 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); -void GL_EndRendering(void); +#include "model.h" void R_SetDefaultState(void); 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 int numgltextures; @@ -163,7 +165,6 @@ extern cvar_t *gl1_overbrightbits; extern cvar_t *gl1_palettedtexture; extern cvar_t *gl1_pointparameters; extern cvar_t *gl1_multitexture; -extern cvar_t *gl1_biglightmaps; extern cvar_t *gl1_particle_min_size; extern cvar_t *gl1_particle_max_size; @@ -229,7 +230,7 @@ extern int c_visible_textures; extern float r_world_matrix[16]; void R_TranslatePlayerSkin(int playernum); -void R_Bind(int texnum); +qboolean R_Bind(int texnum); void R_TexEnv(GLenum value); void R_SelectTexture(GLenum); @@ -290,12 +291,23 @@ void R_TextureAlphaMode(char *string); void R_TextureSolidMode(char *string); 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 void glCheckError_(const char *file, const char *function, int line); // Ideally, the following list should contain all OpenGL calls. // 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. #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 glTexSubImage2D(...) glTexSubImage2D(__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 currenttextures[2]; + int currenttextures[MAX_TEXTURE_UNITS]; int currenttmu; GLenum currenttarget;