Posted by: trepid_jon
Date posted: Oct 11 2005 User Rating: 5 out of 5.0 | Number of views: 2720 Number of comments: 0 | Description: This will let you optionally set far clipping/fading on particles, so you can fade particles out at a certain distance from you. |
As far as I can tell, Valve only made it possible to clip/fade particles when you get near them. But they didn't make an option to clip/fade particles when you get far away from them. I needed this option for one of my mods, and since I got it working, I figured I would post an article to help out anyone who needs or wants to do this as well.
First thing we'll do is go into cl_dll\particle_util.h and look for the GetAlphaDistanceFade inline function...
| | | inline float GetAlphaDistanceFade( const Vector &pos, const float fadeNearDist, const float fadeFarDist) { if(-pos.z > fadeFarDist) { return 1; } else if(-pos.z > fadeNearDist) { return (-pos.z - fadeNearDist) / (fadeFarDist - fadeNearDist); } else { return 0; } } |
Either comment that code or erase it (I commented mine), then put this new code in its place...
| | | // If you can find a MAX_VIEW_DISTANCE or something, maybe use that instead of 65536. inline float GetAlphaDistanceFade( const Vector &pos, const float fadeNearMin, const float fadeNearMax, const float fadeFarMin = 65536, const float fadeFarMax = 65536) { if ( -pos.z < fadeNearMin || -pos.z > fadeFarMax ) { return 0; } else if ( -pos.z < fadeNearMax && fadeNearMax != fadeNearMin ) { return ( -pos.z - fadeNearMin ) / ( fadeNearMax - fadeNearMin ); } else if( -pos.z > fadeFarMin && fadeFarMax != fadeFarMin ) { return 1 - ( ( -pos.z - fadeFarMin ) / ( fadeFarMax - fadeFarMin ) ); } else { return 1; } } |
I made a picture with example settings to help explain what's happening...

In case you're curious, we're defaulting fadeFarMin and fadeFarMax, because we want all previous calls to GetAlphaDistanceFade to still work without us having to change them. I used 65536 as the defaults, because that seems like a good number and I don't think the player will ever get that far away from the particles anyway. I actually first tried just doing the 3D distance formula from points (16384,16384,16384) to (-16384,-16384,-16384), and got 56755.84086241697. But I don't think it really matters, so I just put 65536 in there.
Now, to take advantage of the new GetAlphaDistanceFade function, we need to find out where it's being called that would be useful to us. If you search in files for that function, you will find it being called in a few different places. The two places we're going to worry with are in cl_dll\particles_localspace.cpp and cl_dll\particles_simple.cpp. We'll leave the other calls in the other files the way they are, so they'll be using the defaults that we setup in GetAlphaDistanceFade.
So moving on, open cl_dll\particles_localspace.cpp, go into void CLocalSpaceEmitter::RenderParticles( CParticleRenderIterator *pIterator ), and look for this...
| | | // Render it RenderParticle_ColorSizeAngle( pIterator->GetParticleDraw(), screenPos, UpdateColor( pParticle ), UpdateAlpha( pParticle ) * GetAlphaDistanceFade( screenPos, m_flNearClipMin, m_flNearClipMax ), UpdateScale( pParticle ), pParticle->m_flRoll ); |
Change that to this...
| | | // Render it RenderParticle_ColorSizeAngle( pIterator->GetParticleDraw(), screenPos, UpdateColor( pParticle ), UpdateAlpha( pParticle ) * GetAlphaDistanceFade( screenPos, m_flNearClipMin, m_flNearClipMax, m_flFarClipMin, m_flFarClipMax ), UpdateScale( pParticle ), pParticle->m_flRoll ); |
If you were to compile right now, you would get errors because m_flFarClipMin and m_flFarClipMax are not members of the class CLocalSpaceEmitter. But we're not going to declare them in CLocalSpaceEmitter. Instead, we're going to declare them in CSimpleEmitter, which is CLocalSpaceEmitter's parent.
So open cl_dll\particles_simple.h, and find these...
| | | float m_flNearClipMin; float m_flNearClipMax; |
Right under those, we're going to add m_flFarClipMin and m_flFarClipMax...
| | | float m_flFarClipMin; float m_flFarClipMax; |
We have the member varialbes now, but we need to set the default values in the CSimpleEmitter Constructor so we don't get any chode...because chode is bad!
Open cl_dll\particles_simple.cpp and find the Constructor...
| | | CSimpleEmitter::CSimpleEmitter( const char *pDebugName ) : CParticleEffect( pDebugName ) { m_pDebugName = pDebugName;
m_flNearClipMin = 16.0f; m_flNearClipMax = 64.0f; } |
Change that to this...
| | | CSimpleEmitter::CSimpleEmitter( const char *pDebugName ) : CParticleEffect( pDebugName ) { m_pDebugName = pDebugName;
m_flNearClipMin = 16.0f; m_flNearClipMax = 64.0f;
m_flFarClipMin = 65536; m_flFarClipMax = 65536; } |
Now our initial values are good to go, which also means all previous calls to CLocalSpaceEmitter::RenderParticles will still work in the same old way, since 65536 is a really long distance that I don't even think is possible in Source without you changing something. If you do that, then you'd have to change the defaults that we've setup so far, so keep that in mind.
But wait, we're not done yet. There's another call to GetAlphaDistanceFade that we need to change like we did to the one in cl_dll\particles_localspace.cpp. This second call to GetAlphaDistanceFade is in the same file that you might still be in.
In cl_dll\particles_simple.cpp, go into void CSimpleEmitter::RenderParticles( CParticleRenderIterator *pIterator ) and look for this (almost the same as above)...
| | | //Render it RenderParticle_ColorSizeAngle( pIterator->GetParticleDraw(), tPos, UpdateColor( pParticle ), UpdateAlpha( pParticle ) * GetAlphaDistanceFade( tPos, m_flNearClipMin, m_flNearClipMax ), UpdateScale( pParticle ), pParticle->m_flRoll ); |
Go ahead and change that to this...
| | | //Render it RenderParticle_ColorSizeAngle( pIterator->GetParticleDraw(), tPos, UpdateColor( pParticle ), UpdateAlpha( pParticle ) * GetAlphaDistanceFade( tPos, m_flNearClipMin, m_flNearClipMax, m_flFarClipMin, m_flFarClipMax ), UpdateScale( pParticle ), pParticle->m_flRoll ); |
So now everything is ready, and you should be all set. However, there is one more thing that you should probably do.
You might have noticed that m_flNearClipMin, m_flNearClipMax, m_flFarClipMin, are m_flFarClipMax are protected inside CSimpleEmitter. Well, m_flNearClipMin and m_flNearClipMax can bet set from the outside with a function called SetNearClip. So what we're going to do next is create SetFarClip to let us do the same thing to m_flFarClipMin and m_flFarClipMax.
In cl_dll\particles_simple.h, look for this...
| | | | void SetNearClip( float nearClipMin, float nearClipMax ); |
We'll add SetFarClip right after that...
| | | | void SetFarClip( float farClipMin, float farClipMax ); |
Now that SetFarClip is declared, let's head into cl_dll\particles_simple.cpp and look for the SetNearClip definition to keep them together. So look for this...
| | | //----------------------------------------------------------------------------- // Purpose: Set the internal near clip range for this particle system // Input : nearClipMin - beginning of clip range // nearClipMax - end of clip range //----------------------------------------------------------------------------- void CSimpleEmitter::SetNearClip( float nearClipMin, float nearClipMax ) { m_flNearClipMin = nearClipMin; m_flNearClipMax = nearClipMax; } |
And add our SetFarClip definition right under that...
| | | //----------------------------------------------------------------------------- // Purpose: Set the internal far clip range for this particle system // Input : farClipMin - beginning of clip range // farClipMax - end of clip range //----------------------------------------------------------------------------- void CSimpleEmitter::SetFarClip( float farClipMin, float farClipMax ) { m_flFarClipMin = farClipMin; m_flFarClipMax = farClipMax; } |
Finally. We should now be done and you can fade particles out when they get a certain distance from you or whatever position you want to test.
One way to test the new particle far clipping/fading is to open cl_dll\sdk\c_sdk_env_sparkler.cpp, go into void C_Sparkler::OnDataChanged( DataUpdateType_t updateType ), and look for this...
| | | // Creat the emitter m_hEmitter = CSimpleEmitter::Create( "env_sparkler" ); |
Under that, add this...
| | | m_hEmitter->SetNearClip(32, 96); m_hEmitter->SetFarClip(160, 192); |
Now just run your mod and open the sdk_fx map. If you don't have it compiled yet, the VMF should be in your mod's mapsrc folder. In the map, you just step on the platform to turn the sparklers on or off.
Also, you might notice that if you don't call SetNearClip, the particle will still fade out when you get close to it. That's because back in the CSimpleEmitter Constructor in cl_dll\particles_simple.cpp, m_flNearClipMin and m_flNearClipMax are defaulted to 16 and 64, respectively. You can change those to 0 and 0, and they won't fade out when you get close to them. I left mine at 16 and 64, though, as I think they're defaulted to fade out when you get close to them to help the fill rate...maybe?
Anyway, I think that's all there is to mention. I hope I didn't forget anything, and I hope you can find a good use for this in your mod.
*UPDATE*
I edited the GetAlphaDistanceFade function just a tad by adding in checks to make sure fadeNearMin and fadeNearMax don't equal each other, and to make sure fadeFarMin and fadeFarMax don't equal each other. That way, fadeNearMax - fadeNearMin and fadeFarMax - fadeFarMin won't ever equal 0, thus getting rid of the potential divide by 0 warnings. Plus, there's actually no reason to do any fading calculations if those variables equal each other.
Yup. |
|