diff --git a/CHANGELOG b/CHANGELOG index 67b8c2af..5ee41c99 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -83,7 +83,6 @@ Quake II 8.30 to 8.40: `gl1_multitexture`, enabled by default. (by protocultor) - Optimize dynamic lights and texture allocation in the OpenGL 1.4 renderer. (by protocultor) -- Support big lightmaps in the OpenGL 1.4 renderer. (by protocultor) - Implement gyro tightening for gamepads and joysticks. (by protocultor) - Support long player skin names. (by 0lvin) - Add a very simple download filter. Files ending in .dll, .dylib and @@ -91,6 +90,8 @@ Quake II 8.30 to 8.40: - Don't load OpenAL and cURL libraries if thy are configured with a full or relative path. - Work around naggy help icons. (by BjossiAlfreds) +- Group draw call in GL1. This yields huge performance gains on slow + GPUs. (by protocultor) Quake II 8.20 to 8.30: - Use the same image loading code in all renderers. (by 0lvin) 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 8610a2f5..61832e94 100644 --- a/Makefile +++ b/Makefile @@ -1096,6 +1096,7 @@ REFGL1_OBJS_ := \ src/client/refresh/gl1/gl1_sdl.o \ src/client/refresh/files/mesh.o \ src/client/refresh/files/light.o \ + src/client/refresh/gl1/gl1_buffer.o \ src/client/refresh/files/surf.o \ src/client/refresh/files/maps.o \ src/client/refresh/files/models.o \ diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 3560bb2b..51648f5c 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -495,10 +495,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 9e814a30..732da7f2 100644 --- a/src/client/refresh/gl1/gl1_draw.c +++ b/src/client/refresh/gl1/gl1_draw.c @@ -90,31 +90,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 * @@ -203,6 +182,14 @@ RDraw_PicScaled(int x, int y, const 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[] = { @@ -248,31 +235,10 @@ RDraw_TileClear(int x, int y, int w, int h, const 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); } /* @@ -319,6 +285,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 065e1845..a30df199 100644 --- a/src/client/refresh/gl1/gl1_image.c +++ b/src/client/refresh/gl1/gl1_image.c @@ -147,21 +147,12 @@ R_SetTexturePalette(const 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(const 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 ef580fd8..af22fa34 100644 --- a/src/client/refresh/gl1/gl1_light.c +++ b/src/client/refresh/gl1/gl1_light.c @@ -32,54 +32,36 @@ vec3_t lightspot; static void R_RenderDlight(dlight_t *light) { + const float rad = light->intensity * 0.35; int i, j; - float rad; + float vtx[3]; - 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-- ) { float a; - 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 @@ -92,6 +74,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; @@ -108,6 +91,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 90239faa..86e2667d 100644 --- a/src/client/refresh/gl1/gl1_main.c +++ b/src/client/refresh/gl1/gl1_main.c @@ -92,7 +92,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; @@ -1171,6 +1170,7 @@ R_SetLightLevel(const entity_t *currententity) static void RI_RenderFrame(refdef_t *fd) { + R_ApplyGLBuffer(); // menu rendering when needed R_RenderView(fd); R_SetLightLevel(NULL); R_SetGL2D(); @@ -1234,7 +1234,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); @@ -1415,7 +1414,7 @@ R_SetMode(void) qboolean RI_Init(void) { - int j, max_tex_size; + int j; extern float r_turbsin[256]; Swap_Init(); @@ -1633,35 +1632,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 3a810daf..bde9abfe 100644 --- a/src/client/refresh/gl1/gl1_mesh.c +++ b/src/client/refresh/gl1/gl1_mesh.c @@ -31,31 +31,8 @@ R_DrawAliasDrawCommands(const entity_t *currententity, int *order, const int *or float alpha, dxtrivertx_t *verts, vec4_t *s_lerped, const float *shadelight, const float *shadevector) { -#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 ); - YQ2_VLA( GLfloat, tex, 2 * maxCount ); - YQ2_VLA( GLfloat, clr, 4 * maxCount ); -#endif - - while (1) - { - unsigned short total; - GLenum type; int count; /* get the vertex count and primitive type */ @@ -69,28 +46,16 @@ R_DrawAliasDrawCommands(const entity_t *currententity, int *order, const int *or 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); - YQ2_VLA(GLfloat, tex, 2*total); - YQ2_VLA(GLfloat, clr, 4*total); -#endif - if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)) { - unsigned int index_vtx = 0; - unsigned int index_clr = 0; - do { int index_xyz; @@ -98,32 +63,25 @@ R_DrawAliasDrawCommands(const entity_t *currententity, int *order, const int *or 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; + R_BufferVertex(s_lerped[index_xyz][0], + s_lerped[index_xyz][1], s_lerped[index_xyz][2]); - vtx[index_vtx++] = s_lerped[index_xyz][0]; - vtx[index_vtx++] = s_lerped[index_xyz][1]; - vtx[index_vtx++] = s_lerped[index_xyz][2]; + R_BufferColor(shadelight[0], shadelight[1], + shadelight[2], alpha); } while (--count); } else { - unsigned int index_vtx = 0; - unsigned int index_tex = 0; - unsigned int index_clr = 0; - do { int i, index_xyz; vec3_t normal; - float l; + float l, tex[2]; /* texture coordinates come from the draw list */ - tex[index_tex++] = ((float *) order)[0]; - tex[index_tex++] = ((float *) order)[1]; + tex[0] = ((float *)order)[0]; + tex[1] = ((float *)order)[1]; index_xyz = order[2]; order += 3; @@ -138,35 +96,17 @@ R_DrawAliasDrawCommands(const entity_t *currententity, int *order, const int *or /* shadevector is set above according to rotation (around Z axis I think) */ l = DotProduct(normal, shadevector) + 1; - clr[index_clr++] = l * shadelight[0]; - clr[index_clr++] = l * shadelight[1]; - clr[index_clr++] = l * shadelight[2]; - clr[index_clr++] = alpha; + R_BufferVertex(s_lerped[index_xyz][0], + s_lerped[index_xyz][1], s_lerped[index_xyz][2]); - vtx[index_vtx++] = s_lerped[index_xyz][0]; - vtx[index_vtx++] = s_lerped[index_xyz][1]; - vtx[index_vtx++] = 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); } - - 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 ); - YQ2_VLAFREE( tex ); - YQ2_VLAFREE( clr ); } /* @@ -211,11 +151,6 @@ R_DrawAliasFrameLerp(entity_t *currententity, dmdx_t *paliashdr, float backlerp, alpha = 1.0; } - if (colorOnly) - { - glDisable(GL_TEXTURE_2D); - } - frontlerp = 1.0 - backlerp; /* move should be the delta back to the previous frame * backlerp */ @@ -252,41 +187,15 @@ R_DrawAliasFrameLerp(entity_t *currententity, dmdx_t *paliashdr, float backlerp, mesh_nodes[i].ofs_glcmds + mesh_nodes[i].num_glcmds), alpha, verts, s_lerped, shadelight, shadevector); } - - if (colorOnly) - { - glEnable(GL_TEXTURE_2D); - } } static void R_DrawAliasShadowCommand(const entity_t *currententity, int *order, const int *order_end, float height, float lheight, vec4_t *s_lerped, const float *shadevector) { - unsigned short total; vec3_t point; - GLenum type; int count; - #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 */ @@ -300,21 +209,13 @@ R_DrawAliasShadowCommand(const entity_t *currententity, int *order, const int *o 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 */ @@ -324,22 +225,12 @@ R_DrawAliasShadowCommand(const entity_t *currententity, int *order, const int *o 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); } static void @@ -354,6 +245,8 @@ R_DrawAliasShadow(entity_t *currententity, dmdx_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) { @@ -374,6 +267,8 @@ R_DrawAliasShadow(entity_t *currententity, dmdx_t *paliashdr, int posenum, height, lheight, s_lerped, shadevector); } + R_ApplyGLBuffer(); + /* stencilbuffer shadows */ if (gl_state.stencil && gl1_stencilshadow->value) { @@ -439,7 +334,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) } } - R_EnableMultitexture(false); paliashdr = (dmdx_t *)currentmodel->extradata; /* get lighting information */ @@ -593,45 +487,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); @@ -660,18 +515,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)) { @@ -698,8 +541,10 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) /* buffer for scalled vert from frame */ s_lerped = R_VertBufferRealloc(paliashdr->num_xyz); + R_UpdateGLBuffer(buf_alias, skin->texnum, 0, currententity->flags, 1); R_DrawAliasFrameLerp(currententity, paliashdr, currententity->backlerp, s_lerped, shadelight, shadevector); + R_ApplyGLBuffer(); R_TexEnv(GL_REPLACE); glShadeModel(GL_FLAT); @@ -722,25 +567,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 55b34325..3a169f74 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 3a22c7e3..3732317b 100644 --- a/src/client/refresh/gl1/gl1_surf.c +++ b/src/client/refresh/gl1/gl1_surf.c @@ -40,55 +40,24 @@ void LM_UploadBlock(qboolean dynamic); qboolean LM_AllocBlock(int w, int h, int *x, int *y); static void -R_DrawGLPoly(mpoly_t *p) +R_DrawGLPoly(msurface_t *fa) { - mvtx_t* vert = p->verts; - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - glVertexPointer(3, GL_FLOAT, sizeof(mvtx_t), vert->pos); - glTexCoordPointer(2, GL_FLOAT, sizeof(mvtx_t), vert->texCoord); - 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; - mvtx_t* vert; - mpoly_t *p; + int i, nv; + mvtx_t *v; float sscroll, tscroll; - p = fa->polys; + v = fa->polys->verts; + nv = fa->polys->numverts; R_FlowingScroll(&r_newrefdef, fa->texinfo->flags, &sscroll, &tscroll); - YQ2_VLA(GLfloat, tex, 2 * p->numverts); - unsigned int index_tex = 0; + R_SetBufferIndices(GL_TRIANGLE_FAN, nv); - vert = p->verts; - - for ( i = 0; i < p->numverts; i++, vert++) + for ( i = 0; i < nv; i++, v ++) { - tex[index_tex++] = vert->texCoord[0] + sscroll; - tex[index_tex++] = vert->texCoord[1] + tscroll; + R_BufferVertex(v->pos[0], v->pos[1], v->pos[2]); + R_BufferSingleTex(v->texCoord[0] + sscroll, v->texCoord[1] + tscroll); } - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - glVertexPointer(3, GL_FLOAT, sizeof(mvtx_t), p->verts->pos); - 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 @@ -410,47 +379,11 @@ R_RenderBrushPoly(const 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_SCROLL) - { - R_DrawGLFlowingPoly(fa); - } - else - { - R_DrawGLPoly(fa->polys); - } + R_DrawGLPoly(fa); if (gl_config.multitexture) { @@ -532,7 +465,7 @@ void R_DrawAlphaSurfaces(void) { msurface_t *s; - float intens; + float alpha; /* go back to the world matrix */ glLoadMatrixf(r_world_matrix); @@ -540,41 +473,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_SCROLL) - { - 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); @@ -635,104 +562,21 @@ R_RenderLightmappedPoly(const entity_t *currententity, msurface_t *surf) int i; int nv = surf->polys->numverts; mvtx_t* vert; - - 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); - } + float sscroll, tscroll; c_brush_polys++; vert = surf->polys->verts; - if (surf->texinfo->flags & SURF_FLOWING) + R_FlowingScroll(&r_newrefdef, surf->texinfo->flags, &sscroll, &tscroll); + + R_SetBufferIndices(GL_TRIANGLE_FAN, nv); + + for (i = 0; i < nv; i++, vert++) { - float sscroll, tscroll; - - R_FlowingScroll(&r_newrefdef, surf->texinfo->flags, &sscroll, &tscroll); - - YQ2_VLA(GLfloat, tex, 4 * nv); - unsigned int index_tex = 0; - - for (i = 0; i < nv; i++, vert++) - { - tex[index_tex++] = vert->texCoord[0] + sscroll; - tex[index_tex++] = vert->texCoord[1] + tscroll; - tex[index_tex++] = vert->lmTexCoord[0]; - tex[index_tex++] = vert->lmTexCoord[1]; - } - - // Polygon - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, sizeof(mvtx_t), surf->polys->verts->pos); - - // 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); + R_BufferVertex( vert->pos[0], vert->pos[1], vert->pos[2] ); + R_BufferMultiTex( vert->texCoord[0] + sscroll, vert->texCoord[1] + tscroll, + vert->lmTexCoord[0], vert->lmTexCoord[1] ); } - else - { - // Polygon - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, sizeof(mvtx_t), vert->pos); - - // Texture - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - qglClientActiveTexture(GL_TEXTURE0); - glTexCoordPointer(2, GL_FLOAT, sizeof(mvtx_t), vert->texCoord); - - // Lightmap - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - qglClientActiveTexture(GL_TEXTURE1); - glTexCoordPointer(2, GL_FLOAT, sizeof(mvtx_t), vert->lmTexCoord); - - // Draw it - glDrawArrays(GL_TRIANGLE_FAN, 0, nv); - } - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -static void -R_UploadDynamicLights(msurface_t *surf) -{ - int map, smax, tmax, size; - byte *temp; - - if ( !gl_config.multitexture || !R_HasDynamicLights(surf, &map) ) - { - return; - } - - smax = (surf->extents[0] >> surf->lmshift) + 1; - tmax = (surf->extents[1] >> surf->lmshift) + 1; - - size = smax * tmax * LIGHTMAP_BYTES; - temp = R_GetTemporaryLMBuffer(size); - - R_BuildLightMap(surf, (void *) temp, smax * LIGHTMAP_BYTES, - &r_newrefdef, r_modulate->value, r_framecount); - 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) */ @@ -859,17 +703,16 @@ R_DrawTextureChains(const 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) @@ -877,19 +720,20 @@ R_DrawTextureChains(const 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++) { @@ -902,13 +746,14 @@ R_DrawTextureChains(const 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(); } } @@ -920,7 +765,7 @@ R_DrawInlineBModel(const entity_t *currententity, const model_t *currentmodel) image_t *image; /* calculate dynamic lighting for bmodel */ - if (!r_flashblend->value) + if (!gl_config.multitexture && !r_flashblend->value) { R_PushDlights(&r_newrefdef, currentmodel->nodes + currentmodel->firstnode, r_dlightframecount, currentmodel->surfaces); @@ -962,20 +807,19 @@ R_DrawInlineBModel(const 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)) { @@ -1212,6 +1056,89 @@ 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; + float dot; + + if (!gl_config.multitexture || !r_drawentities->value || r_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() + R_PushDlights(&r_newrefdef, currentmodel->nodes + currentmodel->firstnode, + r_dlightframecount, currentmodel->surfaces); + + 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) { @@ -1240,6 +1167,7 @@ R_DrawWorld(void) RE_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 e8c76df8..9f77d6de 100644 --- a/src/client/refresh/gl1/gl1_warp.c +++ b/src/client/refresh/gl1/gl1_warp.c @@ -68,60 +68,30 @@ R_EmitWaterPolys(msurface_t *fa) { mpoly_t *p, *bp; mvtx_t *v; - int i; + int i, nv; float s, t, os, ot; float sscroll, tscroll; R_FlowingScroll(&r_newrefdef, fa->texinfo->flags, &sscroll, &tscroll); - // workaround for lack of VLAs (=> our workaround uses alloca() which is bad in loops) -#ifdef _MSC_VER - int maxNumVerts = 0; - for ( mpoly_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; i < p->numverts; i++, v++) { os = v->texCoord[0]; ot = v->texCoord[1]; - s = os + r_turbsin [ (int) ( ( ot * 0.125 + r_newrefdef.time ) * TURBSCALE ) & 255 ]; - s += sscroll; - tex[index_tex++] = s * ( 1.0 / 64 ); + s = os + r_turbsin [ (int) ( ( ot * 0.125 + r_newrefdef.time ) * TURBSCALE ) & 255 ] + sscroll; + t = ot + r_turbsin [ (int) ( ( os * 0.125 + r_newrefdef.time ) * TURBSCALE ) & 255 ] + tscroll; - t = ot + r_turbsin [ (int) ( ( os * 0.125 + r_newrefdef.time ) * TURBSCALE ) & 255 ]; - t += tscroll; - tex[index_tex++] = t * ( 1.0 / 64 ); + R_BufferVertex( v->pos[0], v->pos[1], v->pos[2] ); + R_BufferSingleTex( s * ( 1.0 / 64 ), t * ( 1.0 / 64 ) ); } - - v = p->verts; - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - glVertexPointer(3, GL_FLOAT, sizeof(mvtx_t), v->pos); - 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 0fc9879a..4913027a 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -39,14 +39,15 @@ #define GL_COLOR_INDEX8_EXT GL_COLOR_INDEX #endif -#define TEXNUM_LIGHTMAPS 1024 -#define TEXNUM_SCRAPS 1152 -#define TEXNUM_IMAGES 1153 +#define MAX_LIGHTMAPS 256 #define MAX_SCRAPS 1 +#define TEXNUM_LIGHTMAPS 1024 +#define TEXNUM_SCRAPS (TEXNUM_LIGHTMAPS + MAX_LIGHTMAPS) +#define TEXNUM_IMAGES (TEXNUM_SCRAPS + MAX_SCRAPS) #define BLOCK_WIDTH 256 // default values; now defined in glstate_t #define BLOCK_HEIGHT 256 #define REF_VERSION "Yamagi Quake II OpenGL Refresher" -#define MAX_LIGHTMAPS 256 +#define MAX_TEXTURE_UNITS 2 #define GL_LIGHTMAP_FORMAT GL_RGBA extern viddef_t vid; @@ -85,22 +86,23 @@ typedef struct image_s qboolean paletted; } image_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_TEXTURES]; extern int numgltextures; @@ -144,7 +146,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; @@ -211,7 +212,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); @@ -265,12 +266,23 @@ void R_TextureAlphaMode(const char *string); void R_TextureSolidMode(const 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__) @@ -352,7 +364,7 @@ typedef struct int lightmap_textures; - int currenttextures[2]; + int currenttextures[MAX_TEXTURE_UNITS]; int currenttmu; GLenum currenttarget;