From 1bae1e359f3c161bd0b3582c49e2cdfb111c5e03 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Sun, 14 Jul 2024 19:31:00 -0400 Subject: [PATCH 01/11] GL1: removed many fruitless glTexParameteri calls They were in this form: glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG/MIN_FILTER,GL_LINEAR) Only appeared at startup, but we didn't need so many of them. --- src/client/refresh/gl1/gl1_image.c | 24 ++++++++++-------------- src/client/refresh/gl1/header/local.h | 3 ++- 2 files changed, 12 insertions(+), 15 deletions(-) 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/header/local.h b/src/client/refresh/gl1/header/local.h index 7422f633..46f83f2e 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -229,7 +229,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); @@ -296,6 +296,7 @@ void glCheckError_(const char *file, const char *function, int line); // 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__) From 4503333b3a5c170a1388066a40d9c82daea35053 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Sun, 14 Jul 2024 20:20:27 -0400 Subject: [PATCH 02/11] GL1: lighting for brushes on the entity list... ...is being "preprocessed" before regenerating lightmaps, so there are no additional changes to the lightmaps when drawing those brushes. --- src/client/refresh/gl1/gl1_surf.c | 117 ++++++++++++++++++++------ src/client/refresh/gl1/header/local.h | 3 - 2 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/client/refresh/gl1/gl1_surf.c b/src/client/refresh/gl1/gl1_surf.c index 5283d3fb..5848756f 100644 --- a/src/client/refresh/gl1/gl1_surf.c +++ b/src/client/refresh/gl1/gl1_surf.c @@ -719,28 +719,6 @@ R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) glDisableClientState(GL_TEXTURE_COORD_ARRAY); } -static void -R_UploadDynamicLights(msurface_t *surf) -{ - int map, smax, tmax; - - if ( !gl_config.multitexture || !R_HasDynamicLights(surf, &map) ) - { - return; - } - 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) */ static void R_RegenAllLightmaps() @@ -928,7 +906,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,7 +951,7 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB)) { - R_UploadDynamicLights(psurf); + // Dynamic lighting already generated in R_GetBrushesLighting() R_EnableMultitexture(true); R_MBind(GL_TEXTURE0, image->texnum); R_RenderLightmappedPoly(currententity, psurf); @@ -1214,6 +1192,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 +1310,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/header/local.h b/src/client/refresh/gl1/header/local.h index 46f83f2e..2740df4a 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -108,9 +108,6 @@ typedef enum #include "model.h" -void GL_BeginRendering(int *x, int *y, int *width, int *height); -void GL_EndRendering(void); - void R_SetDefaultState(void); extern float gldepthmin, gldepthmax; From 50aebd2de45b8dbcf18e459b38796864534d0fc5 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 06:56:02 -0400 Subject: [PATCH 03/11] GL1 unified draw calls, init Implemented a batching procedure, to try to group meshes in a buffer and use a final GL call to draw them all in one step, instead of the many GL draw calls existing today. For now, only 2D textures are included, especifically console text ("conchars"), scrap and tiles. It's not worth doing this for individual 2D elements (e.g. crosshair). --- CMakeLists.txt | 1 + Makefile | 1 + src/client/refresh/gl1/gl1_buffer.c | 171 ++++++++++++++++++++++++++ src/client/refresh/gl1/gl1_draw.c | 63 +++------- src/client/refresh/gl1/gl1_sdl.c | 1 + src/client/refresh/gl1/header/local.h | 30 +++-- 6 files changed, 206 insertions(+), 61 deletions(-) create mode 100644 src/client/refresh/gl1/gl1_buffer.c 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/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c new file mode 100644 index 00000000..3e591e5e --- /dev/null +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -0,0 +1,171 @@ +/* + * 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; + + if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0) + { + return; + } + + // defaults for drawing + vtx_size = 3; + texture = true; + + // choosing features by type + switch (gl_buf.type) + { + case buf_2d: + vtx_size = 2; + break; + default: + break; + } + + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer (vtx_size, GL_FLOAT, 0, gl_buf.vtx); + + if (texture) + { + R_Bind(gl_buf.texture[0]); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, gl_buf.tex[0]); + } + + // 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 (texture) + { + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + } + + glDisableClientState( GL_VERTEX_ARRAY ); + + 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 ) + { + 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; +} 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_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/header/local.h b/src/client/refresh/gl1/header/local.h index 2740df4a..554b3b4a 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,19 +107,17 @@ typedef enum rserr_unknown } rserr_t; +typedef enum +{ + buf_2d +} buffered_draw_t; + #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; @@ -287,6 +286,11 @@ 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); + #ifdef DEBUG void glCheckError_(const char *file, const char *function, int line); // Ideally, the following list should contain all OpenGL calls. @@ -380,7 +384,7 @@ typedef struct int lightmap_textures; - int currenttextures[2]; + int currenttextures[MAX_TEXTURE_UNITS]; int currenttmu; GLenum currenttarget; From 4461128255ab24735d7110cb7fe99ac2479cef04 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 08:23:43 -0400 Subject: [PATCH 04/11] GL1 unified draw calls, cont. Batching procedure from previous commit, applied this time to GLPoly / single texture surfaces; alpha and liquid included. --- src/client/refresh/gl1/gl1_buffer.c | 116 ++++++++++++++++++++- src/client/refresh/gl1/gl1_surf.c | 145 +++++++------------------- src/client/refresh/gl1/gl1_warp.c | 45 ++------ src/client/refresh/gl1/header/local.h | 7 +- 4 files changed, 166 insertions(+), 147 deletions(-) diff --git a/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c index 3e591e5e..cbcbb5df 100644 --- a/src/client/refresh/gl1/gl1_buffer.c +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -61,16 +61,17 @@ R_ApplyGLBuffer(void) { // Properties of batched draws here GLint vtx_size; - qboolean texture; + qboolean texture, alpha, texenv_set; if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0) { return; } - // defaults for drawing + // defaults for drawing (mostly buf_singletex features) vtx_size = 3; texture = true; + alpha = texenv_set = false; // choosing features by type switch (gl_buf.type) @@ -78,10 +79,52 @@ R_ApplyGLBuffer(void) case buf_2d: vtx_size = 2; break; + case buf_alpha: + alpha = true; + break; default: break; } + 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); @@ -104,13 +147,20 @@ R_ApplyGLBuffer(void) glDisableClientState( GL_VERTEX_ARRAY ); + if (texenv_set) + { + R_TexEnv(GL_REPLACE); + } + 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 ) + if ( gl_buf.type != type || gl_buf.texture[0] != colortex || + (type == buf_singletex && gl_buf.flags != flags) || + (type == buf_alpha && gl_buf.alpha != alpha)) { R_ApplyGLBuffer(); @@ -169,3 +219,63 @@ R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy, 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; + 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; +} diff --git a/src/client/refresh/gl1/gl1_surf.c b/src/client/refresh/gl1/gl1_surf.c index 5848756f..d63d0ac9 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); @@ -842,12 +772,13 @@ 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 { @@ -872,7 +803,7 @@ R_DrawTextureChains(entity_t *currententity) } } - R_EnableMultitexture(false); + R_EnableMultitexture(false); // force disabling, SURF_DRAWTURB surfaces may not exist for (i = 0, image = gltextures; i < numgltextures; i++, image++) { @@ -885,13 +816,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(); } } @@ -958,13 +890,14 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) } else { - R_EnableMultitexture(false); - R_Bind(image->texnum); + R_UpdateGLBuffer(buf_singletex, image->texnum, 0, psurf->flags, 1); R_RenderBrushPoly(currententity, psurf); } } } } + R_EnableMultitexture(false); + R_ApplyGLBuffer(); if (!(currententity->flags & RF_TRANSLUCENT)) { 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 554b3b4a..b6a24e9b 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -109,7 +109,9 @@ typedef enum typedef enum { - buf_2d + buf_2d, + buf_singletex, + buf_alpha } buffered_draw_t; #include "model.h" @@ -290,6 +292,9 @@ 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); #ifdef DEBUG void glCheckError_(const char *file, const char *function, int line); From 749e70b9885ff61f32455ab9220115e6cfbbb1dc Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 09:05:21 -0400 Subject: [PATCH 05/11] GL1 unified draw calls, part 3 Batching / buffered procedure, applied to multitexture surfaces. This solves the issue with gl1_overbrightbits, when jumping between 0 and 2-4. --- src/client/refresh/gl1/gl1_buffer.c | 45 ++++++++++++++-- src/client/refresh/gl1/gl1_surf.c | 75 ++++----------------------- src/client/refresh/gl1/header/local.h | 2 + 3 files changed, 55 insertions(+), 67 deletions(-) diff --git a/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c index cbcbb5df..2632516f 100644 --- a/src/client/refresh/gl1/gl1_buffer.c +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -61,7 +61,7 @@ R_ApplyGLBuffer(void) { // Properties of batched draws here GLint vtx_size; - qboolean texture, alpha, texenv_set; + qboolean texture, mtex, alpha, texenv_set; if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0) { @@ -71,7 +71,7 @@ R_ApplyGLBuffer(void) // defaults for drawing (mostly buf_singletex features) vtx_size = 3; texture = true; - alpha = texenv_set = false; + mtex = alpha = texenv_set = false; // choosing features by type switch (gl_buf.type) @@ -79,6 +79,9 @@ R_ApplyGLBuffer(void) case buf_2d: vtx_size = 2; break; + case buf_mtex: + mtex = true; + break; case buf_alpha: alpha = true; break; @@ -86,6 +89,8 @@ R_ApplyGLBuffer(void) break; } + R_EnableMultitexture(mtex); + if (alpha) { // the textures are prescaled up for a better @@ -130,7 +135,27 @@ R_ApplyGLBuffer(void) if (texture) { - R_Bind(gl_buf.texture[0]); + 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]); @@ -159,6 +184,7 @@ 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 && gl_buf.flags != flags) || (type == buf_alpha && gl_buf.alpha != alpha)) { @@ -279,3 +305,16 @@ R_BufferSingleTex(GLfloat s, GLfloat t) 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; +} diff --git a/src/client/refresh/gl1/gl1_surf.c b/src/client/refresh/gl1/gl1_surf.c index d63d0ac9..25050188 100644 --- a/src/client/refresh/gl1/gl1_surf.c +++ b/src/client/refresh/gl1/gl1_surf.c @@ -573,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]; @@ -593,60 +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); + + for (i = 0; i < nv; i++, v += VERTEXSIZE) + { + R_BufferVertex( v[0], v[1], v[2] ); + R_BufferMultiTex( v[3] + scroll, v[4], v[5], v[6] ); + } } /* Upload dynamic lights to each lightmap texture (multitexture path only) */ @@ -782,8 +732,6 @@ R_DrawTextureChains(entity_t *currententity) } else // multitexture { - R_EnableMultitexture(true); - for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence || !image->texturechain) @@ -791,17 +739,18 @@ 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); // force disabling, SURF_DRAWTURB surfaces may not exist @@ -884,8 +833,7 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB)) { // Dynamic lighting already generated in R_GetBrushesLighting() - R_EnableMultitexture(true); - R_MBind(GL_TEXTURE0, image->texnum); + R_UpdateGLBuffer(buf_mtex, image->texnum, psurf->lightmaptexturenum, 0, 1); R_RenderLightmappedPoly(currententity, psurf); } else @@ -896,7 +844,6 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) } } } - R_EnableMultitexture(false); R_ApplyGLBuffer(); if (!(currententity->flags & RF_TRANSLUCENT)) diff --git a/src/client/refresh/gl1/header/local.h b/src/client/refresh/gl1/header/local.h index b6a24e9b..cb56a1df 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -111,6 +111,7 @@ typedef enum { buf_2d, buf_singletex, + buf_mtex, buf_alpha } buffered_draw_t; @@ -295,6 +296,7 @@ void R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy, 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); #ifdef DEBUG void glCheckError_(const char *file, const char *function, int line); From e8b2e36bd81020a3f306f460e382174724b6c2b1 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 10:18:46 -0400 Subject: [PATCH 06/11] GL1 unified draw calls, chapter iv Buffered flashblend effects. --- src/client/refresh/gl1/gl1_buffer.c | 31 +++++++++++++++++-- src/client/refresh/gl1/gl1_light.c | 43 ++++++++------------------- src/client/refresh/gl1/header/local.h | 4 ++- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c index 2632516f..4b48cf30 100644 --- a/src/client/refresh/gl1/gl1_buffer.c +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -61,7 +61,7 @@ R_ApplyGLBuffer(void) { // Properties of batched draws here GLint vtx_size; - qboolean texture, mtex, alpha, texenv_set; + qboolean texture, mtex, alpha, color, texenv_set; if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0) { @@ -71,7 +71,7 @@ R_ApplyGLBuffer(void) // defaults for drawing (mostly buf_singletex features) vtx_size = 3; texture = true; - mtex = alpha = texenv_set = false; + mtex = alpha = color = texenv_set = false; // choosing features by type switch (gl_buf.type) @@ -85,6 +85,10 @@ R_ApplyGLBuffer(void) case buf_alpha: alpha = true; break; + case buf_flash: + color = true; + texture = false; + break; default: break; } @@ -161,10 +165,21 @@ R_ApplyGLBuffer(void) 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 ); @@ -318,3 +333,15 @@ R_BufferMultiTex(GLfloat cs, GLfloat ct, GLfloat ls, GLfloat lt) 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_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/header/local.h b/src/client/refresh/gl1/header/local.h index cb56a1df..50545517 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -112,7 +112,8 @@ typedef enum buf_2d, buf_singletex, buf_mtex, - buf_alpha + buf_alpha, + buf_flash } buffered_draw_t; #include "model.h" @@ -297,6 +298,7 @@ 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); From 55dfaa2e3ba9be15b2b63239ea1b4e0a66cd9d45 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 11:20:26 -0400 Subject: [PATCH 07/11] GL1 unified draw calls, 05 Buffered 'alias' models. Logic from R_DrawAliasModel() and R_DrawAliasFrameLerp() was moved to R_ApplyGLBuffer(). --- src/client/refresh/gl1/gl1_buffer.c | 107 ++++++++++- src/client/refresh/gl1/gl1_mesh.c | 258 ++++++-------------------- src/client/refresh/gl1/header/local.h | 1 + 3 files changed, 162 insertions(+), 204 deletions(-) diff --git a/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c index 4b48cf30..f27e815e 100644 --- a/src/client/refresh/gl1/gl1_buffer.c +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -61,7 +61,8 @@ R_ApplyGLBuffer(void) { // Properties of batched draws here GLint vtx_size; - qboolean texture, mtex, alpha, color, texenv_set; + qboolean texture, mtex, alpha, color, alias, texenv_set; + float fovy, dist; if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0) { @@ -71,7 +72,7 @@ R_ApplyGLBuffer(void) // defaults for drawing (mostly buf_singletex features) vtx_size = 3; texture = true; - mtex = alpha = color = texenv_set = false; + mtex = alpha = color = alias = texenv_set = false; // choosing features by type switch (gl_buf.type) @@ -85,6 +86,9 @@ R_ApplyGLBuffer(void) case buf_alpha: alpha = true; break; + case buf_alias: + alias = color = true; + break; case buf_flash: color = true; texture = false; @@ -95,6 +99,53 @@ R_ApplyGLBuffer(void) 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 @@ -192,6 +243,39 @@ R_ApplyGLBuffer(void) 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; } @@ -200,7 +284,7 @@ R_UpdateGLBuffer(buffered_draw_t type, int colortex, int lighttex, int flags, fl { if ( gl_buf.type != type || gl_buf.texture[0] != colortex || (gl_config.multitexture && type == buf_mtex && gl_buf.texture[1] != lighttex) || - (type == buf_singletex && gl_buf.flags != flags) || + ((type == buf_singletex || type == buf_alias) && gl_buf.flags != flags) || (type == buf_alpha && gl_buf.alpha != alpha)) { R_ApplyGLBuffer(); @@ -285,6 +369,23 @@ R_SetBufferIndices(GLenum type, GLuint vertices_num) 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; diff --git a/src/client/refresh/gl1/gl1_mesh.c b/src/client/refresh/gl1/gl1_mesh.c index e4ab4f8c..78d15e60 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,127 +141,66 @@ 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); + } } } @@ -557,7 +483,6 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) } } - R_EnableMultitexture(false); paliashdr = (dmdl_t *)currentmodel->extradata; /* get lighting information */ @@ -685,7 +610,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 +631,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 +663,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 +686,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 +711,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/header/local.h b/src/client/refresh/gl1/header/local.h index 50545517..bf843387 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -113,6 +113,7 @@ typedef enum buf_singletex, buf_mtex, buf_alpha, + buf_alias, buf_flash } buffered_draw_t; From 24b546ba54726ad925de32e82256f6f4c318316a Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 12:33:19 -0400 Subject: [PATCH 08/11] GL1 unified draw calls, episode vi Buffered shadows. --- src/client/refresh/gl1/gl1_buffer.c | 1 + src/client/refresh/gl1/gl1_mesh.c | 49 ++++----------------------- src/client/refresh/gl1/header/local.h | 3 +- 3 files changed, 10 insertions(+), 43 deletions(-) diff --git a/src/client/refresh/gl1/gl1_buffer.c b/src/client/refresh/gl1/gl1_buffer.c index f27e815e..161ff500 100644 --- a/src/client/refresh/gl1/gl1_buffer.c +++ b/src/client/refresh/gl1/gl1_buffer.c @@ -91,6 +91,7 @@ R_ApplyGLBuffer(void) break; case buf_flash: color = true; + case buf_shadow: texture = false; break; default: diff --git a/src/client/refresh/gl1/gl1_mesh.c b/src/client/refresh/gl1/gl1_mesh.c index 78d15e60..f6ec39c2 100644 --- a/src/client/refresh/gl1/gl1_mesh.c +++ b/src/client/refresh/gl1/gl1_mesh.c @@ -207,8 +207,6 @@ R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp) 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; @@ -218,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) { @@ -226,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 */ @@ -258,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 */ @@ -282,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) diff --git a/src/client/refresh/gl1/header/local.h b/src/client/refresh/gl1/header/local.h index bf843387..cfa97c15 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -114,7 +114,8 @@ typedef enum buf_mtex, buf_alpha, buf_alias, - buf_flash + buf_flash, + buf_shadow } buffered_draw_t; #include "model.h" From 15a1ebdd740103f6de4e5b8d08409610a9ef114a Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Mon, 15 Jul 2024 12:45:15 -0400 Subject: [PATCH 09/11] Deleted gl1_biglightmaps glTexSubImage2D() calls are very slow, and are even slower when the texture is big. Dynamic lighting changes are small compared to the huge 512x512 size of the lightmap this option provided, so it was detrimental to performance. Original logic remains underneath if there's a need of a comeback. --- doc/040_cvarlist.md | 4 --- src/client/refresh/gl1/gl1_main.c | 36 ++++++--------------------- src/client/refresh/gl1/header/local.h | 1 - 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 0c0bba93..1cd8b87e 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_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/header/local.h b/src/client/refresh/gl1/header/local.h index cfa97c15..ada739b9 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -165,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; From 824cd9c034a52ca5d5513d806900c49d4d2dcf67 Mon Sep 17 00:00:00 2001 From: Yamagi Date: Sat, 20 Jul 2024 09:35:14 +0200 Subject: [PATCH 10/11] Update CHANGELOG for 'Group draw call in GL1.' --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3f1fc517..012c0646 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,7 +16,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 @@ -24,6 +23,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) From 528ffe6a6d5eff14e40f2d8261bd2c0125647a86 Mon Sep 17 00:00:00 2001 From: Yamagi Date: Sun, 21 Jul 2024 09:24:49 +0200 Subject: [PATCH 11/11] Add missing `R_ApplyGLBuffer()` to actually render the player menu. This fixes part of the player setup menu missing after the GL1 grouped drawcalls branch was merged. Submitted by @protocultor in https://github.com/yquake2/yquake2/pull/1124#issuecomment-2241272507 --- src/client/refresh/gl1/gl1_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client/refresh/gl1/gl1_main.c b/src/client/refresh/gl1/gl1_main.c index 4edfe024..ab30054a 100644 --- a/src/client/refresh/gl1/gl1_main.c +++ b/src/client/refresh/gl1/gl1_main.c @@ -1161,6 +1161,7 @@ R_SetLightLevel(entity_t *currententity) static void RI_RenderFrame(refdef_t *fd) { + R_ApplyGLBuffer(); // menu rendering when needed R_RenderView(fd); R_SetLightLevel (NULL); R_SetGL2D();