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_image.c b/src/client/refresh/gl1/gl1_image.c index 196667a4..f8303713 100644 --- a/src/client/refresh/gl1/gl1_image.c +++ b/src/client/refresh/gl1/gl1_image.c @@ -144,6 +144,30 @@ R_SetTexturePalette(unsigned palette[256]) } } +void +R_SelectTexture(GLenum texture) +{ + int tmu; + + if (!gl_config.multitexture) + { + return; + } + + tmu = texture - GL_TEXTURE0; + + if (tmu == gl_state.currenttmu) + { + return; + } + + gl_state.currenttmu = tmu; + gl_state.currenttarget = texture; + + qglActiveTexture(texture); + qglClientActiveTexture(texture); +} + void R_TexEnv(GLenum mode) { @@ -151,7 +175,7 @@ R_TexEnv(GLenum mode) if (mode != lastmodes[gl_state.currenttmu]) { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode); // FIXME: shouldn't this be glTexEnvi() ? + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode); lastmodes[gl_state.currenttmu] = mode; } } @@ -175,6 +199,93 @@ R_Bind(int texnum) glBindTexture(GL_TEXTURE_2D, texnum); } +void +R_MBind(GLenum target, int texnum) +{ + const int tmu = target - GL_TEXTURE0; + + if (target != gl_state.currenttarget) + { + R_SelectTexture(target); + } + + if (gl_state.currenttextures[tmu] == texnum) + { + return; + } + + R_Bind(texnum); +} + +void +R_EnableMultitexture(qboolean enable) +{ + static qboolean active; + + if (!gl_config.multitexture || enable == active) + { + return; // current state is the right one + } + + active = enable; + R_SelectTexture(GL_TEXTURE1); + + if (active && !r_fullbright->value) + { + glEnable(GL_TEXTURE_2D); + + if (gl_config.mtexcombine) + { + R_TexEnv(GL_COMBINE); + + if (gl_lightmap->value) + { + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + } + else + { + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS); + } + + R_SelectTexture(GL_TEXTURE0); + R_TexEnv(GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + return; + } + else + { + if (gl_lightmap->value) + { + R_TexEnv(GL_REPLACE); + } + else + { + R_TexEnv(GL_MODULATE); + } + } + + } + else // disable multitexturing + { + glDisable(GL_TEXTURE_2D); + R_TexEnv(GL_REPLACE); + } + + R_SelectTexture(GL_TEXTURE0); + R_TexEnv(GL_REPLACE); +} + void R_TextureMode(char *string) { diff --git a/src/client/refresh/gl1/gl1_lightmap.c b/src/client/refresh/gl1/gl1_lightmap.c index 5e81fc4e..2ee2dedc 100644 --- a/src/client/refresh/gl1/gl1_lightmap.c +++ b/src/client/refresh/gl1/gl1_lightmap.c @@ -31,27 +31,58 @@ 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; iflags & (SURF_DRAWSKY | SURF_DRAWTURB)) @@ -231,8 +262,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); @@ -244,9 +276,9 @@ LM_BeginBuildingLightmaps(model_t *m) { static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; int i; - unsigned dummy[128 * 128] = {0}; memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated)); + LM_FreeLightmapBuffers(); r_framecount = 1; /* no dlightcache */ @@ -271,13 +303,22 @@ LM_BeginBuildingLightmaps(model_t *m) gl_lms.current_lightmap_texture = 1; gl_lms.internal_format = GL_LIGHTMAP_FORMAT; + 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_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, - GL_UNSIGNED_BYTE, dummy); + GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer[0]); } void diff --git a/src/client/refresh/gl1/gl1_main.c b/src/client/refresh/gl1/gl1_main.c index a328f16e..9f95e83b 100644 --- a/src/client/refresh/gl1/gl1_main.c +++ b/src/client/refresh/gl1/gl1_main.c @@ -90,6 +90,7 @@ cvar_t *gl1_particle_square; cvar_t *gl1_palettedtexture; cvar_t *gl1_pointparameters; +cvar_t *gl1_multitexture; cvar_t *gl_drawbuffer; cvar_t *gl_lightmap; @@ -143,6 +144,8 @@ cvar_t *gl1_stereo_convergence; refimport_t ri; +void LM_FreeLightmapBuffers(void); + void R_RotateForEntity(entity_t *e) { @@ -163,6 +166,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; @@ -259,6 +263,7 @@ R_DrawNullModel(entity_t *currententity) R_LightPoint(currententity, currententity->origin, shadelight); } + R_EnableMultitexture(false); glPushMatrix(); R_RotateForEntity(currententity); @@ -1211,6 +1216,7 @@ 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", "2", CVAR_ARCHIVE); gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); @@ -1468,17 +1474,29 @@ RI_Init(void) /* Point parameters */ R_Printf(PRINT_ALL, " - Point parameters: "); - if (strstr(gl_config.extensions_string, "GL_ARB_point_parameters")) + if ( strstr(gl_config.extensions_string, "GL_ARB_point_parameters") || + strstr(gl_config.extensions_string, "GL_EXT_point_parameters") ) // should exist for all OGL 1.4 hw... { - qglPointParameterfARB = (void (APIENTRY *)(GLenum, GLfloat))RI_GetProcAddress ( "glPointParameterfARB" ); - qglPointParameterfvARB = (void (APIENTRY *)(GLenum, const GLfloat *))RI_GetProcAddress ( "glPointParameterfvARB" ); + qglPointParameterf = (void (APIENTRY *)(GLenum, GLfloat))RI_GetProcAddress ( "glPointParameterf" ); + qglPointParameterfv = (void (APIENTRY *)(GLenum, const GLfloat *))RI_GetProcAddress ( "glPointParameterfv" ); + + if (!qglPointParameterf || !qglPointParameterfv) + { + qglPointParameterf = (void (APIENTRY *)(GLenum, GLfloat))RI_GetProcAddress ( "glPointParameterfARB" ); + qglPointParameterfv = (void (APIENTRY *)(GLenum, const GLfloat *))RI_GetProcAddress ( "glPointParameterfvARB" ); + } + if (!qglPointParameterf || !qglPointParameterfv) + { + qglPointParameterf = (void (APIENTRY *)(GLenum, GLfloat))RI_GetProcAddress ( "glPointParameterfEXT" ); + qglPointParameterfv = (void (APIENTRY *)(GLenum, const GLfloat *))RI_GetProcAddress ( "glPointParameterfvEXT" ); + } } gl_config.pointparameters = false; if (gl1_pointparameters->value) { - if (qglPointParameterfARB && qglPointParameterfvARB) + if (qglPointParameterf && qglPointParameterfv) { gl_config.pointparameters = true; R_Printf(PRINT_ALL, "Okay\n"); @@ -1562,6 +1580,65 @@ RI_Init(void) // ---- + /* Multitexturing */ + gl_config.multitexture = gl_config.mtexcombine = false; + + R_Printf(PRINT_ALL, " - Multitexturing: "); + + if (strstr(gl_config.extensions_string, "GL_ARB_multitexture")) + { + qglActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glActiveTexture"); + qglClientActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glClientActiveTexture"); + + if (!qglActiveTexture || !qglClientActiveTexture) + { + qglActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glActiveTextureARB"); + qglClientActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glClientActiveTextureARB"); + } + } + + if (gl1_multitexture->value) + { + if (qglActiveTexture && qglClientActiveTexture) + { + gl_config.multitexture = true; + R_Printf(PRINT_ALL, "Okay\n"); + } + else + { + R_Printf(PRINT_ALL, "Failed\n"); + } + } + else + { + R_Printf(PRINT_ALL, "Disabled\n"); + } + + // ---- + + /* Multi texturing combine */ + R_Printf(PRINT_ALL, " - Multitexturing combine: "); + + if ( ( strstr(gl_config.extensions_string, "GL_ARB_texture_env_combine") + || strstr(gl_config.extensions_string, "GL_EXT_texture_env_combine") ) ) + { + if (gl_config.multitexture && gl1_multitexture->value > 1) + { + gl_config.mtexcombine = true; + R_Printf(PRINT_ALL, "Okay\n"); + } + else + { + R_Printf(PRINT_ALL, "Disabled\n"); + } + } + else + { + R_Printf(PRINT_ALL, "Failed\n"); + } + + // ---- + R_SetDefaultState(); R_InitImages(); @@ -1580,6 +1657,7 @@ RI_Shutdown(void) ri.Cmd_RemoveCommand("imagelist"); ri.Cmd_RemoveCommand("gl_strings"); + LM_FreeLightmapBuffers(); Mod_FreeAll(); R_ShutdownImages(); @@ -1835,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); 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_misc.c b/src/client/refresh/gl1/gl1_misc.c index 9f52e6d2..1885b0af 100644 --- a/src/client/refresh/gl1/gl1_misc.c +++ b/src/client/refresh/gl1/gl1_misc.c @@ -208,9 +208,9 @@ R_SetDefaultState(void) attenuations[1] = gl1_particle_att_b->value; attenuations[2] = gl1_particle_att_c->value; - qglPointParameterfARB(GL_POINT_SIZE_MIN_EXT, gl1_particle_min_size->value); - qglPointParameterfARB(GL_POINT_SIZE_MAX_EXT, gl1_particle_max_size->value); - qglPointParameterfvARB(GL_DISTANCE_ATTENUATION_EXT, attenuations); + qglPointParameterf(GL_POINT_SIZE_MIN, gl1_particle_min_size->value); + qglPointParameterf(GL_POINT_SIZE_MAX, gl1_particle_max_size->value); + qglPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, attenuations); /* GL_POINT_SMOOTH is not implemented by some OpenGL drivers, especially the crappy Mesa3D backends like diff --git a/src/client/refresh/gl1/gl1_sdl.c b/src/client/refresh/gl1/gl1_sdl.c index ab1543bf..7623ea32 100644 --- a/src/client/refresh/gl1/gl1_sdl.c +++ b/src/client/refresh/gl1/gl1_sdl.c @@ -33,12 +33,6 @@ #include #endif -#if defined(__APPLE__) -#include -#else -#include -#endif - static SDL_Window* window = NULL; static SDL_GLContext context = NULL; qboolean IsHighDPIaware = false; diff --git a/src/client/refresh/gl1/gl1_surf.c b/src/client/refresh/gl1/gl1_surf.c index ac67df9d..e60d78c2 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; } @@ -294,8 +289,8 @@ R_BlendLightmaps(const model_t *currentmodel) // Apply overbright bits to the static lightmaps if (gl1_overbrightbits->value) { - R_TexEnv(GL_COMBINE_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl1_overbrightbits->value); + R_TexEnv(GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value); } R_DrawGLPolyChain(surf->polys, 0, 0); @@ -330,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; @@ -353,8 +348,8 @@ R_BlendLightmaps(const model_t *currentmodel) // Apply overbright bits to the dynamic lightmaps if (gl1_overbrightbits->value) { - R_TexEnv(GL_COMBINE_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl1_overbrightbits->value); + R_TexEnv(GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value); } R_DrawGLPolyChain(drawsurf->polys, @@ -376,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; @@ -397,8 +392,8 @@ R_BlendLightmaps(const model_t *currentmodel) // Apply overbright bits to the remainder lightmaps if (gl1_overbrightbits->value) { - R_TexEnv(GL_COMBINE_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl1_overbrightbits->value); + R_TexEnv(GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value); } R_DrawGLPolyChain(surf->polys, @@ -418,17 +413,12 @@ static void R_RenderBrushPoly(entity_t *currententity, msurface_t *fa) { int maps; - image_t *image; qboolean is_dynamic = false; c_brush_polys++; - image = R_TextureAnimation(currententity, fa->texinfo); - if (fa->flags & SURF_DRAWTURB) { - R_Bind(image->texnum); - /* 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 @@ -444,8 +434,8 @@ R_RenderBrushPoly(entity_t *currententity, msurface_t *fa) They oversaturate otherwise. */ if (gl1_overbrightbits->value) { - R_TexEnv(GL_COMBINE_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1); + R_TexEnv(GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1); } else { @@ -459,12 +449,8 @@ R_RenderBrushPoly(entity_t *currententity, msurface_t *fa) return; } - else - { - R_Bind(image->texnum); - R_TexEnv(GL_REPLACE); - } + R_TexEnv(GL_REPLACE); if (fa->texinfo->flags & SURF_FLOWING) { @@ -475,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++) { @@ -598,6 +589,244 @@ 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; + } + } + + // 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); + } + surf->dirty_lightmap = (surf->dlightframe == r_framecount); +} + +static void +R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) +{ + int i; + int nv = surf->polys->numverts; + 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]; + + 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_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) { @@ -607,31 +836,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_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 @@ -642,6 +918,7 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) float dot; msurface_t *psurf; dlight_t *lt; + image_t *image; /* calculate dynamic lighting for bmodel */ if (!gl1_flashblend->value) @@ -685,14 +962,27 @@ R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) } else { - R_RenderBrushPoly(currententity, psurf); + 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)) { - R_BlendLightmaps(currentmodel); } else @@ -768,8 +1058,6 @@ R_DrawBrushModel(entity_t *currententity, const model_t *currentmodel) currententity->angles[0] = -currententity->angles[0]; currententity->angles[2] = -currententity->angles[2]; - R_TexEnv(GL_REPLACE); - if (gl_lightmap->value) { R_TexEnv(GL_REPLACE); @@ -906,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; + } } } @@ -917,7 +1211,6 @@ void R_DrawWorld(void) { entity_t ent; - const model_t *currentmodel; if (!r_drawworld->value) { @@ -929,8 +1222,6 @@ R_DrawWorld(void) return; } - currentmodel = r_worldmodel; - VectorCopy(r_newrefdef.vieworg, modelorg); /* auto cycle the world frame for texture animation */ @@ -944,8 +1235,9 @@ R_DrawWorld(void) R_ClearSkyBox(); R_RecursiveWorldNode(&ent, r_worldmodel->nodes); + R_RegenAllLightmaps(); R_DrawTextureChains(&ent); - R_BlendLightmaps(currentmodel); + R_BlendLightmaps(r_worldmodel); R_DrawSkyBox(); R_DrawTriangleOutlines(); } @@ -1045,4 +1337,3 @@ R_MarkLeaves(void) } } } - diff --git a/src/client/refresh/gl1/header/local.h b/src/client/refresh/gl1/header/local.h index e98e9655..1e60b092 100644 --- a/src/client/refresh/gl1/header/local.h +++ b/src/client/refresh/gl1/header/local.h @@ -39,24 +39,6 @@ #define GL_COLOR_INDEX8_EXT GL_COLOR_INDEX #endif -#ifndef GL_EXT_texture_filter_anisotropic - #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE - #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF -#endif - -#ifndef GL_VERSION_1_3 - #define GL_TEXTURE0 0x84C0 - #define GL_TEXTURE1 0x84C1 -#endif - -#ifndef GL_MULTISAMPLE -#define GL_MULTISAMPLE 0x809D -#endif - -#ifndef GL_MULTISAMPLE_FILTER_HINT_NV -#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 -#endif - #define TEXNUM_LIGHTMAPS 1024 #define TEXNUM_SCRAPS 1152 #define TEXNUM_IMAGES 1153 @@ -180,6 +162,7 @@ extern cvar_t *gl1_overbrightbits; extern cvar_t *gl1_palettedtexture; extern cvar_t *gl1_pointparameters; +extern cvar_t *gl1_multitexture; extern cvar_t *gl1_particle_min_size; extern cvar_t *gl1_particle_max_size; @@ -248,6 +231,9 @@ void R_TranslatePlayerSkin(int playernum); void R_Bind(int texnum); void R_TexEnv(GLenum value); +void R_SelectTexture(GLenum); +void R_MBind(GLenum target, int texnum); +void R_EnableMultitexture(qboolean enable); void R_LightPoint(entity_t *currententity, vec3_t p, vec3_t color); void R_PushDlights(void); @@ -377,6 +363,8 @@ typedef struct qboolean npottextures; qboolean palettedtexture; qboolean pointparameters; + qboolean multitexture; + qboolean mtexcombine; // ---- @@ -415,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]; diff --git a/src/client/refresh/gl1/header/qgl.h b/src/client/refresh/gl1/header/qgl.h index 81f5ea47..cf4a04c2 100644 --- a/src/client/refresh/gl1/header/qgl.h +++ b/src/client/refresh/gl1/header/qgl.h @@ -44,54 +44,39 @@ #define APIENTRY #endif -#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +// Extracted from +#ifndef GL_VERSION_1_4 +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#endif -#define GL_POINT_SIZE_MIN_EXT 0x8126 -#define GL_POINT_SIZE_MAX_EXT 0x8127 -#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +#ifndef GL_VERSION_1_3 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_MULTISAMPLE 0x809D +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_RGB_SCALE 0x8573 +#define GL_PREVIOUS 0x8578 +#endif -#ifndef GL_EXT_texture_env_combine -#define GL_COMBINE_EXT 0x8570 -#define GL_COMBINE_RGB_EXT 0x8571 -#define GL_COMBINE_ALPHA_EXT 0x8572 -#define GL_RGB_SCALE_EXT 0x8573 -#define GL_ADD_SIGNED_EXT 0x8574 -#define GL_INTERPOLATE_EXT 0x8575 -#define GL_CONSTANT_EXT 0x8576 -#define GL_PRIMARY_COLOR_EXT 0x8577 -#define GL_PREVIOUS_EXT 0x8578 -#define GL_SOURCE0_RGB_EXT 0x8580 -#define GL_SOURCE1_RGB_EXT 0x8581 -#define GL_SOURCE2_RGB_EXT 0x8582 -#define GL_SOURCE3_RGB_EXT 0x8583 -#define GL_SOURCE4_RGB_EXT 0x8584 -#define GL_SOURCE5_RGB_EXT 0x8585 -#define GL_SOURCE6_RGB_EXT 0x8586 -#define GL_SOURCE7_RGB_EXT 0x8587 -#define GL_SOURCE0_ALPHA_EXT 0x8588 -#define GL_SOURCE1_ALPHA_EXT 0x8589 -#define GL_SOURCE2_ALPHA_EXT 0x858A -#define GL_SOURCE3_ALPHA_EXT 0x858B -#define GL_SOURCE4_ALPHA_EXT 0x858C -#define GL_SOURCE5_ALPHA_EXT 0x858D -#define GL_SOURCE6_ALPHA_EXT 0x858E -#define GL_SOURCE7_ALPHA_EXT 0x858F -#define GL_OPERAND0_RGB_EXT 0x8590 -#define GL_OPERAND1_RGB_EXT 0x8591 -#define GL_OPERAND2_RGB_EXT 0x8592 -#define GL_OPERAND3_RGB_EXT 0x8593 -#define GL_OPERAND4_RGB_EXT 0x8594 -#define GL_OPERAND5_RGB_EXT 0x8595 -#define GL_OPERAND6_RGB_EXT 0x8596 -#define GL_OPERAND7_RGB_EXT 0x8597 -#define GL_OPERAND0_ALPHA_EXT 0x8598 -#define GL_OPERAND1_ALPHA_EXT 0x8599 -#define GL_OPERAND2_ALPHA_EXT 0x859A -#define GL_OPERAND3_ALPHA_EXT 0x859B -#define GL_OPERAND4_ALPHA_EXT 0x859C -#define GL_OPERAND5_ALPHA_EXT 0x859D -#define GL_OPERAND6_ALPHA_EXT 0x859E -#define GL_OPERAND7_ALPHA_EXT 0x859F +#ifndef GL_EXT_shared_texture_palette +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#ifndef GL_NV_multisample_filter_hint +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 #endif // ======================================================================= @@ -99,7 +84,7 @@ /* * This is responsible for setting up our QGL extension pointers */ -qboolean QGL_Init ( void ); +void QGL_Init ( void ); /* * Unloads the specified DLL then nulls out all the proc pointers. @@ -107,10 +92,12 @@ qboolean QGL_Init ( void ); void QGL_Shutdown ( void ); /* GL extensions */ -extern void ( APIENTRY *qglPointParameterfARB ) ( GLenum param, GLfloat value ); -extern void ( APIENTRY *qglPointParameterfvARB ) ( GLenum param, +extern void ( APIENTRY *qglPointParameterf ) ( GLenum param, GLfloat value ); +extern void ( APIENTRY *qglPointParameterfv ) ( GLenum param, const GLfloat *value ); extern void ( APIENTRY *qglColorTableEXT ) ( GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid * ); +extern void ( APIENTRY *qglActiveTexture ) ( GLenum texture ); +extern void ( APIENTRY *qglClientActiveTexture ) ( GLenum texture ); #endif diff --git a/src/client/refresh/gl1/qgl.c b/src/client/refresh/gl1/qgl.c index 78d991e7..913283a9 100644 --- a/src/client/refresh/gl1/qgl.c +++ b/src/client/refresh/gl1/qgl.c @@ -38,18 +38,22 @@ /* * GL extensions */ -void (APIENTRY *qglPointParameterfARB)(GLenum param, GLfloat value); -void (APIENTRY *qglPointParameterfvARB)(GLenum param, const GLfloat *value); +void (APIENTRY *qglPointParameterf)(GLenum param, GLfloat value); +void (APIENTRY *qglPointParameterfv)(GLenum param, const GLfloat *value); void (APIENTRY *qglColorTableEXT)(GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); +void (APIENTRY *qglActiveTexture) (GLenum texture); +void (APIENTRY *qglClientActiveTexture) (GLenum texture); /* ========================================================================= */ void QGL_EXT_Reset ( void ) { - qglPointParameterfARB = NULL; - qglPointParameterfvARB = NULL; - qglColorTableEXT = NULL; + qglPointParameterf = NULL; + qglPointParameterfv = NULL; + qglColorTableEXT = NULL; + qglActiveTexture = NULL; + qglClientActiveTexture = NULL; } /* ========================================================================= */ @@ -63,11 +67,10 @@ QGL_Shutdown ( void ) /* ========================================================================= */ -qboolean +void QGL_Init (void) { // Reset GL extension pointers QGL_EXT_Reset(); - return true; }