GL1 multitexture

Merge changes from https://github.com/yquake2/yquake2/pull/1103
Merge remote-tracking branch 'yquake2/master'
This commit is contained in:
Denis Pauk 2024-05-12 12:55:53 +03:00
commit 33a3aed03e
7 changed files with 409 additions and 53 deletions

View file

@ -488,6 +488,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

View file

@ -28,25 +28,56 @@
extern gllightmapstate_t gl_lms;
void
LM_FreeLightmapBuffers(void)
{
for (int i=0; i<MAX_LIGHTMAPS; i++)
{
if (gl_lms.lightmap_buffer[i])
{
free(gl_lms.lightmap_buffer[i]);
}
gl_lms.lightmap_buffer[i] = NULL;
}
}
static void
LM_AllocLightmapBuffer(int buffer, qboolean clean)
{
static const unsigned int lightmap_size =
BLOCK_WIDTH * BLOCK_HEIGHT * LIGHTMAP_BYTES;
if (!gl_lms.lightmap_buffer[buffer])
{
gl_lms.lightmap_buffer[buffer] = malloc (lightmap_size);
}
if (!gl_lms.lightmap_buffer[buffer])
{
ri.Sys_Error(ERR_FATAL, "Could not allocate lightmap buffer %d\n",
buffer);
}
if (clean)
{
memset (gl_lms.lightmap_buffer[buffer], 0, lightmap_size);
}
}
void
LM_InitBlock(void)
{
memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated));
if (gl_config.multitexture)
{
LM_AllocLightmapBuffer(gl_lms.current_lightmap_texture, false);
}
}
void
LM_UploadBlock(qboolean dynamic)
{
int texture;
if (dynamic)
{
texture = 0;
}
else
{
texture = gl_lms.current_lightmap_texture;
}
const int texture = (dynamic)? 0 : gl_lms.current_lightmap_texture;
const int buffer = (gl_config.multitexture)? gl_lms.current_lightmap_texture : 0;
R_Bind(gl_state.lightmap_textures + texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@ -67,13 +98,13 @@ 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
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_LIGHTMAP_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)
{
@ -231,7 +262,7 @@ LM_BuildPolygonFromSurface(model_t *currentmodel, msurface_t *fa)
static void
LM_CreateSurfaceLightmap(msurface_t *surf)
{
int smax, tmax;
int smax, tmax, buffer;
byte *base;
if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB))
@ -256,8 +287,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, &r_newrefdef);
@ -284,13 +316,10 @@ void
LM_BeginBuildingLightmaps(model_t *m)
{
static lightstyle_t lightstyles[MAX_LIGHTSTYLES];
int i, size;
byte *dummy;
size = BLOCK_WIDTH * BLOCK_HEIGHT * LIGHTMAP_BYTES;
dummy = R_GetTemporaryLMBuffer(size);
int i;
memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated));
LM_FreeLightmapBuffers();
r_framecount = 1; /* no dlightcache */
@ -314,13 +343,22 @@ LM_BeginBuildingLightmaps(model_t *m)
gl_lms.current_lightmap_texture = 1;
if (gl_config.multitexture)
{
LM_AllocLightmapBuffer(gl_lms.current_lightmap_texture, false);
return;
}
// dynamic lightmap for classic rendering path (no multitexture)
LM_AllocLightmapBuffer(0, true);
/* 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_LIGHTMAP_FORMAT,
BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT,
GL_UNSIGNED_BYTE, dummy);
GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer[0]);
}
void

View file

@ -145,6 +145,8 @@ cvar_t *gl1_stereo_convergence;
refimport_t ri;
void LM_FreeLightmapBuffers(void);
void
R_RotateForEntity(entity_t *e)
{
@ -165,6 +167,7 @@ R_DrawSpriteModel(entity_t *currententity, const model_t *currentmodel)
vec3_t point[4];
float *up, *right;
R_EnableMultitexture(false);
/* don't even bother culling, because it's just
a single polygon without a surface cache */
psprite = (dsprite_t *)currentmodel->extradata;
@ -263,6 +266,7 @@ R_DrawNullModel(entity_t *currententity)
shadelight, r_modulate->value, lightspot);
}
R_EnableMultitexture(false);
glPushMatrix();
R_RotateForEntity(currententity);
@ -1669,6 +1673,7 @@ RI_Shutdown(void)
ri.Cmd_RemoveCommand("imagelist");
ri.Cmd_RemoveCommand("gl_strings");
LM_FreeLightmapBuffers();
Mod_FreeAll();
R_ShutdownImages();
@ -1926,6 +1931,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);

View file

@ -440,6 +440,7 @@ R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel)
}
}
R_EnableMultitexture(false);
paliashdr = (dmdx_t *)currentmodel->extradata;
/* get lighting information */

View file

@ -225,13 +225,8 @@ R_BlendLightmaps(const model_t *currentmodel)
int i;
msurface_t *surf;
/* 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;
}
@ -319,7 +314,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;
@ -366,7 +361,7 @@ R_BlendLightmaps(const model_t *currentmodel)
__func__, smax, tmax);
}
base = gl_lms.lightmap_buffer;
base = gl_lms.lightmap_buffer[0];
base += (surf->dlight_t * BLOCK_WIDTH +
surf->dlight_s) * LIGHTMAP_BYTES;
@ -457,6 +452,11 @@ R_RenderBrushPoly(const 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++)
{
@ -583,6 +583,245 @@ 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 || !r_dynamic->value ||
(surf->texinfo->flags & (SURF_SKY | SURF_TRANSPARENT | 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;
}
}
// 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;
}
if (mapNum)
{
*mapNum = map;
}
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, &r_newrefdef);
}
surf->dirty_lightmap = (surf->dlightframe == r_framecount);
}
static void
R_RenderLightmappedPoly(const entity_t *currententity, msurface_t *surf)
{
int i;
int nv = surf->polys->numverts;
mvtx_t* vert;
R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + surf->lightmaptexturenum);
// Apply overbrightbits to TMU 1 (lightmap)
if (gl1_overbrightbits->value)
{
R_TexEnv(GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value);
}
c_brush_polys++;
vert = surf->polys->verts;
if (surf->texinfo->flags & SURF_FLOWING)
{
float sscroll, tscroll;
R_FlowingScroll(&r_newrefdef, surf->texinfo->flags, &sscroll, &tscroll);
YQ2_VLA(GLfloat, tex, 4 * nv);
unsigned int index_tex = 0;
for (i = 0; i < nv; i++, vert++)
{
tex[index_tex++] = vert->texCoord[0] + sscroll;
tex[index_tex++] = vert->texCoord[1] + tscroll;
tex[index_tex++] = vert->lmTexCoord[0];
tex[index_tex++] = vert->lmTexCoord[1];
}
// Polygon
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(mvtx_t), surf->polys->verts->pos);
// Texture
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), tex);
// Lightmap
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE1);
glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), tex + 2);
// Draw the thing
glDrawArrays(GL_TRIANGLE_FAN, 0, nv);
YQ2_VLAFREE(tex);
}
else
{
// Polygon
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(mvtx_t), vert->pos);
// Texture
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE0);
glTexCoordPointer(2, GL_FLOAT, sizeof(mvtx_t), vert->texCoord);
// Lightmap
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
qglClientActiveTexture(GL_TEXTURE1);
glTexCoordPointer(2, GL_FLOAT, sizeof(mvtx_t), vert->lmTexCoord);
// Draw it
glDrawArrays(GL_TRIANGLE_FAN, 0, nv);
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
static void
R_UploadDynamicLights(msurface_t *surf)
{
int map, smax, tmax, size;
byte *temp;
if ( !gl_config.multitexture || !R_HasDynamicLights(surf, &map) )
{
return;
}
smax = (surf->extents[0] >> surf->lmshift) + 1;
tmax = (surf->extents[1] >> surf->lmshift) + 1;
size = smax * tmax * LIGHTMAP_BYTES;
temp = R_GetTemporaryLMBuffer(size);
R_BuildLightMap(surf, (void *) temp, smax * LIGHTMAP_BYTES,
&r_newrefdef, r_modulate->value, r_framecount);
R_UpdateSurfCache(surf, map);
R_Bind(gl_state.lightmap_textures + surf->lightmaptexturenum);
glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax,
tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp);
}
/* 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] >> surf->lmshift) + 1;
tmax = (surf->extents[1] >> surf->lmshift) + 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_newrefdef, r_modulate->value, r_framecount);
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(const entity_t *currententity)
{
@ -592,6 +831,8 @@ R_DrawTextureChains(const entity_t *currententity)
c_visible_textures = 0;
if (!gl_config.multitexture) // classic path
{
for (i = 0, image = gltextures; i < numgltextures; i++, image++)
{
if (!image->registration_sequence)
@ -616,8 +857,52 @@ R_DrawTextureChains(const entity_t *currententity)
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
@ -667,11 +952,23 @@ R_DrawInlineBModel(const entity_t *currententity, const model_t *currentmodel)
else
{
image = R_TextureAnimation(currententity, psurf->texinfo);
if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB))
{
R_UploadDynamicLights(psurf);
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);
}
}
}
}
if (!(currententity->flags & RF_TRANSLUCENT))
{
@ -895,6 +1192,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;
}
}
}
@ -930,6 +1233,7 @@ R_DrawWorld(void)
RE_ClearSkyBox();
R_RecursiveWorldNode(&ent, r_worldmodel->nodes);
R_RegenAllLightmaps();
R_DrawTextureChains(&ent);
R_BlendLightmaps(r_worldmodel);
R_DrawSkyBox();

View file

@ -377,7 +377,7 @@ typedef struct
/* the lightmap texture data needs to be kept in
main memory so texsubimage can update properly */
byte lightmap_buffer[LIGHTMAP_BYTES * BLOCK_WIDTH * BLOCK_HEIGHT];
byte *lightmap_buffer[MAX_LIGHTMAPS];
} gllightmapstate_t;
void LM_CreateLightmapsPoligon(model_t *currentmodel, msurface_t *fa);

View file

@ -268,6 +268,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];