cvar_t r_texture_dds_load = {CF_CLIENT | CF_ARCHIVE, "r_texture_dds_load", "0", "load compressed dds/filename.dds texture instead of filename.tga, if the file exists (requires driver support)"};
cvar_t r_texture_dds_save = {CF_CLIENT | CF_ARCHIVE, "r_texture_dds_save", "0", "save compressed dds/filename.dds texture when filename.tga is loaded, so that it can be loaded instead next time"};
-cvar_t r_textureunits = {CF_CLIENT, "r_textureunits", "32", "number of texture units to use in GL 1.1 and GL 1.3 rendering paths"};
-static cvar_t gl_combine = {CF_CLIENT | CF_READONLY, "gl_combine", "1", "indicates whether the OpenGL 1.3 rendering path is active"};
-static cvar_t r_glsl = {CF_CLIENT | CF_READONLY, "r_glsl", "1", "indicates whether the OpenGL 2.0 rendering path is active"};
-
cvar_t r_usedepthtextures = {CF_CLIENT | CF_ARCHIVE, "r_usedepthtextures", "1", "use depth texture instead of depth renderbuffer where possible, uses less video memory but may render slower (or faster) depending on hardware"};
cvar_t r_viewfbo = {CF_CLIENT | CF_ARCHIVE, "r_viewfbo", "0", "enables use of an 8bit (1) or 16bit (2) or 32bit (3) per component float framebuffer render, which may be at a different resolution than the video mode"};
cvar_t r_rendertarget_debug = {CF_CLIENT, "r_rendertarget_debug", "-1", "replaces the view with the contents of the specified render target (by number - note that these can fluctuate depending on scene)"};
cvar_t r_glsl_postprocess_uservec3_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_postprocess_uservec3_enable", "1", "enables postprocessing uservec3 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
cvar_t r_glsl_postprocess_uservec4_enable = {CF_CLIENT | CF_ARCHIVE, "r_glsl_postprocess_uservec4_enable", "1", "enables postprocessing uservec4 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
cvar_t r_colorfringe = {CF_CLIENT | CF_ARCHIVE, "r_colorfringe", "0", "Chromatic aberration. Values higher than 0.025 will noticeably distort the image"};
+cvar_t r_fxaa = {CF_CLIENT | CF_ARCHIVE, "r_fxaa", "0", "fast approximate anti aliasing"};
cvar_t r_water = {CF_CLIENT | CF_ARCHIVE, "r_water", "0", "whether to use reflections and refraction on water surfaces (note: r_wateralpha must be set below 1)"};
cvar_t r_water_cameraentitiesonly = {CF_CLIENT | CF_ARCHIVE, "r_water_cameraentitiesonly", "0", "whether to only show QC-defined reflections/refractions (typically used for camera- or portal-like effects)"};
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] =
{
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
{
case RENDERPATH_GL32:
case RENDERPATH_GLES2:
- Cvar_SetValueQuick(&r_textureunits, MAX_TEXTUREUNITS);
- Cvar_SetValueQuick(&gl_combine, 1);
- Cvar_SetValueQuick(&r_glsl, 1);
r_loadnormalmap = true;
r_loadgloss = true;
r_loadfog = false;
r_texture_numcubemaps = 0;
//r_texture_fogintensity = NULL;
memset(&r_fb, 0, sizeof(r_fb));
- R_GLSL_Restart_f(&cmd_client);
+ R_GLSL_Restart_f(cmd_local);
r_glsl_permutation = NULL;
memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
Cvar_RegisterVariable(&r_transparent_sortarraysize);
Cvar_RegisterVariable(&r_texture_dds_load);
Cvar_RegisterVariable(&r_texture_dds_save);
- Cvar_RegisterVariable(&r_textureunits);
- Cvar_RegisterVariable(&gl_combine);
Cvar_RegisterVariable(&r_usedepthtextures);
Cvar_RegisterVariable(&r_viewfbo);
Cvar_RegisterVariable(&r_rendertarget_debug);
Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepsize);
Cvar_RegisterVariable(&r_viewscale_fpsscaling_stepmax);
Cvar_RegisterVariable(&r_viewscale_fpsscaling_target);
- Cvar_RegisterVariable(&r_glsl);
Cvar_RegisterVariable(&r_glsl_deluxemapping);
Cvar_RegisterVariable(&r_glsl_offsetmapping);
Cvar_RegisterVariable(&r_glsl_offsetmapping_steps);
Cvar_RegisterVariable(&r_glsl_postprocess_uservec4_enable);
Cvar_RegisterVariable(&r_celshading);
Cvar_RegisterVariable(&r_celoutlines);
+ Cvar_RegisterVariable(&r_fxaa);
Cvar_RegisterVariable(&r_water);
Cvar_RegisterVariable(&r_water_cameraentitiesonly);
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++)
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;
}
}
if (r_refdef.scene.worldmodel)
{
r_refdef.scene.lightmapintensity *= r_refdef.scene.worldmodel->lightmapscale;
+
+ // Apply the default lightstyle to the lightmap even on q3bsp
+ if (cl.worldmodel && cl.worldmodel->type == mod_brushq3) {
+ r_refdef.scene.lightmapintensity *= r_refdef.scene.rtlightstylevalue[0];
+ }
}
if (r_showsurfaces.integer)
{
rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveModelEntity
if(R_CompileShader_CheckStaticParms())
- R_GLSL_Restart_f(&cmd_client);
+ 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)
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)
{
}
// check if this is an empty model
- if (model->nummodelsurfaces == 0)
+ if (model->submodelsurfaces_start >= model->submodelsurfaces_end)
return;
rsurface.lightmaptexture = NULL;
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;
}
}
else if (ui)
{
- // for ui we have to preserve the order of surfaces (not using sortedmodelsurfaces)
- 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];
}
/*