Merge soft particles from thedarkmod 2.04

don't really work, though
This commit is contained in:
Daniel Gibson 2024-06-14 00:12:00 +02:00
parent 10eee1eb43
commit 41eef54611
12 changed files with 379 additions and 33 deletions

View file

@ -409,6 +409,10 @@ idParticleStage *idDeclParticle::ParseParticleStage( idLexer &src ) {
stage->gravity = src.ParseFloat();
continue;
}
if ( !token.Icmp( "softeningRadius" ) ) { // #3878 soft particles
stage->softeningRadius = src.ParseFloat();
continue;
}
src.Error( "unknown token %s\n", token.c_str() );
}
@ -733,6 +737,7 @@ idParticleStage::idParticleStage( void ) {
hidden = false;
boundsExpansion = 0.0f;
bounds.Clear();
softeningRadius = -2.0f; // -2 means "auto" - #3878 soft particles
}
/*
@ -806,6 +811,7 @@ void idParticleStage::Default() {
randomDistribution = true;
entityColor = false;
cycleMsec = ( particleLife + deadTime ) * 1000;
softeningRadius = -2.0f; // -2 means "auto" - #3878 soft particles
}
/*

View file

@ -193,6 +193,16 @@ public:
float boundsExpansion; // user tweak to fix poorly calculated bounds
idBounds bounds; // derived
/* Soft particles -- SteveL #3878
-2.0 is the value at initialization, meaning no user specification: "auto".
-1.0 means no change to old system: suppress soft particles, but allow modelDepthhack if specified.
0 means disable all softening for this stage, including modelDepthHack.
+ve value means apply soft particle effect, allowing overdraw up to the specified depth.
This is more flexible even when not using soft particles, as modelDepthHack
can be turned off for specific stages to stop them poking through walls.
*/
float softeningRadius;
};

View file

@ -183,7 +183,8 @@ public:
void CopyFramebuffer( int x, int y, int width, int height, bool useOversizedBuffer );
void CopyDepthbuffer( int x, int y, int width, int height );
void CopyDepthbuffer( int x, int y, int width, int height, bool useOversizedBuffer );
void UploadScratch( const byte *pic, int width, int height );
@ -417,6 +418,9 @@ public:
idImage * specular2DTableImage; // 2D intensity texture with our specular function with variable specularity
idImage * borderClampImage; // white inside, black outside
idImage * currentDepthImage; // #3877. Allow shaders to access scene depth
//--------------------------------------------------------
idImage * AllocImage( const char *name );

View file

@ -1993,6 +1993,7 @@ void idImageManager::Init() {
accumImage = ImageFromFunction("_accum", R_RGBA8Image );
scratchCubeMapImage = ImageFromFunction("_scratchCubeMap", makeNormalizeVectorCubeMap );
currentRenderImage = ImageFromFunction("_currentRender", R_RGBA8Image );
currentDepthImage = ImageFromFunction( "_currentDepth", R_RGBA8Image ); // #3877. Allow shaders to access scene depth
cmdSystem->AddCommand( "reloadImages", R_ReloadImages_f, CMD_FL_RENDERER, "reloads images" );
cmdSystem->AddCommand( "listImages", R_ListImages_f, CMD_FL_RENDERER, "lists images" );

View file

@ -1898,26 +1898,30 @@ CopyDepthbuffer
This should just be part of copyFramebuffer once we have a proper image type field
====================
*/
void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight ) {
Bind();
void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight, bool useOversizedBuffer )
{
this->Bind();
// if the size isn't a power of 2, the image must be increased in size
int potWidth, potHeight;
potWidth = MakePowerOfTwo( imageWidth );
potHeight = MakePowerOfTwo( imageHeight );
if ( uploadWidth != potWidth || uploadHeight != potHeight ) {
GetDownsize( imageWidth, imageHeight );
GetDownsize( potWidth, potHeight );
// Ensure we are reading from the back buffer:
qglReadBuffer( GL_BACK );
// only resize if the current dimensions can't hold it at all,
// otherwise subview renderings could thrash this
if ( ( useOversizedBuffer && ( uploadWidth < potWidth || uploadHeight < potHeight ) ) || ( !useOversizedBuffer && ( uploadWidth != potWidth || uploadHeight != potHeight ) ) )
{
uploadWidth = potWidth;
uploadHeight = potHeight;
if ( potWidth == imageWidth && potHeight == imageHeight ) {
qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, 0 );
} else {
// we need to create a dummy image with power of two dimensions,
// then do a qglCopyTexSubImage2D of the data we want
qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
}
// This bit runs once only at map start, because it tests whether the image is too small to hold the screen.
// It resizes the texture to a power of two that can hold the screen,
// and then subsequent captures to the texture put the depth component into the RGB channels
qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
} else {
// otherwise, just subimage upload it so that drivers can tell we are going to be changing
// it and don't try and do a texture compression or some other silliness

View file

@ -1434,6 +1434,10 @@ void idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
ss->drawStateBits |= GLS_DEPTHMASK;
continue;
}
if ( !token.Icmp( "ignoreDepth" ) ) { // Added in #3877.
ss->drawStateBits |= GLS_DEPTHFUNC_ALWAYS;
continue;
}
if ( !token.Icmp( "alphaTest" ) ) {
ss->hasAlphaTest = true;
ss->alphaTestRegister = ParseExpression( src );

View file

@ -302,8 +302,13 @@ public:
virtual float DepthHack() const;
virtual int Memory() const;
float SofteningRadius( const int stage ) const; // #3878 soft particles
private:
void SetSofteningRadii(); // #3878 soft particles
const idDeclParticle * particleSystem;
idList<float> softeningRadii; // #3878 soft particles
};
/*

View file

@ -50,6 +50,7 @@ idRenderModelPrt::InitFromFile
void idRenderModelPrt::InitFromFile( const char *fileName ) {
name = fileName;
particleSystem = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, fileName ) );
SetSofteningRadii(); // # 3878
}
/*
@ -286,3 +287,64 @@ int idRenderModelPrt::Memory() const {
return total;
}
/*
====================
idRenderModelPrt::SetSofteningRadii
Calculate "depth" of each particle stage that represents a 3d volume, so the particle can
be allowed to overdraw solid geometry by the right amount, and the particle "thickness" (visibility)
can be adjusted by how much of it is visible in front of the background surface.
"depth" is by default 0.8 of the particle radius, and particles less than 2 units in size won't be softened.
The particles that represent 3d volumes are the view-aligned ones. Others have depth set to 0.
Cache these values rather than calculate them for each stage every frame.
Added for soft particles -- SteveL #3878.
====================
*/
void idRenderModelPrt::SetSofteningRadii()
{
softeningRadii.AssureSize( particleSystem->stages.Num() );
for ( int i = 0; i < particleSystem->stages.Num(); ++i )
{
const idParticleStage* ps = particleSystem->stages[i];
if ( ps->softeningRadius > -2.0f ) // User has specified a setting
{
softeningRadii[i] = ps->softeningRadius;
}
else if ( ps->orientation == POR_VIEW ) // Only view-aligned particle stages qualify for softening
{
float diameter = Max( ps->size.from, ps->size.to );
float scale = Max( ps->aspect.from, ps->aspect.to );
diameter *= Max( scale, 1.0f ); // aspect applies to 1 axis only. If it's < 1, the other axis will still be at scale 1
if ( diameter > 2.0f ) // Particle is big enough to soften
{
softeningRadii[i] = diameter * 0.8f / 2.0f;
}
else // Particle is small. Disable both softening and modelDepthHack
{
softeningRadii[i] = 0.0f;
}
}
else // Particle isn't view-aligned, and no user setting. Don't change anything.
{
softeningRadii[i] = -1;
}
}
}
/*
====================
idRenderModelPrt::SofteningRadius
Return the max radius of the individual quads that make up this stage.
Added for soft particles #3878.
====================
*/
float idRenderModelPrt::SofteningRadius( const int stage ) const {
assert( particleSystem );
assert( stage > -1 && stage < softeningRadii.Num() );
return softeningRadii[stage];
}

View file

@ -104,7 +104,7 @@ void RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
float parm[4];
parm[0] = parm[1] = parm[2] = r_brightness.GetFloat();
parm[3] = 1.0/r_gamma.GetFloat(); // 1.0/gamma so the shader doesn't have to do this calculation
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_GAMMA_BRIGHTNESS, parm );
}
// set the textures
@ -345,6 +345,10 @@ static progDef_t progs[MAX_GLPROGS] = {
{ GL_VERTEX_PROGRAM_ARB, VPROG_GLASSWARP, "arbVP_glasswarp.txt" },
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
// SteveL #3878: Particle softening applied by the engine
{ GL_VERTEX_PROGRAM_ARB, VPROG_SOFT_PARTICLE, "soft_particle.vfp" }, // TODO: can we specify this in C++?
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_SOFT_PARTICLE, "soft_particle.vfp" },
// additional programs can be dynamically specified in materials
};
@ -468,10 +472,10 @@ void R_LoadARBProgram( int progIndex ) {
// POW might not work with a negative base (it looks wrong with intel's Linux driver)
// and clamping values >1 to 1 is ok because when writing to result.color
// it's clamped anyway and pow(base, exp) is always >= 1 for base >= 1
"MUL_SAT dhewm3tmpres.xyz, program.env[4], dhewm3tmpres;\n" // first multiply with brightness
"POW result.color.x, dhewm3tmpres.x, program.env[4].w;\n" // then do pow(dhewm3tmpres.xyz, vec3(1/gamma))
"POW result.color.y, dhewm3tmpres.y, program.env[4].w;\n" // (apparently POW only supports scalars, not whole vectors)
"POW result.color.z, dhewm3tmpres.z, program.env[4].w;\n"
"MUL_SAT dhewm3tmpres.xyz, program.env[21], dhewm3tmpres;\n" // first multiply with brightness
"POW result.color.x, dhewm3tmpres.x, program.env[21].w;\n" // then do pow(dhewm3tmpres.xyz, vec3(1/gamma))
"POW result.color.y, dhewm3tmpres.y, program.env[21].w;\n" // (apparently POW only supports scalars, not whole vectors)
"POW result.color.z, dhewm3tmpres.z, program.env[21].w;\n"
"MOV result.color.w, dhewm3tmpres.w;\n" // alpha remains unmodified
"\nEND\n\n"; // we add this block right at the end, replacing the original "END" string

View file

@ -33,6 +33,9 @@ If you have questions concerning this license or the applicable additional terms
extern idCVar r_useCarmacksReverse;
extern idCVar r_useStencilOpSeparate;
idCVar r_skipDepthCapture( "r_skipDepthCapture", "0", CVAR_RENDERER | CVAR_BOOL, "skip depth capture" ); // #3877
/*
=====================
RB_BakeTextureMatrixIntoTexgen
@ -313,7 +316,7 @@ void RB_FinishStageTexturing( const shaderStage_t *pStage, const drawSurf_t *sur
qglDisable( GL_FRAGMENT_PROGRAM_ARB );
qglDisable( GL_VERTEX_PROGRAM_ARB );
// Fixme: Hack to get around an apparent bug in ATI drivers. Should remove as soon as it gets fixed.
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 );
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); // FIXME ...
} else {
qglDisable( GL_TEXTURE_GEN_S );
qglDisable( GL_TEXTURE_GEN_T );
@ -513,6 +516,9 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
}
void RB_SetProgramEnvironment( bool isPostProcess ); // so RB_STD_FillDepthBuffer() can use it
/*
=====================
RB_STD_FillDepthBuffer
@ -553,6 +559,19 @@ void RB_STD_FillDepthBuffer( drawSurf_t **drawSurfs, int numDrawSurfs ) {
RB_RenderDrawSurfListWithFunction( drawSurfs, numDrawSurfs, RB_T_FillDepthBuffer );
// Make the early depth pass available to shaders. #3877
if ( backEnd.viewDef->renderView.viewID >= 0 // Suppress for lightgem rendering passes
&& !r_skipDepthCapture.GetBool() )
{
globalImages->currentDepthImage->CopyDepthbuffer( backEnd.viewDef->viewport.x1,
backEnd.viewDef->viewport.y1,
backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1,
backEnd.viewDef->viewport.y2 - backEnd.viewDef->viewport.y1 + 1,
true );
bool isPostProcess = false; // TODO
RB_SetProgramEnvironment( isPostProcess );
}
if ( backEnd.viewDef->numClipPlanes ) {
GL_SelectTexture( 1 );
globalImages->BindNull();
@ -575,6 +594,19 @@ SHADER PASSES
RB_SetProgramEnvironment
Sets variables that can be used by all vertex programs
[SteveL #3877] Note on the use of fragment program environmental variables.
Parameters 0 and 1 are set here to allow conversion of screen coordinates to
texture coordinates, for use when sampling _currentRender.
Those same parameters 0 and 1, plus 2 and 3, are given entirely different
meanings in draw_arb2.cpp while light interactions are being drawn.
This function is called again before currentRender size is needed by post processing
effects are done, so there's no clash.
// TODO: I'm using 4 for gamma in shaders, so the following shit must be incremented by 1
Only parameters 0..3 were in use before #3877 - and in dhewm3 also 4, for gamma in shader.
Now I've used a new parameter 5 for the size of _currentDepth. It's needed throughout,
including by light interactions, and its size might in theory differ from _currentRender.
Parameters 6 and 7 are used by soft particles #3878. Note these can be freely reused by different draw calls.
==================
*/
void RB_SetProgramEnvironment( bool isPostProcess ) {
@ -643,9 +675,23 @@ void RB_SetProgramEnvironment( bool isPostProcess ) {
// (setting them to 1.0 makes them no-ops)
parm[0] = parm[1] = parm[2] = parm[3] = 1.0f;
}
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_GAMMA_BRIGHTNESS, parm );
}
// #3877: Allow shaders to access depth buffer.
// Two useful ratios are packed into this parm: [0] and [1] hold the x,y multipliers you need to map a screen
// coordinate (fragment position) to the depth image: those are simply the reciprocal of the depth
// image size, which has been rounded up to a power of two. Slots [3] and [4] hold the ratio of the depth image
// size to the current render image size. These sizes can differ if the game crops the render viewport temporarily
// during post-processing effects. The depth render is smaller during the effect too, but the depth image doesn't
// need to be downsized, whereas the current render image does get downsized when it's captured by the game after
// the skybox render pass. The ratio is needed to map between the two render images.
parm[0] = 1.0f / globalImages->currentDepthImage->uploadWidth;
parm[1] = 1.0f / globalImages->currentDepthImage->uploadHeight;
parm[2] = static_cast<float>(globalImages->currentRenderImage->uploadWidth) / globalImages->currentDepthImage->uploadWidth;
parm[3] = static_cast<float>(globalImages->currentRenderImage->uploadHeight) / globalImages->currentDepthImage->uploadHeight;
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_CURDEPTH_RECIPR, parm );
//
// set eye position in global space
//
@ -654,8 +700,6 @@ void RB_SetProgramEnvironment( bool isPostProcess ) {
parm[2] = backEnd.viewDef->renderView.vieworg[2];
parm[3] = 1.0;
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 1, parm );
}
/*
@ -749,6 +793,9 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
return;
}
// check whether we're drawing a soft particle surface #3878
const bool soft_particle = ( surf->dsFlags & DSF_SOFT_PARTICLE ) != 0;
// get the expressions for conditionals / color / texcoords
regs = surf->shaderRegisters;
@ -765,7 +812,8 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
RB_EnterWeaponDepthHack();
}
if ( surf->space->modelDepthHack != 0.0f ) {
if ( surf->space->modelDepthHack != 0.0f && !soft_particle ) // #3878 soft particles don't want modelDepthHack, which is
{ // an older way to slightly "soften" particles
RB_EnterModelDepthHack( surf->space->modelDepthHack );
}
@ -791,6 +839,9 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
continue;
}
// determine the blend mode (used by soft particles #3878)
const int src_blend = pStage->drawStateBits & GLS_SRCBLEND_BITS;
// see if we are a new-style stage
newShaderStage_t *newStage = pStage->newStage;
if ( newStage ) {
@ -866,7 +917,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
qglDisable( GL_VERTEX_PROGRAM_ARB );
qglDisable( GL_FRAGMENT_PROGRAM_ARB );
// Fixme: Hack to get around an apparent bug in ATI drivers. Should remove as soon as it gets fixed.
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 );
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); // FIXME: ...
qglDisableClientState( GL_COLOR_ARRAY );
qglDisableVertexAttribArrayARB( 9 );
@ -874,6 +925,142 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
qglDisableClientState( GL_NORMAL_ARRAY );
continue;
}
else if ( soft_particle
&& surf->particle_radius > 0.0f
&& ( src_blend == GLS_SRCBLEND_ONE || src_blend == GLS_SRCBLEND_SRC_ALPHA )
&& tr.backEndRenderer == BE_ARB2
&& !r_skipNewAmbient.GetBool() )
{
// SteveL #3878. Particles are automatically softened by the engine, unless they have shader programs of
// their own (i.e. are "newstages" handled above). This section comes after the newstage part so that if a
// designer has specified their own shader programs, those will be used instead of the soft particle program.
if ( pStage->vertexColor == SVC_IGNORE )
{
// Ignoring vertexColor is not recommended for particles. The particle system uses vertexColor for fading.
// However, there are existing particle effects that don't use it, in which case we default to using the
// rgb color modulation specified in the material like the "old stages" do below.
color[0] = regs[pStage->color.registers[0]];
color[1] = regs[pStage->color.registers[1]];
color[2] = regs[pStage->color.registers[2]];
color[3] = regs[pStage->color.registers[3]];
qglColor4fv( color );
}
else
{
// A properly set-up particle shader
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
qglEnableClientState( GL_COLOR_ARRAY );
}
GL_State( pStage->drawStateBits | GLS_DEPTHFUNC_ALWAYS ); // Disable depth clipping. The fragment program will
// handle it to allow overdraw.
//GL_State( pStage->drawStateBits );
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_SOFT_PARTICLE );
qglEnable( GL_VERTEX_PROGRAM_ARB );
// Bind image and _currentDepth
GL_SelectTexture( 0 );
pStage->texture.image->Bind();
GL_SelectTexture( 1 );
globalImages->currentDepthImage->Bind();
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_SOFT_PARTICLE );
qglEnable( GL_FRAGMENT_PROGRAM_ARB );
// Set up parameters for fragment program
const char* srcblendstr = "???";
if ( src_blend >= 0 && src_blend <= 9 ) {
const char* blendModes[] = {
"ONE",
"ZERO",
"!! INVALID !!",
"DST_COLOR",
"ONE_MINUS_DST_COLOR",
"SRC_ALPHA",
"ONE_MINUS_SRC_ALPHA",
"DST_ALPHA",
"ONE_MINUS_DST_ALPHA",
"ALPHA_SATURATE"
};
srcblendstr = blendModes[src_blend];
}
/*
int dst_blend =
const int GLS_DSTBLEND_ZERO = 0x0;
const int GLS_DSTBLEND_ONE = 0x00000020;
const int GLS_DSTBLEND_SRC_COLOR = 0x00000030;
const int GLS_DSTBLEND_ONE_MINUS_SRC_COLOR = 0x00000040;
const int GLS_DSTBLEND_SRC_ALPHA = 0x00000050;
const int GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA = 0x00000060;
const int GLS_DSTBLEND_DST_ALPHA = 0x00000070;
const int GLS_DSTBLEND_ONE_MINUS_DST_ALPHA = 0x00000080;
const int GLS_DSTBLEND_BITS = 0x000000f0;
*/
printf("XX mat: %s (%s), src_blend = %d (%s)\n", shader->GetName(), shader->GetDescription(), src_blend, srcblendstr);
// program.env[5] is the particle radius, given as { radius, 1/(faderange), 1/radius }
float fadeRange;
// fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive
// blends (light glares), because additive effects work differently. Fog is half as apparent when a wall
// is in the middle of it. Light glares lose no visibility when they have something to reflect off. See
// issue #3878 for diagram
if ( src_blend == GLS_SRCBLEND_SRC_ALPHA ) // an alpha blend material
{
fadeRange = surf->particle_radius * 2.0f;
}
else if ( src_blend == GLS_SRCBLEND_ONE ) // an additive (blend add) material
{
fadeRange = surf->particle_radius;
}
float parm[4] = {
surf->particle_radius,
1.0f / ( fadeRange ),
1.0f / surf->particle_radius,
0.0f
};
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_PARTICLE_RADIUS, parm );
// program.env[6] is the color channel mask. It gets added to the fade multiplier, so adding 1
// to a channel will make sure it doesn't get faded at all. Particles with additive blend
// need their RGB channels modifying to blend them out. Particles with an alpha blend need
// their alpha channel modifying.
if ( src_blend == GLS_SRCBLEND_SRC_ALPHA ) // an alpha blend material
{
parm[0] = parm[1] = parm[2] = 1.0f; // Leave the rgb channels at full strength when fading
parm[3] = 0.0f; // but fade the alpha channel
}
else if ( src_blend == GLS_SRCBLEND_ONE ) // an additive (blend add) material
{
parm[0] = parm[1] = parm[2] = 0.0f; // Fade the rgb channels but
parm[3] = 1.0f; // leave the alpha channel at full strength
}
//parm[0] = parm[1] = parm[2] = 0.0f; // XXX hack
//parm[3] = 1.0f; // XXX hack
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_PARTICLE_COLCHAN_MASK, parm );
// draw it
RB_DrawElementsWithCounters( tri );
// Clean up GL state
GL_SelectTexture( 1 );
globalImages->BindNull();
GL_SelectTexture( 0 );
globalImages->BindNull();
qglDisable( GL_VERTEX_PROGRAM_ARB );
qglDisable( GL_FRAGMENT_PROGRAM_ARB );
if ( pStage->vertexColor != SVC_IGNORE ) {
qglDisableClientState( GL_COLOR_ARRAY );
}
continue;
}
//--------------------------
//
@ -972,7 +1159,8 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
if ( shader->TestMaterialFlag(MF_POLYGONOFFSET) ) {
qglDisable( GL_POLYGON_OFFSET_FILL );
}
if ( surf->space->weaponDepthHack || surf->space->modelDepthHack != 0.0f ) {
if ( surf->space->weaponDepthHack || ( !soft_particle && surf->space->modelDepthHack != 0.0f ) ) // #3878 soft particles
{
RB_LeaveDepthHack();
}
}

View file

@ -35,8 +35,13 @@ If you have questions concerning this license or the applicable additional terms
#include "renderer/tr_local.h"
#include "Model_local.h"
static const float CHECK_BOUNDS_EPSILON = 1.0f;
static idCVar r_useSoftParticles( "r_useSoftParticles", "1", CVAR_RENDERER | CVAR_BOOL, "soften particle transitions when player walks through them or they cross solid geometry" );
/*
===========================================================================================
@ -673,6 +678,8 @@ void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const
drawSurf->material = shader;
drawSurf->scissorRect = scissor;
drawSurf->dsFlags = 0;
drawSurf->particle_radius = 0.0f; // #3878
if ( viewInsideShadow ) {
drawSurf->dsFlags |= DSF_VIEW_INSIDE_SHADOW;
}
@ -1186,7 +1193,8 @@ R_AddDrawSurf
=================
*/
void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
const idMaterial *shader, const idScreenRect &scissor ) {
const idMaterial *shader, const idScreenRect &scissor, const float soft_particle_radius )
{
drawSurf_t *drawSurf;
const float *shaderParms;
static float refRegs[MAX_EXPRESSION_REGISTERS]; // don't put on stack, or VC++ will do a page touch
@ -1198,7 +1206,17 @@ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const
drawSurf->material = shader;
drawSurf->scissorRect = scissor;
drawSurf->sort = shader->GetSort() + tr.sortOffset;
drawSurf->dsFlags = 0;
if ( soft_particle_radius != -1.0f ) // #3878
{
drawSurf->dsFlags = DSF_SOFT_PARTICLE;
drawSurf->particle_radius = soft_particle_radius;
}
else
{
drawSurf->dsFlags = 0;
drawSurf->particle_radius = 0.0f;
}
// bumping this offset each time causes surfaces with equal sort orders to still
// deterministically draw in the order they are added
@ -1218,7 +1236,7 @@ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const
}
tr.viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ) );
if(count > 0)
memcpy( tr.viewDef->drawSurfs, old, count ); // XXX null pointer passed as argument 2, which is declared to never be null
memcpy( tr.viewDef->drawSurfs, old, count );
}
tr.viewDef->drawSurfs[tr.viewDef->numDrawSurfs] = drawSurf;
tr.viewDef->numDrawSurfs++;
@ -1423,8 +1441,21 @@ static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) {
vertexCache.Touch( tri->indexCache );
}
// Soft Particles -- SteveL #3878
float particle_radius = -1.0f; // Default = disallow softening, but allow modelDepthHack if specified in the decl.
if ( r_useSoftParticles.GetBool()
&& !shader->ReceivesLighting() // don't soften surfaces that are meant to be solid
&& tr.viewDef->renderView.viewID >= 0 ) // Skip during "invisible" rendering passes (e.g. lightgem)
{
const idRenderModelPrt* prt = dynamic_cast<const idRenderModelPrt*>( def->parms.hModel ); // yuck.
if ( prt )
{
particle_radius = prt->SofteningRadius( surf->id );
}
}
// add the surface for drawing
R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect );
R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect, particle_radius );
// ambientViewCount is used to allow light interactions to be rejected
// if the ambient surface isn't visible at all
@ -1514,6 +1545,14 @@ void R_AddModelSurfaces( void ) {
continue;
}
// Don't let particle entities re-instantiate their dynamic model during non-visible
// views (in TDM, the light gem render) -- SteveL #3970
if ( tr.viewDef->renderView.viewID < 0
&& dynamic_cast<const idRenderModelPrt*>( vEntity->entityDef->parms.hModel ) != NULL ) // yuck.
{
continue;
}
// add the ambient surface if it has a visible rectangle
if ( !vEntity->scissorRect.IsEmpty() ) {
model = R_EntityDefDynamicModel( vEntity->entityDef );

View file

@ -109,6 +109,7 @@ SURFACES
// drawSurf_t are always allocated and freed every frame, they are never cached
static const int DSF_VIEW_INSIDE_SHADOW = 1;
static const int DSF_SOFT_PARTICLE = 2; // #3878 - soft particles
typedef struct drawSurf_s {
const srfTriangles_t *geo;
@ -121,6 +122,7 @@ typedef struct drawSurf_s {
int dsFlags; // DSF_VIEW_INSIDE_SHADOW, etc
struct vertCache_s *dynamicTexCoords; // float * in vertex cache memory
// specular directions for non vertex program cards, skybox texcoords, etc
float particle_radius; // The radius of individual quads for soft particles #3878
} drawSurf_t;
@ -1179,7 +1181,7 @@ viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def );
viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *def );
void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
const idMaterial *shader, const idScreenRect &scissor );
const idMaterial *shader, const idScreenRect &scissor, const float soft_particle_radius = -1.0f ); // soft particles in #3878
void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space,
const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow );
@ -1315,6 +1317,10 @@ typedef enum {
FPROG_AMBIENT,
VPROG_GLASSWARP,
FPROG_GLASSWARP,
// SteveL #3878: soft particles
VPROG_SOFT_PARTICLE,
FPROG_SOFT_PARTICLE,
//
PROG_USER
} program_t;
@ -1362,9 +1368,22 @@ typedef enum {
PP_SPECULAR_MATRIX_S,
PP_SPECULAR_MATRIX_T,
PP_COLOR_MODULATE,
PP_COLOR_ADD,
PP_COLOR_ADD, // 17
PP_LIGHT_FALLOFF_TQ = 20 // only for NV programs
PP_LIGHT_FALLOFF_TQ = 20, // only for NV programs - DG: unused
PP_GAMMA_BRIGHTNESS = 21, // DG: for gamma in shader: { r_brightness, r_brightness, r_brightness, 1/r_gamma }
// DG: for soft particles from TDM: reciprocal of _currentDepth size.
// Lets us convert a screen position to a texcoord in _currentDepth
PP_CURDEPTH_RECIPR = 22,
// DG: for soft particles from TDM: particle radius, given as { radius, 1/(fadeRange), 1/radius }
// fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive
// blends (light glares), because additive effects work differently. Fog is half as apparent when a wall
// is in the middle of it. Light glares lose no visibility when they have something to reflect off.
PP_PARTICLE_RADIUS = 23,
// DG: for soft particles from TDM: color channel mask.
// Particles with additive blend need their RGB channels modifying to blend them out
// Particles with an alpha blend need their alpha channel modifying.
PP_PARTICLE_COLCHAN_MASK = 24,
} programParameter_t;