X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=gl_rmain.c;h=3efa46d2042499b24b106bef687b5505d42512fc;hp=2e2db42446095a53872568be42ebcd4e106f49d9;hb=58c33270c280aafe85a1eacb224efdd9138fdb19;hpb=00969f81f23de7332fb49cd42bce9f2b03bff676 diff --git a/gl_rmain.c b/gl_rmain.c index 2e2db424..3efa46d2 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -256,6 +256,10 @@ cvar_t r_buffermegs[R_BUFFERDATA_COUNT] = {CF_CLIENT | CF_ARCHIVE, "r_buffermegs_uniform", "0.25", "uniform buffer size for one frame"}, }; +cvar_t r_q1bsp_lightmap_updates_enabled = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_enabled", "1", "allow lightmaps to be updated on Q1BSP maps (don't turn this off except for debugging)"}; +cvar_t r_q1bsp_lightmap_updates_combine = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_combine", "2", "combine lightmap texture updates to make fewer glTexSubImage2D calls, modes: 0 = immediately upload lightmaps (may be thousands of small 3x3 updates), 1 = combine to one call, 2 = combine to one full texture update (glTexImage2D) which tells the driver it does not need to lock the resource (faster on most drivers)"}; +cvar_t r_q1bsp_lightmap_updates_hidden_surfaces = {CF_CLIENT | CF_ARCHIVE, "r_q1bsp_lightmap_updates_hidden_surfaces", "0", "update lightmaps on surfaces that are not visible, so that updates only occur on frames where lightstyles changed value (animation or light switches), only makes sense with combine = 2"}; + extern cvar_t v_glslgamma_2d; extern qbool v_flipped_state; @@ -511,8 +515,8 @@ static void R_BuildFogTexture(void) } if (r_texture_fogattenuation) { - R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1); - //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1); + R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1, 0); + //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1, 0); } else { @@ -843,9 +847,10 @@ enum SHADERSTATICPARM_SHADOWSAMPLER = 10, ///< sampler SHADERSTATICPARM_CELSHADING = 11, ///< celshading (alternative diffuse and specular math) SHADERSTATICPARM_CELOUTLINES = 12, ///< celoutline (depth buffer analysis to produce outlines) - SHADERSTATICPARM_FXAA = 13 ///< fast approximate anti aliasing + SHADERSTATICPARM_FXAA = 13, ///< fast approximate anti aliasing + SHADERSTATICPARM_COLORFRINGE = 14 ///< colorfringe (chromatic aberration) }; -#define SHADERSTATICPARMS_COUNT 14 +#define SHADERSTATICPARMS_COUNT 15 static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT]; static int shaderstaticparms_count = 0; @@ -894,6 +899,8 @@ qbool R_CompileShader_CheckStaticParms(void) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_CELSHADING); if (r_celoutlines.integer) R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_CELOUTLINES); + if (r_colorfringe.value) + R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_COLORFRINGE); return memcmp(r_compileshader_staticparms, r_compileshader_staticparms_save, sizeof(r_compileshader_staticparms)) != 0; } @@ -922,6 +929,7 @@ static void R_CompileShader_AddStaticParms(unsigned int mode, uint64_t permutati R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELSHADING, "USECELSHADING"); R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELOUTLINES, "USECELOUTLINES"); R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_FXAA, "USEFXAA"); + R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_COLORFRINGE, "USECOLORFRINGE"); } /// information about each possible shader permutation @@ -1553,7 +1561,7 @@ static int R_BlendFuncFlags(int src, int dst) return r; } -void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdiffuse[3], const float rtlightspecular[3], rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist, void *surfacewaterplane, qbool notrippy) +void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdiffuse[3], const float rtlightspecular[3], rsurfacepass_t rsurfacepass, int texturenumsurfaces, const msurface_t **texturesurfacelist, void *surfacewaterplane, qbool notrippy, qbool ui) { // select a permutation of the lighting shader appropriate to this // combination of texture, entity, light source, and fogging, only use the @@ -1820,7 +1828,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif // lightmapped wall if ((t->glowtexture || t->backgroundglowtexture) && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer) permutation |= SHADERPERMUTATION_GLOW; - if (r_refdef.fogenabled && !notrippy) + if (r_refdef.fogenabled && !ui) permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE); if (t->colormapping) permutation |= SHADERPERMUTATION_COLORMAPPING; @@ -1892,7 +1900,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif } if(!(blendfuncflags & BLENDFUNC_ALLOWS_ANYFOG)) permutation &= ~(SHADERPERMUTATION_FOGHEIGHTTEXTURE | SHADERPERMUTATION_FOGOUTSIDE | SHADERPERMUTATION_FOGINSIDE); - if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACKALPHA && !notrippy) + if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACKALPHA && !ui) permutation |= SHADERPERMUTATION_FOGALPHAHACK; switch(vid.renderpath) { @@ -1954,7 +1962,7 @@ void R_SetupShader_Surface(const float rtlightambient[3], const float rtlightdif if (r_glsl_permutation->loc_DeferredMod_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_DeferredMod_Specular, t->render_rtlight_specular[0], t->render_rtlight_specular[1], t->render_rtlight_specular[2]); } // additive passes are only darkened by fog, not tinted - if (r_glsl_permutation->loc_FogColor >= 0 && !notrippy) + if (r_glsl_permutation->loc_FogColor >= 0 && !ui) { if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0) qglUniform3f(r_glsl_permutation->loc_FogColor, 0, 0, 0); @@ -3189,7 +3197,7 @@ static void gl_main_shutdown(void) r_texture_numcubemaps = 0; //r_texture_fogintensity = NULL; memset(&r_fb, 0, sizeof(r_fb)); - R_GLSL_Restart_f(&cmd_local); + R_GLSL_Restart_f(cmd_local); r_glsl_permutation = NULL; memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash)); @@ -3409,6 +3417,9 @@ void GL_Main_Init(void) for (i = 0;i < R_BUFFERDATA_COUNT;i++) Cvar_RegisterVariable(&r_buffermegs[i]); Cvar_RegisterVariable(&r_batch_dynamicbuffer); + Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_enabled); + Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_combine); + Cvar_RegisterVariable(&r_q1bsp_lightmap_updates_hidden_surfaces); if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE) Cvar_SetValue(&cvars_all, "r_fullbrights", 0); #ifdef DP_MOBILETOUCH @@ -3438,104 +3449,44 @@ void Render_Init(void) Mod_RenderInit(); } -int R_CullBox(const vec3_t mins, const vec3_t maxs) +static void R_GetCornerOfBox(vec3_t out, const vec3_t mins, const vec3_t maxs, int signbits) { - int i; - mplane_t *p; - if (r_trippy.integer) - return false; - for (i = 0;i < r_refdef.view.numfrustumplanes;i++) - { - p = r_refdef.view.frustum + i; - switch(p->signbits) - { - default: - case 0: - if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 1: - if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 2: - if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 3: - if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 4: - if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - case 5: - if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - case 6: - if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - case 7: - if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - } - } - return false; + out[0] = ((signbits & 1) ? mins : maxs)[0]; + out[1] = ((signbits & 2) ? mins : maxs)[1]; + out[2] = ((signbits & 4) ? mins : maxs)[2]; } -int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes) +static qbool _R_CullBox(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes, int ignore) { int i; const mplane_t *p; + vec3_t corner; if (r_trippy.integer) return false; for (i = 0;i < numplanes;i++) { + if(i == ignore) + continue; p = planes + i; - switch(p->signbits) - { - default: - case 0: - if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 1: - if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 2: - if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 3: - if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*maxs[2] < p->dist) - return true; - break; - case 4: - if (p->normal[0]*maxs[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - case 5: - if (p->normal[0]*mins[0] + p->normal[1]*maxs[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - case 6: - if (p->normal[0]*maxs[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - case 7: - if (p->normal[0]*mins[0] + p->normal[1]*mins[1] + p->normal[2]*mins[2] < p->dist) - return true; - break; - } + R_GetCornerOfBox(corner, mins, maxs, p->signbits); + if (DotProduct(p->normal, corner) < p->dist) + return true; } return false; } +qbool R_CullFrustum(const vec3_t mins, const vec3_t maxs) +{ + // skip nearclip plane, it often culls portals when you are very close, and is almost never useful + return _R_CullBox(mins, maxs, r_refdef.view.numfrustumplanes, r_refdef.view.frustum, 4); +} + +qbool R_CullBox(const vec3_t mins, const vec3_t maxs, int numplanes, const mplane_t *planes) +{ + // nothing to ignore + return _R_CullBox(mins, maxs, numplanes, planes, -1); +} + //================================================================================== // LadyHavoc: this stores temporary data used within the same frame @@ -4077,7 +4028,7 @@ static void R_View_UpdateEntityVisible (void) continue; } if (!(ent->flags & renderimask)) - if (!R_CullBox(ent->mins, ent->maxs) || (ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE))) + if (!R_CullFrustum(ent->mins, ent->maxs) || (ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE))) if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_WORLDOBJECT | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs)) r_refdef.viewcache.entityvisible[i] = true; } @@ -4089,7 +4040,7 @@ static void R_View_UpdateEntityVisible (void) { ent = r_refdef.scene.entities[i]; if (!(ent->flags & renderimask)) - if (!R_CullBox(ent->mins, ent->maxs) || (ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE))) + if (!R_CullFrustum(ent->mins, ent->maxs) || (ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE))) r_refdef.viewcache.entityvisible[i] = true; } } @@ -4249,7 +4200,7 @@ static void R_View_SetFrustum(const int *scissor) int i; double fpx = +1, fnx = -1, fpy = +1, fny = -1; vec3_t forward, left, up, origin, v; - if(r_lockvisibility.integer || r_lockpvs.integer) + if(r_lockvisibility.integer) return; if(scissor) { @@ -5577,7 +5528,7 @@ void R_UpdateVariables(void) } if (r_texture_gammaramps) { - R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1); + R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1, 0); } else { @@ -5681,7 +5632,7 @@ void R_RenderView(int fbo, rtexture_t *depthtexture, rtexture_t *colortexture, i rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity if(R_CompileShader_CheckStaticParms()) - R_GLSL_Restart_f(&cmd_local); + R_GLSL_Restart_f(cmd_local); if (!r_drawentities.integer) r_refdef.scene.numentities = 0; @@ -8734,7 +8685,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface { // render screenspace normalmap to texture GL_DepthMask(true); - R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist, NULL, false); + R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist, NULL, false, false); RSurf_DrawBatch(); return; } @@ -8762,18 +8713,18 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface { // render water or distortion background GL_DepthMask(true); - R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false); + R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BACKGROUND, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false, false); RSurf_DrawBatch(); // blend surface on top GL_DepthMask(false); - R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, NULL, false); + R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, NULL, false, false); RSurf_DrawBatch(); } else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)) { // render surface with reflection texture as input GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED)); - R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false); + R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, end-start, texturesurfacelist + start, (void *)(r_fb.water.waterplanes + startplaneindex), false, false); RSurf_DrawBatch(); } } @@ -8782,7 +8733,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface // render surface batch normally GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED)); - R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist, NULL, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) != 0 || ui); + R_SetupShader_Surface(vec3_origin, vec3_origin, vec3_origin, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist, NULL, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) != 0 || ui, ui); RSurf_DrawBatch(); } @@ -9308,11 +9259,8 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor model_t *model; const msurface_t *surface; const msurface_t *surfaces; - const int *surfacelist; const texture_t *texture; int numtriangles; - int numsurfacelist; - int surfacelistindex; int surfaceindex; int triangleindex; float localorigin[3]; @@ -9405,8 +9353,6 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor #endif dynamic = model->surfmesh.isanimated; - numsurfacelist = model->nummodelsurfaces; - surfacelist = model->sortedmodelsurfaces; surfaces = model->data_surfaces; bih = NULL; @@ -9442,9 +9388,8 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor } else { - for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++) + for (surfaceindex = model->submodelsurfaces_start;surfaceindex < model->submodelsurfaces_end;surfaceindex++) { - surfaceindex = surfacelist[surfacelistindex]; surface = surfaces + surfaceindex; // check cull box first because it rejects more than any other check if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs)) @@ -9770,7 +9715,7 @@ static void R_DrawModelDecals(void) static void R_DrawDebugModel(void) { entity_render_t *ent = rsurface.entity; - int i, j, flagsmask; + int j, flagsmask; const msurface_t *surface; model_t *model = ent->model; @@ -9786,10 +9731,11 @@ static void R_DrawDebugModel(void) GL_DepthMask(false); GL_DepthRange(0, 1); GL_BlendFunc(GL_ONE, GL_ONE); - for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++) + for (j = model->submodelsurfaces_start;j < model->submodelsurfaces_end;j++) { if (ent == r_refdef.scene.worldentity && !r_refdef.viewcache.world_surfacevisible[j]) continue; + surface = model->data_surfaces + j; rsurface.texture = R_GetCurrentTexture(surface->texture); if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles) { @@ -9829,7 +9775,7 @@ static void R_DrawDebugModel(void) GL_PolygonOffset(r_refdef.polygonfactor + r_showcollisionbrushes_polygonfactor.value, r_refdef.polygonoffset + r_showcollisionbrushes_polygonoffset.value); for (bihleafindex = 0, bihleaf = bih->leafs;bihleafindex < bih->numleafs;bihleafindex++, bihleaf++) { - if (cullbox && R_CullBox(bihleaf->mins, bihleaf->maxs)) + if (cullbox && R_CullFrustum(bihleaf->mins, bihleaf->maxs)) continue; switch (bihleaf->type) { @@ -9880,10 +9826,11 @@ static void R_DrawDebugModel(void) GL_DepthMask(true); } qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);CHECKGLERROR - for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++) + for (j = model->submodelsurfaces_start; j < model->submodelsurfaces_end; j++) { if (ent == r_refdef.scene.worldentity && !r_refdef.viewcache.world_surfacevisible[j]) continue; + surface = model->data_surfaces + j; rsurface.texture = R_GetCurrentTexture(surface->texture); if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles) { @@ -9918,10 +9865,11 @@ static void R_DrawDebugModel(void) GL_BlendFunc(GL_ONE, GL_ZERO); GL_DepthMask(true); } - for (i = 0, j = model->firstmodelsurface, surface = model->data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++) + for (j = model->submodelsurfaces_start; j < model->submodelsurfaces_end; j++) { if (ent == r_refdef.scene.worldentity && !r_refdef.viewcache.world_surfacevisible[j]) continue; + surface = model->data_surfaces + j; rsurface.texture = R_GetCurrentTexture(surface->texture); if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles) { @@ -9989,7 +9937,7 @@ int r_maxsurfacelist = 0; const msurface_t **r_surfacelist = NULL; void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedepth, qbool depthonly, qbool debug, qbool prepass, qbool ui) { - int i, j, endj, flagsmask; + int i, j, flagsmask; model_t *model = ent->model; msurface_t *surfaces; unsigned char *update; @@ -10017,26 +9965,6 @@ void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedep surfaces = model->data_surfaces; update = model->brushq1.lightmapupdateflags; - // update light styles - if (!skysurfaces && !depthonly && !prepass && model->brushq1.num_lightstyles && r_refdef.scene.lightmapintensity > 0) - { - model_brush_lightstyleinfo_t *style; - // Iterate over each active style - for (i = 0, style = model->brushq1.data_lightstyleinfo;i < model->brushq1.num_lightstyles;i++, style++) - { - if (style->value != r_refdef.scene.lightstylevalue[style->style]) - { - int *list = style->surfacelist; - style->value = r_refdef.scene.lightstylevalue[style->style]; - // Iterate over every surface this style applies to - for (j = 0;j < style->numsurfaces;j++) - // Update brush entities even if not visible otherwise they'll render solid black. - if(r_refdef.viewcache.world_surfacevisible[list[j]] || ent != r_refdef.scene.worldentity) - update[list[j]] = true; - } - } - } - flagsmask = skysurfaces ? MATERIALFLAG_SKY : MATERIALFLAG_WALL; if (debug) @@ -10046,48 +9974,88 @@ void R_DrawModelSurfaces(entity_render_t *ent, qbool skysurfaces, qbool writedep return; } + // check if this is an empty model + if (model->submodelsurfaces_start >= model->submodelsurfaces_end) + return; + rsurface.lightmaptexture = NULL; rsurface.deluxemaptexture = NULL; rsurface.uselightmaptexture = false; rsurface.texture = NULL; rsurface.rtlight = NULL; numsurfacelist = 0; + // add visible surfaces to draw list if (ent == r_refdef.scene.worldentity) { // for the world entity, check surfacevisible - for (i = 0;i < model->nummodelsurfaces;i++) + for (i = model->submodelsurfaces_start;i < model->submodelsurfaces_end;i++) { - j = model->sortedmodelsurfaces[i]; + j = model->modelsurfaces_sorted[i]; if (r_refdef.viewcache.world_surfacevisible[j]) r_surfacelist[numsurfacelist++] = surfaces + j; } + + // don't do anything if there were no surfaces added (none of the world entity is visible) + if (!numsurfacelist) + { + rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity + return; + } } else if (ui) { - // for ui we have to preserve the order of surfaces - for (i = 0; i < model->nummodelsurfaces; i++) - r_surfacelist[numsurfacelist++] = surfaces + model->firstmodelsurface + i; + // for ui we have to preserve the order of surfaces (not using modelsurfaces_sorted) + for (i = model->submodelsurfaces_start; i < model->submodelsurfaces_end; i++) + r_surfacelist[numsurfacelist++] = surfaces + i; } else { // add all surfaces - for (i = 0; i < model->nummodelsurfaces; i++) - r_surfacelist[numsurfacelist++] = surfaces + model->sortedmodelsurfaces[i]; + for (i = model->submodelsurfaces_start; i < model->submodelsurfaces_end; i++) + r_surfacelist[numsurfacelist++] = surfaces + model->modelsurfaces_sorted[i]; } - // don't do anything if there were no surfaces - if (!numsurfacelist) - { - rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity - return; - } - // update lightmaps if needed - if (update) + + /* + * Mark lightmaps as dirty if their lightstyle's value changed. We do this by + * using style chains because most styles do not change on most frames, and most + * surfaces do not have styles on them. Mods like Arcane Dimensions (e.g. ad_necrokeep) + * break this rule and animate most surfaces. + */ + if (update && !skysurfaces && !depthonly && !prepass && model->brushq1.num_lightstyles && r_refdef.scene.lightmapintensity > 0 && r_q1bsp_lightmap_updates_enabled.integer) { - for (j = model->firstmodelsurface, endj = model->firstmodelsurface + model->nummodelsurfaces;j < endj;j++) + model_brush_lightstyleinfo_t *style; + + // For each lightstyle, check if its value changed and mark the lightmaps as dirty if so + for (i = 0, style = model->brushq1.data_lightstyleinfo; i < model->brushq1.num_lightstyles; i++, style++) + { + if (style->value != r_refdef.scene.lightstylevalue[style->style]) + { + int* list = style->surfacelist; + style->value = r_refdef.scene.lightstylevalue[style->style]; + // Value changed - mark the surfaces belonging to this style chain as dirty + for (j = 0; j < style->numsurfaces; j++) + update[list[j]] = true; + } + } + // Now check if update flags are set on any surfaces that are visible + if (r_q1bsp_lightmap_updates_hidden_surfaces.integer) + { + /* + * We can do less frequent texture uploads (approximately 10hz for animated + * lightstyles) by rebuilding lightmaps on surfaces that are not currently visible. + * For optimal efficiency, this includes the submodels of the worldmodel, so we + * use model->num_surfaces, not nummodelsurfaces. + */ + for (i = 0; i < model->num_surfaces;i++) + if (update[i]) + R_BuildLightMap(ent, surfaces + i, r_q1bsp_lightmap_updates_combine.integer); + } + else { - if (update[j]) - R_BuildLightMap(ent, surfaces + j); + for (i = 0; i < numsurfacelist; i++) + if (update[r_surfacelist[i] - surfaces]) + R_BuildLightMap(ent, (msurface_t *)r_surfacelist[i], r_q1bsp_lightmap_updates_combine.integer); } }