cvar_t r_glsl_vertextextureblend_usebothalphas = {CF_CLIENT | CF_ARCHIVE, "r_glsl_vertextextureblend_usebothalphas", "0", "use both alpha layers on vertex blended surfaces, each alpha layer sets amount of 'blend leak' on another layer, requires mod_q3shader_force_terrain_alphaflag on."};
+// FIXME: This cvar would grow to a ridiculous size after several launches and clean exits when used during surface sorting.
cvar_t r_framedatasize = {CF_CLIENT | CF_ARCHIVE, "r_framedatasize", "0.5", "size of renderer data cache used during one frame (for skeletal animation caching, light processing, etc)"};
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;
}
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
{
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;
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;
}
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
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
// 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;
}
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)
{
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);
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));
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
R_Particles_Init();
R_Explosion_Init();
R_LightningBeams_Init();
+ CL_MeshEntities_Init();
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
if (BoxesOverlap(boxmins, boxmaxs, eyemins, eyemaxs))
return true;
+ VectorCopy(eye, start);
// try specific positions in the box first - note that these can be cached
if (r_cullentities_trace_entityocclusion.integer)
{
for (i = 0; i < sizeof(positions) / sizeof(positions[0]); i++)
{
trace_t trace;
- VectorCopy(eye, start);
end[0] = boxmins[0] + (boxmaxs[0] - boxmins[0]) * positions[i][0];
end[1] = boxmins[1] + (boxmaxs[1] - boxmins[1]) * positions[i][1];
end[2] = boxmins[2] + (boxmaxs[2] - boxmins[2]) * positions[i][2];
return true;
}
}
- else if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
- return true;
+ else
+ {
+ // try center
+ VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, end);
+ if (model->brush.TraceLineOfSight(model, start, end, padmins, padmaxs))
+ return true;
+ }
// try various random positions
for (j = 0; j < numsamples; j++)
for (i = 0;i < r_refdef.scene.numentities;i++)
{
ent = r_refdef.scene.entities[i];
- if (ent != &cl.viewent.render && r_refdef.viewcache.world_novis)
+ if (r_refdef.viewcache.world_novis && !(ent->flags & RENDER_VIEWMODEL))
{
r_refdef.viewcache.entityvisible[i] = false;
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;
}
{
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;
}
}
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)
{
}
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
{
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;
for (i = 0; i < prog->num_edicts; i++)
{
edict = PRVM_EDICT_NUM(i);
- if (edict->priv.server->free)
+ if (edict->free)
continue;
// exclude the following for now, as they don't live in world coordinate space and can't be solid:
if (PRVM_gameedictedict(edict, tag_entity) != 0)
{
// 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;
}
{
// 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();
}
}
// 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();
}
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];
#endif
dynamic = model->surfmesh.isanimated;
- numsurfacelist = model->nummodelsurfaces;
- surfacelist = model->sortedmodelsurfaces;
surfaces = model->data_surfaces;
bih = NULL;
}
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))
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;
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)
{
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)
{
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)
{
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)
{
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;
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)
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];
- }
- // don't do anything if there were no surfaces
- if (!numsurfacelist)
- {
- rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity
- return;
+ for (i = model->submodelsurfaces_start; i < model->submodelsurfaces_end; i++)
+ r_surfacelist[numsurfacelist++] = surfaces + model->modelsurfaces_sorted[i];
}
- // 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);
}
}