From 86528d7812278fe0e502206dd1bf694bd27c4bb1 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Tue, 23 Apr 2024 12:25:38 -0400 Subject: [PATCH 1/3] GL1 multitexture, first version Unlike the old, buggy one, this implementation follows the texture chain, just like the standard execution path. It also avoids doing the lightmap chains, since it has already done it in the second TMU; there's no duplicated work for lightmaps. No errors appear in the lava on the "boss1" map either. It's still slow when having an overdraw of dynamic lights. Further work is needed. --- doc/040_cvarlist.md | 6 + src/client/refresh/gl1/gl1_main.c | 1 + src/client/refresh/gl1/gl1_mesh.c | 1 + src/client/refresh/gl1/gl1_surf.c | 260 ++++++++++++++++++++++++++---- 4 files changed, 238 insertions(+), 30 deletions(-) diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 9bbca845..2cad40a8 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -464,6 +464,12 @@ Set `0` by default. value, at least `1.0` - default is `2.0`. Applied when textures are loaded, so it needs a `vid_restart`. +* **gl1_multitexture**: Enables (`1`) the blending of color and light + textures on a single drawing pass; disabling this (`0`) does one pass + for color and another for light. Default is `2`, which also enables + texture combine mode (`GL_ARB_texture_env_combine`) when supported. + Requires a `vid_restart` when changed. + * **gl1_overbrightbits**: Enables overbright bits, brightness scaling of lightmaps and models. Higher values make shadows less dark. Possible values are `0` (no overbright bits), `1` (more correct lighting for diff --git a/src/client/refresh/gl1/gl1_main.c b/src/client/refresh/gl1/gl1_main.c index 6fcda10d..2a1028d5 100644 --- a/src/client/refresh/gl1/gl1_main.c +++ b/src/client/refresh/gl1/gl1_main.c @@ -164,6 +164,7 @@ R_DrawSpriteModel(entity_t *currententity, const model_t *currentmodel) dsprite_t *psprite; image_t *skin; + R_EnableMultitexture(false); /* don't even bother culling, because it's just a single polygon without a surface cache */ psprite = (dsprite_t *)currentmodel->extradata; diff --git a/src/client/refresh/gl1/gl1_mesh.c b/src/client/refresh/gl1/gl1_mesh.c index e7b257b1..1c3f5d13 100644 --- a/src/client/refresh/gl1/gl1_mesh.c +++ b/src/client/refresh/gl1/gl1_mesh.c @@ -557,6 +557,7 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel) } } + R_EnableMultitexture(false); paliashdr = (dmdl_t *)currentmodel->extradata; /* get lighting information */ diff --git a/src/client/refresh/gl1/gl1_surf.c b/src/client/refresh/gl1/gl1_surf.c index db7bc8c3..2795603b 100644 --- a/src/client/refresh/gl1/gl1_surf.c +++ b/src/client/refresh/gl1/gl1_surf.c @@ -238,13 +238,8 @@ R_BlendLightmaps(const model_t *currentmodel) int i; msurface_t *surf, *newdrawsurf = 0; - /* don't bother if we're set to fullbright */ - if (r_fullbright->value) - { - return; - } - - if (!r_worldmodel->lightdata) + /* don't bother if we're set to fullbright or multitexture is enabled */ + if (gl_config.multitexture || r_fullbright->value || !r_worldmodel->lightdata) { return; } @@ -466,6 +461,11 @@ R_RenderBrushPoly(entity_t *currententity, msurface_t *fa) R_DrawGLPoly(fa->polys); } + if (gl_config.multitexture) + { + return; // lighting already done at this point for mtex + } + /* check for lightmap modification */ for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) { @@ -589,6 +589,149 @@ R_DrawAlphaSurfaces(void) r_alpha_surfaces = NULL; } +static qboolean +R_HasDynamicLights(msurface_t *surf, int *mapNum) +{ + int map; + qboolean is_dynamic = false; + + if ( r_fullbright->value || !gl1_dynamic->value || + (surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) ) + { + return false; + } + + // Any dynamic lights on this surface? + for (map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++) + { + if (r_newrefdef.lightstyles[surf->styles[map]].white != surf->cached_light[map]) + { + is_dynamic = true; + break; + } + } + + if ( !is_dynamic && surf->dlightframe == r_framecount ) + { + is_dynamic = true; + } + + if (mapNum) + { + *mapNum = map; + } + return is_dynamic; +} + +static void +R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) +{ + int i, map, smax, tmax; + int nv = surf->polys->numverts; + float scroll; + float *v; + unsigned lmtex = surf->lightmaptexturenum; + unsigned temp[128 * 128]; + + if ( R_HasDynamicLights(surf, &map) ) + { + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + R_BuildLightMap(surf, (void *) temp, smax * 4); + + // Dynamic lights on a surface + if (((surf->styles[map] >= 32) || (surf->styles[map] == 0)) && (surf->dlightframe != r_framecount)) + { + R_SetCacheState(surf); + } + else // Normal dynamic lights + { + lmtex = 0; + } + + R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex); + glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, + tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp); + } + else + { + R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex); + } + + // 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]; + + if (surf->texinfo->flags & SURF_FLOWING) + { + scroll = -64 * ((r_newrefdef.time / 40.0) - (int) (r_newrefdef.time / 40.0)); + + if (scroll == 0.0) + { + scroll = -64.0; + } + + YQ2_VLA(GLfloat, tex, 4 * nv); + unsigned int index_tex = 0; + + for (i = 0; i < nv; i++, v += VERTEXSIZE) + { + tex[index_tex++] = v[3] + scroll; + tex[index_tex++] = v[4]; + tex[index_tex++] = v[5]; + tex[index_tex++] = v[6]; + } + v = surf->polys->verts[0]; + + // Polygon + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, VERTEXSIZE * sizeof(GLfloat), v); + + // Texture + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + qglClientActiveTexture(GL_TEXTURE0); + glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), tex); + + // Lightmap + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + qglClientActiveTexture(GL_TEXTURE1); + glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), tex + 2); + + // Draw the thing + glDrawArrays(GL_TRIANGLE_FAN, 0, nv); + + YQ2_VLAFREE(tex); + } + else + { + // 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); + } + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + static void R_DrawTextureChains(entity_t *currententity) { @@ -598,32 +741,78 @@ R_DrawTextureChains(entity_t *currententity) c_visible_textures = 0; - for (i = 0, image = gltextures; i < numgltextures; i++, image++) + if (!gl_config.multitexture) // classic path { - if (!image->registration_sequence) + for (i = 0, image = gltextures; i < numgltextures; i++, image++) { - continue; + if (!image->registration_sequence) + { + continue; + } + + s = image->texturechain; + + if (!s) + { + continue; + } + + c_visible_textures++; + + for ( ; s; s = s->texturechain) + { + R_Bind(image->texnum); // may reset because of dynamic lighting in R_RenderBrushPoly + R_RenderBrushPoly(currententity, s); + } + + image->texturechain = NULL; } - - s = image->texturechain; - - if (!s) - { - continue; - } - - c_visible_textures++; - - for ( ; s; s = s->texturechain) - { - R_Bind(image->texnum); // may reset because of dynamic lighting in R_RenderBrushPoly - R_RenderBrushPoly(currententity, s); - } - - image->texturechain = NULL; } + else // multitexture + { + R_EnableMultitexture(true); - R_TexEnv(GL_REPLACE); + for (i = 0, image = gltextures; i < numgltextures; i++, image++) + { + if (!image->registration_sequence || !image->texturechain) + { + 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_RenderLightmappedPoly(currententity, s); + } + } + } + + R_EnableMultitexture(false); + + for (i = 0, image = gltextures; i < numgltextures; i++, image++) + { + if (!image->registration_sequence || !image->texturechain) + { + continue; + } + + R_Bind(image->texnum); // no danger of resetting in R_RenderBrushPoly + + for (s = image->texturechain; s; s = s->texturechain) + { + if (s->flags & SURF_DRAWTURB) + { + R_RenderBrushPoly(currententity, s); + } + } + + image->texturechain = NULL; + } + } } static void @@ -679,8 +868,19 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) else { image = R_TextureAnimation(currententity, psurf->texinfo); - R_Bind(image->texnum); - R_RenderBrushPoly(currententity, psurf); + + if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB)) + { + R_EnableMultitexture(true); + R_MBind(GL_TEXTURE0, image->texnum); + R_RenderLightmappedPoly(currententity, psurf); + } + else + { + R_EnableMultitexture(false); + R_Bind(image->texnum); + R_RenderBrushPoly(currententity, psurf); + } } } } From ba71af2af83fc2536cc9e773d0b925baa0cf02a3 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Tue, 23 Apr 2024 12:26:44 -0400 Subject: [PATCH 2/3] GL1 multitexture: fast dynamic lighting Using MH's solution, which is keeping all lightmaps in memory to modify and upload them as a batch when possible. lightmap_buffer is now an array; index 0 is used as the legacy lightmap buffer (when no mtex), and the rest of the indexes are to store the different lightmaps (only when using mtex). --- src/client/refresh/gl1/gl1_lightmap.c | 54 ++++++--- src/client/refresh/gl1/gl1_surf.c | 163 +++++++++++++++++++++----- src/client/refresh/gl1/header/local.h | 2 +- src/client/refresh/gl1/header/model.h | 1 + 4 files changed, 173 insertions(+), 47 deletions(-) diff --git a/src/client/refresh/gl1/gl1_lightmap.c b/src/client/refresh/gl1/gl1_lightmap.c index 5e81fc4e..6ff92ff5 100644 --- a/src/client/refresh/gl1/gl1_lightmap.c +++ b/src/client/refresh/gl1/gl1_lightmap.c @@ -35,23 +35,19 @@ void LM_InitBlock(void) { memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated)); + + if (gl_config.multitexture && !gl_lms.lightmap_buffer[gl_lms.current_lightmap_texture]) { + gl_lms.lightmap_buffer[gl_lms.current_lightmap_texture] = malloc (BLOCK_WIDTH*BLOCK_HEIGHT*LIGHTMAP_BYTES); + } } void LM_UploadBlock(qboolean dynamic) { - int texture; + const int texture = (dynamic)? 0 : gl_lms.current_lightmap_texture; + const int buffer = (gl_config.multitexture)? gl_lms.current_lightmap_texture : 0; int height = 0; - if (dynamic) - { - texture = 0; - } - else - { - texture = gl_lms.current_lightmap_texture; - } - R_Bind(gl_state.lightmap_textures + texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -70,14 +66,14 @@ LM_UploadBlock(qboolean dynamic) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, height, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, - gl_lms.lightmap_buffer); + gl_lms.lightmap_buffer[buffer]); } else { gl_lms.internal_format = GL_LIGHTMAP_FORMAT; glTexImage2D(GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, - GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer); + GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer[buffer]); if (++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS) { @@ -207,7 +203,7 @@ LM_BuildPolygonFromSurface(model_t *currentmodel, msurface_t *fa) void LM_CreateSurfaceLightmap(msurface_t *surf) { - int smax, tmax; + int smax, tmax, buffer; byte *base; if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) @@ -231,8 +227,9 @@ LM_CreateSurfaceLightmap(msurface_t *surf) } surf->lightmaptexturenum = gl_lms.current_lightmap_texture; + buffer = (gl_config.multitexture)? surf->lightmaptexturenum : 0; - base = gl_lms.lightmap_buffer; + base = gl_lms.lightmap_buffer[buffer]; base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES; R_SetCacheState(surf); @@ -242,12 +239,22 @@ LM_CreateSurfaceLightmap(msurface_t *surf) void LM_BeginBuildingLightmaps(model_t *m) { + static const unsigned int lightmap_size = BLOCK_WIDTH*BLOCK_HEIGHT*LIGHTMAP_BYTES; static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; int i; - unsigned dummy[128 * 128] = {0}; memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated)); + // free lightmap update buffers + for (i=0; idlight_s, &surf->dlight_t)) { - base = gl_lms.lightmap_buffer; + base = gl_lms.lightmap_buffer[0]; base += (surf->dlight_t * BLOCK_WIDTH + surf->dlight_s) * LIGHTMAP_BYTES; @@ -371,7 +371,7 @@ R_BlendLightmaps(const model_t *currentmodel) smax, tmax); } - base = gl_lms.lightmap_buffer; + base = gl_lms.lightmap_buffer[0]; base += (surf->dlight_t * BLOCK_WIDTH + surf->dlight_s) * LIGHTMAP_BYTES; @@ -611,7 +611,8 @@ R_HasDynamicLights(msurface_t *surf, int *mapNum) } } - if ( !is_dynamic && surf->dlightframe == r_framecount ) + // No matter if it is this frame or was in the previous one: has dynamic lights + if ( !is_dynamic && (surf->dlightframe == r_framecount || surf->dirty_lightmap) ) { is_dynamic = true; } @@ -623,40 +624,26 @@ R_HasDynamicLights(msurface_t *surf, int *mapNum) return is_dynamic; } +static void +R_UpdateSurfCache(msurface_t *surf, int map) +{ + if ( ((surf->styles[map] >= 32) || (surf->styles[map] == 0)) + && (surf->dlightframe != r_framecount) ) + { + R_SetCacheState(surf); + } + surf->dirty_lightmap = (surf->dlightframe == r_framecount); +} + static void R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) { - int i, map, smax, tmax; + int i; int nv = surf->polys->numverts; float scroll; float *v; - unsigned lmtex = surf->lightmaptexturenum; - unsigned temp[128 * 128]; - if ( R_HasDynamicLights(surf, &map) ) - { - smax = (surf->extents[0] >> 4) + 1; - tmax = (surf->extents[1] >> 4) + 1; - R_BuildLightMap(surf, (void *) temp, smax * 4); - - // Dynamic lights on a surface - if (((surf->styles[map] >= 32) || (surf->styles[map] == 0)) && (surf->dlightframe != r_framecount)) - { - R_SetCacheState(surf); - } - else // Normal dynamic lights - { - lmtex = 0; - } - - R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex); - glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, - tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp); - } - else - { - R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex); - } + R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + surf->lightmaptexturenum); // Apply overbrightbits to TMU 1 (lightmap) if (gl1_overbrightbits->value) @@ -732,6 +719,114 @@ R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) glDisableClientState(GL_TEXTURE_COORD_ARRAY); } +static void +R_UploadDynamicLights(msurface_t *surf) +{ + int map, smax, tmax; + byte temp[BLOCK_WIDTH * BLOCK_HEIGHT]; + + if ( !gl_config.multitexture || !R_HasDynamicLights(surf, &map) ) + { + return; + } + + 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); +} + +/* Upload dynamic lights to each lightmap texture (multitexture path only) */ +static void +R_RegenAllLightmaps() +{ + int i, map, smax, tmax, top, bottom, left, right, bt, bb, bl, br; + qboolean affected_lightmap; + msurface_t *surf; + byte *base; + + if ( !gl_config.multitexture ) + { + return; + } + + glPixelStorei(GL_UNPACK_ROW_LENGTH, BLOCK_WIDTH); + + for (i = 1; i < MAX_LIGHTMAPS; i++) + { + if (!gl_lms.lightmap_surfaces[i] || !gl_lms.lightmap_buffer[i]) + { + continue; + } + + affected_lightmap = false; + bt = BLOCK_HEIGHT; + bl = BLOCK_WIDTH; + bb = br = 0; + + for (surf = gl_lms.lightmap_surfaces[i]; + surf != 0; + surf = surf->lightmapchain) + { + if ( !R_HasDynamicLights(surf, &map) ) + { + continue; + } + + affected_lightmap = true; + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + + left = surf->light_s; + right = surf->light_s + smax; + top = surf->light_t; + bottom = surf->light_t + tmax; + + base = gl_lms.lightmap_buffer[i]; + base += (top * BLOCK_WIDTH + left) * LIGHTMAP_BYTES; + + R_BuildLightMap(surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES); + R_UpdateSurfCache(surf, map); + + if (left < bl) + { + bl = left; + } + if (right > br) + { + br = right; + } + if (top < bt) + { + bt = top; + } + if (bottom > bb) + { + bb = bottom; + } + } + + if (!affected_lightmap) + { + continue; + } + + base = gl_lms.lightmap_buffer[i]; + base += (bt * BLOCK_WIDTH + bl) * LIGHTMAP_BYTES; + + // upload changes + R_Bind(gl_state.lightmap_textures + i); + glTexSubImage2D(GL_TEXTURE_2D, 0, bl, bt, br - bl, bb - bt, + GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, base); + } + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +} + static void R_DrawTextureChains(entity_t *currententity) { @@ -871,6 +966,7 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB)) { + R_UploadDynamicLights(psurf); R_EnableMultitexture(true); R_MBind(GL_TEXTURE0, image->texnum); R_RenderLightmappedPoly(currententity, psurf); @@ -1098,6 +1194,12 @@ R_RecursiveWorldNode(entity_t *currententity, mnode_t *node) image = R_TextureAnimation(currententity, surf->texinfo); surf->texturechain = image->texturechain; image->texturechain = surf; + + if (gl_config.multitexture && !(surf->texinfo->flags & SURF_WARP)) // needed for R_RegenAllLightmaps() + { + surf->lightmapchain = gl_lms.lightmap_surfaces[surf->lightmaptexturenum]; + gl_lms.lightmap_surfaces[surf->lightmaptexturenum] = surf; + } } } @@ -1133,6 +1235,7 @@ R_DrawWorld(void) R_ClearSkyBox(); R_RecursiveWorldNode(&ent, r_worldmodel->nodes); + R_RegenAllLightmaps(); R_DrawTextureChains(&ent); R_BlendLightmaps(r_worldmodel); R_DrawSkyBox(); diff --git a/src/client/refresh/gl1/header/local.h b/src/client/refresh/gl1/header/local.h index 0e5be2d8..1e60b092 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -403,7 +403,7 @@ typedef struct /* the lightmap texture data needs to be kept in main memory so texsubimage can update properly */ - byte lightmap_buffer[4 * BLOCK_WIDTH * BLOCK_HEIGHT]; + byte *lightmap_buffer[MAX_LIGHTMAPS]; } gllightmapstate_t; extern glconfig_t gl_config; diff --git a/src/client/refresh/gl1/header/model.h b/src/client/refresh/gl1/header/model.h index 36d57dfe..2eda331d 100644 --- a/src/client/refresh/gl1/header/model.h +++ b/src/client/refresh/gl1/header/model.h @@ -65,6 +65,7 @@ typedef struct msurface_s /* lighting info */ int dlightframe; int dlightbits; + qboolean dirty_lightmap; // lightmap has dynamic lights from previous frame (mtex only) int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; From b81e9109291d13323e6cfaacaf45ebd2924b77f1 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Wed, 24 Apr 2024 19:43:24 -0400 Subject: [PATCH 3/3] GL1 multitexture, refactored memory allocation It was missing error checking and cleanup at shutdown. Also, reset state when needed. Fixes "death by laser" in boss1 map. --- src/client/refresh/gl1/gl1_lightmap.c | 61 ++++++++++++++++++--------- src/client/refresh/gl1/gl1_main.c | 5 +++ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/client/refresh/gl1/gl1_lightmap.c b/src/client/refresh/gl1/gl1_lightmap.c index 6ff92ff5..2ee2dedc 100644 --- a/src/client/refresh/gl1/gl1_lightmap.c +++ b/src/client/refresh/gl1/gl1_lightmap.c @@ -31,13 +31,48 @@ extern gllightmapstate_t gl_lms; void R_SetCacheState(msurface_t *surf); void R_BuildLightMap(msurface_t *surf, byte *dest, int stride); +void +LM_FreeLightmapBuffers(void) +{ + for (int i=0; iorigin, shadelight); } + R_EnableMultitexture(false); glPushMatrix(); R_RotateForEntity(currententity); @@ -1654,6 +1657,7 @@ RI_Shutdown(void) ri.Cmd_RemoveCommand("imagelist"); ri.Cmd_RemoveCommand("gl_strings"); + LM_FreeLightmapBuffers(); Mod_FreeAll(); R_ShutdownImages(); @@ -1909,6 +1913,7 @@ R_DrawBeam(entity_t *e) VectorAdd(start_points[i], direction, end_points[i]); } + R_EnableMultitexture(false); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glDepthMask(GL_FALSE);