mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2025-02-01 21:30:57 +00:00
added r_transpSort and we can now sort all surface types by depth
also fixed up RE_AddPolyToScene's overflow check
This commit is contained in:
parent
0fcd462244
commit
a5e28eb08f
7 changed files with 139 additions and 28 deletions
|
@ -93,6 +93,13 @@ add: r_lightmapGreyscale <0.0 to 1.0> (default: 0) to control how monochromatic
|
|||
r_lightmapGreyscale 0 = full color (nothing done)
|
||||
r_lightmapGreyscale 1 = completely monochrome
|
||||
|
||||
add: r_transpSort <0|1> (default: 0) to select the transparency sorting mode
|
||||
r_transpSort 0 = sort dynamic transparent surfaces
|
||||
r_transpSort 1 = sort all transparent surfaces
|
||||
the drawback of r_transpSort 1 is that gameplay legibility might suffer and results might look
|
||||
quite inconsistent based on the view angles and surface dimensions
|
||||
example: dropped items in the cpm18r acid with cg_simpleItems 1
|
||||
|
||||
add: r_mapBrightness now works on q3map2's external lightmap atlas images
|
||||
|
||||
add: the renderer can now batch surfaces with different (but sufficiently similar) shaders
|
||||
|
|
|
@ -391,6 +391,14 @@ static void ParseFace( const dsurface_t* ds, const drawVert_t* verts, msurface_t
|
|||
VectorCopy(cv->plane.normal, cv->verts[i].normal);
|
||||
}
|
||||
}
|
||||
|
||||
vec3_t mins, maxs;
|
||||
ClearBounds(mins, maxs);
|
||||
for ( int i = 0 ; i < numVerts ; i++ ) {
|
||||
AddPointToBounds(cv->verts[i].xyz, mins, maxs);
|
||||
}
|
||||
VectorAdd(mins, maxs, cv->localOrigin);
|
||||
VectorScale(cv->localOrigin, 0.5f, cv->localOrigin);
|
||||
}
|
||||
|
||||
|
||||
|
@ -487,6 +495,9 @@ static void ParseTriSurf( const dsurface_t* ds, const drawVert_t* verts, msurfac
|
|||
R_ColorShiftLightingBytes( verts[i].color, tri->verts[i].rgba );
|
||||
}
|
||||
|
||||
VectorAdd( tri->bounds[0], tri->bounds[1], tri->localOrigin );
|
||||
VectorScale( tri->localOrigin, 0.5f, tri->localOrigin );
|
||||
|
||||
indexes += LittleLong( ds->firstIndex );
|
||||
for ( i = 0 ; i < numIndexes ; i++ ) {
|
||||
tri->indexes[i] = LittleLong( indexes[i] );
|
||||
|
|
|
@ -163,6 +163,14 @@ S_COLOR_VAL " T2 " S_COLOR_HELP "= Tent 2 (1/3 2/3, same as the CPU version)
|
|||
"dithering noise strength\n" \
|
||||
"The dithering on/off switch is " S_COLOR_CVAR "r_dither" S_COLOR_HELP "."
|
||||
|
||||
#define help_r_transpSort \
|
||||
"transparency sorting mode\n" \
|
||||
S_COLOR_VAL " 0 " S_COLOR_HELP "= Sort dynamic transparent surfaces\n" \
|
||||
S_COLOR_VAL " 1 " S_COLOR_HELP "= Sort all transparent surfaces\n" \
|
||||
"The drawback of " S_COLOR_CVAR "r_transpSort " S_COLOR_VAL "1 " S_COLOR_HELP "is that gameplay legibility " \
|
||||
"might suffer and results might look quite inconsistent based on the view angles and surface dimensions.\n" \
|
||||
"Example: dropped items in the cpm18r acid with " S_COLOR_CVAR "cg_simpleItems " S_COLOR_VAL "1"
|
||||
|
||||
#define help_r_rtColorFormat \
|
||||
"render target color format\n" \
|
||||
S_COLOR_VAL " 0 " S_COLOR_HELP "= R8G8B8A8\n" \
|
||||
|
|
|
@ -73,6 +73,7 @@ cvar_t *r_rtColorFormat;
|
|||
cvar_t *r_mipGenFilter;
|
||||
cvar_t *r_mipGenGamma;
|
||||
cvar_t *r_noiseScale;
|
||||
cvar_t *r_transpSort;
|
||||
cvar_t *r_gl3_geoStream;
|
||||
cvar_t *r_d3d11_syncOffsets;
|
||||
cvar_t *r_d3d11_presentMode;
|
||||
|
@ -408,6 +409,7 @@ static const cvarTableItem_t r_cvars[] =
|
|||
{ &r_gamma, "r_gamma", "1.2", CVAR_ARCHIVE, CVART_FLOAT, "0.5", "3", help_r_gamma },
|
||||
{ &r_greyscale, "r_greyscale", "0", CVAR_ARCHIVE, CVART_FLOAT, "0", "1", "controls how monochrome the final image looks" },
|
||||
{ &r_noiseScale, "r_noiseScale", "1.0", CVAR_ARCHIVE, CVART_FLOAT, "0.125", "8.0", help_r_noiseScale },
|
||||
{ &r_transpSort, "r_transpSort", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, help_r_transpSort },
|
||||
{ &r_lodCurveError, "r_lodCurveError", "2000", CVAR_ARCHIVE, CVART_FLOAT, "250", "10000", "curved surfaces LOD scale" },
|
||||
|
||||
//
|
||||
|
|
|
@ -492,10 +492,13 @@ typedef enum {
|
|||
} surfaceType_t;
|
||||
|
||||
struct drawSurf_t {
|
||||
unsigned sort; // bit combination for fast compares
|
||||
float depth; // for sorting transparent surfaces
|
||||
int index; // for sorting transparent surfaces
|
||||
const surfaceType_t* surface; // any of surface*_t
|
||||
// we keep the sort key at the top instead of the pointer
|
||||
// to allow for slightly cleaner code gen in the radix sort code
|
||||
unsigned sort; // bit combination for fast compares
|
||||
float depth; // transparent surface's midpoint's depth
|
||||
const surfaceType_t* surface; // any of surface*_t
|
||||
int index; // transparent surface's registration order
|
||||
qhandle_t model; // MD3 model handle
|
||||
};
|
||||
|
||||
extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( const void* );
|
||||
|
@ -531,6 +534,7 @@ struct srfPoly_t {
|
|||
int fogIndex;
|
||||
int numVerts;
|
||||
polyVert_t* verts;
|
||||
vec3_t localOrigin;
|
||||
};
|
||||
|
||||
|
||||
|
@ -578,6 +582,8 @@ struct srfSurfaceFace_t {
|
|||
|
||||
int numVerts;
|
||||
srfVert_t *verts;
|
||||
|
||||
vec3_t localOrigin;
|
||||
};
|
||||
|
||||
|
||||
|
@ -996,6 +1002,7 @@ extern cvar_t *r_intensity;
|
|||
extern cvar_t *r_gamma;
|
||||
extern cvar_t *r_greyscale;
|
||||
extern cvar_t *r_noiseScale; // the strength of the dithering noise
|
||||
extern cvar_t *r_transpSort; // transparency sorting mode
|
||||
extern cvar_t *r_lightmap; // render lightmaps only
|
||||
extern cvar_t *r_lightmapGreyscale; // how monochrome the lightmap looks
|
||||
extern cvar_t *r_fullbright; // avoid lightmap pass
|
||||
|
|
|
@ -1068,7 +1068,7 @@ static void R_SortLitsurfs( dlight_t* dl )
|
|||
void R_AddDrawSurf( const surfaceType_t* surface, const shader_t* shader, int fogIndex )
|
||||
{
|
||||
// instead of checking for overflow, we just mask the index so it wraps around
|
||||
int index = tr.refdef.numDrawSurfs++ & DRAWSURF_MASK;
|
||||
const int index = tr.refdef.numDrawSurfs++ & DRAWSURF_MASK;
|
||||
// the sort data is packed into a single 32 bit value so it can be
|
||||
// compared quickly during the qsorting process
|
||||
tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT)
|
||||
|
@ -1076,6 +1076,7 @@ void R_AddDrawSurf( const surfaceType_t* surface, const shader_t* shader, int fo
|
|||
| (shader->cullType << QSORT_CULLTYPE_SHIFT)
|
||||
| (shader->polygonOffset << QSORT_POLYOFF_SHIFT);
|
||||
tr.refdef.drawSurfs[index].surface = surface;
|
||||
tr.refdef.drawSurfs[index].model = tr.currentModel != NULL ? tr.currentModel->index : 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1121,20 +1122,94 @@ static float R_ComputePointDepth( const vec3_t point, const float* modelMatrix )
|
|||
}
|
||||
|
||||
|
||||
static float R_ComputeSurfaceDepth( const surfaceType_t* surf, int entityNum )
|
||||
static float R_ComputeEntityPointDepth( const vec3_t point, int entityNum )
|
||||
{
|
||||
if ( *surf == SF_ENTITY ) {
|
||||
const refEntity_t* ent = &tr.refdef.entities[entityNum].e;
|
||||
if ( ent->reType == RT_SPRITE )
|
||||
return R_ComputePointDepth( ent->origin, tr.viewParms.world.modelMatrix );
|
||||
if ( ent->reType == RT_LIGHTNING )
|
||||
return -999666.0f;
|
||||
}
|
||||
|
||||
return 999666.0f;
|
||||
orientationr_t orient;
|
||||
if ( entityNum != ENTITYNUM_WORLD )
|
||||
R_RotateForEntity( &tr.refdef.entities[entityNum], &tr.viewParms, &orient );
|
||||
else
|
||||
orient = tr.viewParms.world;
|
||||
|
||||
return R_ComputePointDepth( point, orient.modelMatrix );
|
||||
}
|
||||
|
||||
|
||||
static float R_ComputeSurfaceDepth( const surfaceType_t* surf, int entityNum, qhandle_t model )
|
||||
{
|
||||
const float back = 999666.0f;
|
||||
const float front = -999666.0f;
|
||||
|
||||
if ( *surf == SF_ENTITY ) {
|
||||
const refEntity_t* const ent = &tr.refdef.entities[entityNum].e;
|
||||
if ( ent->reType == RT_SPRITE ) // CPMA: simple items, rocket explosions, ...
|
||||
return R_ComputeEntityPointDepth( ent->origin, entityNum );
|
||||
if ( ent->reType == RT_LIGHTNING ) // CPMA: first-person lightning gun beam
|
||||
return front;
|
||||
// note that RT_MODEL not being checked isn't an omission, it's not needed
|
||||
return back;
|
||||
}
|
||||
|
||||
if ( *surf == SF_POLY ) { // CPMA: impact marks, rocket smoke, ...
|
||||
const srfPoly_t* const poly = (const srfPoly_t*)surf;
|
||||
return R_ComputeEntityPointDepth( poly->localOrigin, entityNum );
|
||||
}
|
||||
|
||||
if ( *surf == SF_MD3 ) { // CPMA: spawn points, rocket projectiles, ...
|
||||
vec3_t mins, maxs, midPoint;
|
||||
R_ModelBounds( model, mins, maxs );
|
||||
VectorAdd( mins, maxs, midPoint );
|
||||
VectorScale( midPoint, 0.5f, midPoint );
|
||||
return R_ComputeEntityPointDepth( midPoint, entityNum );
|
||||
}
|
||||
|
||||
// If we don't sort them, we let "static" surfaces be drawn behind the "dynamic" ones.
|
||||
// This helps avoid inconsistent-looking results like CPMA simple items and
|
||||
// large enough transparent liquid pools (e.g. dropped weapons in the cpm18r acid).
|
||||
if ( r_transpSort->integer == 0 )
|
||||
return back;
|
||||
|
||||
if ( *surf == SF_FACE ) { // cpm25 water
|
||||
const srfSurfaceFace_t* const face = (const srfSurfaceFace_t*)surf;
|
||||
return R_ComputeEntityPointDepth( face->localOrigin, entityNum );
|
||||
}
|
||||
|
||||
if ( *surf == SF_GRID ) { // hektik_b3 item markers
|
||||
const srfGridMesh_t* const grid = (const srfGridMesh_t*)surf;
|
||||
return R_ComputeEntityPointDepth( grid->localOrigin, entityNum );
|
||||
}
|
||||
|
||||
if ( *surf == SF_TRIANGLES ) { // cpm18r acid
|
||||
const srfTriangles_t* const tri = (const srfTriangles_t*)surf;
|
||||
return R_ComputeEntityPointDepth( tri->localOrigin, entityNum );
|
||||
}
|
||||
|
||||
return back;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
A few notes on transparency handling:
|
||||
|
||||
a) User-specified blend modes and user-created data mean we can't robustly substitute blend modes
|
||||
with better alternatives that are commutative (e.g. pre-multiplied alpha instead of the "standard" blend).
|
||||
The amount of corner cases to handle would be a headache and textures would need to be modified.
|
||||
I'm not even sure if it's possible. I'd gladly be proven wrong, though.
|
||||
|
||||
b) The code currently sorts surfaces (triangle groups) back to front.
|
||||
Sorting individual triangles would obviously be better for many scenarios,
|
||||
but it would still not be correct at all times (e.g. intersecting triangles, 3-way overlaps).
|
||||
|
||||
c) What we really want is true order-independent transparency (OIT).
|
||||
There are numerous techniques, but this 2-step method is the most promising avenue:
|
||||
1. Render transparent surfaces into per-pixel linked lists.
|
||||
2. Do a single "full-screen" pass which sorts and resolves each pixel's list.
|
||||
This requires support for atomic shader operations.
|
||||
|
||||
When atomic shader operations are not available, we could fall back to:
|
||||
- The current approach.
|
||||
- Per-pixel fixed-size arrays (there are several methods).
|
||||
- Depth peeling (there are also several methods).
|
||||
*/
|
||||
static int R_CompareDrawSurfDepth( const void* aPtr, const void* bPtr )
|
||||
{
|
||||
const drawSurf_t* a = ( const drawSurf_t* )aPtr;
|
||||
|
@ -1211,7 +1286,7 @@ static void R_SortDrawSurfs( int firstDrawSurf, int firstLitSurf )
|
|||
break;
|
||||
}
|
||||
|
||||
drawSurfs[i].depth = R_ComputeSurfaceDepth( drawSurfs[i].surface, entityNum );
|
||||
drawSurfs[i].depth = R_ComputeSurfaceDepth( drawSurfs[i].surface, entityNum, drawSurfs[i].model );
|
||||
drawSurfs[i].index = i;
|
||||
}
|
||||
|
||||
|
@ -1275,7 +1350,7 @@ static void R_AddEntitySurfaces()
|
|||
break;
|
||||
|
||||
case RT_MODEL:
|
||||
// we must set up parts of tr.or for model culling
|
||||
// we must set up parts of tr.orient for model culling
|
||||
R_RotateForEntity( ent, &tr.viewParms, &tr.orient );
|
||||
|
||||
tr.currentModel = R_GetModelByHandle( ent->e.hModel );
|
||||
|
|
|
@ -91,9 +91,6 @@ void R_AddPolygonSurfaces()
|
|||
|
||||
void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t* verts, int numPolys )
|
||||
{
|
||||
int i, j;
|
||||
vec3_t bounds[2];
|
||||
|
||||
if ( !tr.registered ) {
|
||||
return;
|
||||
}
|
||||
|
@ -106,12 +103,12 @@ void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t* verts
|
|||
// make sure the entire set will fit, rather than taking as many as we can
|
||||
// both ways have downsides, but if we ARE hitting the cap we're already screwed
|
||||
// and it's better to avoid something degenerate than to squeeze in 3 extra snowflakes
|
||||
if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) {
|
||||
if ( r_numpolyverts + numPolys * numVerts > max_polyverts || r_numpolys + numPolys > max_polys ) {
|
||||
ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( j = 0; j < numPolys; j++ ) {
|
||||
for ( int j = 0; j < numPolys; j++ ) {
|
||||
srfPoly_t* poly = &backEndData->polys[r_numpolys];
|
||||
poly->surfaceType = SF_POLY;
|
||||
poly->hShader = hShader;
|
||||
|
@ -123,15 +120,19 @@ void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t* verts
|
|||
r_numpolys++;
|
||||
r_numpolyverts += numVerts;
|
||||
|
||||
vec3_t bounds[2];
|
||||
VectorCopy( poly->verts[0].xyz, bounds[0] );
|
||||
VectorCopy( poly->verts[0].xyz, bounds[1] );
|
||||
for ( int i = 1 ; i < poly->numVerts ; i++ ) {
|
||||
AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] );
|
||||
}
|
||||
VectorAdd(bounds[0], bounds[1], poly->localOrigin);
|
||||
VectorScale(poly->localOrigin, 0.5f, poly->localOrigin);
|
||||
|
||||
poly->fogIndex = 0;
|
||||
// find which fog volume the poly is in (if any)
|
||||
if (tr.world && (tr.world->numfogs > 1)) {
|
||||
VectorCopy( poly->verts[0].xyz, bounds[0] );
|
||||
VectorCopy( poly->verts[0].xyz, bounds[1] );
|
||||
for ( i = 1 ; i < poly->numVerts ; i++ ) {
|
||||
AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] );
|
||||
}
|
||||
for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
|
||||
for ( int i = 1 ; i < tr.world->numfogs ; i++ ) {
|
||||
const fog_t* fog = &tr.world->fogs[i];
|
||||
if ( bounds[1][0] >= fog->bounds[0][0]
|
||||
&& bounds[1][1] >= fog->bounds[0][1]
|
||||
|
|
Loading…
Reference in a new issue