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).
This commit is contained in:
Jaime Moreira 2024-04-23 12:26:44 -04:00
parent 86528d7812
commit ba71af2af8
4 changed files with 173 additions and 47 deletions

View file

@ -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; i<MAX_LIGHTMAPS; i++)
{
if (gl_lms.lightmap_buffer[i])
{
free(gl_lms.lightmap_buffer[i]);
}
gl_lms.lightmap_buffer[i] = NULL;
}
r_framecount = 1; /* no dlightcache */
/* setup the base lightstyles so the lightmaps
@ -271,13 +278,28 @@ LM_BeginBuildingLightmaps(model_t *m)
gl_lms.current_lightmap_texture = 1;
gl_lms.internal_format = GL_LIGHTMAP_FORMAT;
if (gl_config.multitexture)
{
// alloc lightmap update buffer if needed
if (!gl_lms.lightmap_buffer[gl_lms.current_lightmap_texture]) {
gl_lms.lightmap_buffer[gl_lms.current_lightmap_texture] = malloc (lightmap_size);
}
return;
}
// dynamic lightmap for classic rendering path (no multitexture)
if (!gl_lms.lightmap_buffer[0]) {
gl_lms.lightmap_buffer[0] = malloc (lightmap_size);
memset (gl_lms.lightmap_buffer[0], 0, lightmap_size);
}
/* initialize the dynamic lightmap texture */
R_Bind(gl_state.lightmap_textures + 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, gl_lms.internal_format,
BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE, dummy);
GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer[0]);
}
void

View file

@ -325,7 +325,7 @@ R_BlendLightmaps(const model_t *currentmodel)
if (LM_AllocBlock(smax, tmax, &surf->dlight_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();

View file

@ -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;

View file

@ -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];