NightVision Tutorial
Adding Nightvision To your Source Single Player Mod
Here's something that is part of Nightfall nightfall, and many people have been stumpled on - Night Vision.
Summary
Before i start, this is only one, and a very early attempt (It works), of one way to get the Night Vision Mode to work with your mod.
Before i start, i would like to credit Gnolfo from hl2coding.com and user:ts2do
Seting up the convar, and the Meat and Spuds
Open the file src/cl_dll/view_scene.cpp, and jump to the end (ctrl+end)
//nightfall - amckern - amckern@yahoo.com //NightVision static void ScreenOver_f( void ) { IMaterial *pMaterial = materials->FindMaterial( "HUDoverlays/nightvision", TEXTURE_GROUP_OTHER, true ); { static bool bDisplayed = false; if( bDisplayed ) { // turn it off view->SetScreenOverlayMaterial( NULL ); engine->ClientCmd( "mat_fullbright 0\n" );//turn full bright off again CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, 0, "Nightfall.NightVisOff" ); } else { // turn it on view->SetScreenOverlayMaterial( pMaterial ); engine->ClientCmd( "mat_fullbright 1\n" );//light up the world CLocalPlayerFilter filter; C_BaseEntity::EmitSound( filter, 0, "Nightfall.NightVisOn" ); } bDisplayed = !bDisplayed; } } static ConCommand r_screenover( "r_screenover", ScreenOver_f );
This is the function we will use to get a pointer to the render target. The '256' numbers are meaningless, they get overridden by RT_SIZE_FULL_FRAME_BUFFER which forces the texture to be the size of the frame buffer.
So now in the file src/cl_dll/rendertexture.h add:
ITexture *GetMoBlurTex0( void );
This will allow us to use this function.
Rendering the Motion Blur
First of all lets add some console commands that we're going to use. I make this console commands because people will want to change them in Garry's Mod. This code can go pretty much anywhere that it can be seen from CViewRender::RenderView( const CViewSetup &view, bool drawViewModel ) in view_scene.cpp.
// To toggle the blur on and off ConVar pp_motionblur("pp_motionblur", "1", 0, "Motion Blur"); // The amount of alpha to use when adding the FB to our custom buffer ConVar pp_motionblur_addalpha("pp_motionblur_addalpha", "0.1", 0, "Motion Blur Alpha"); // The amount of alpha to use when adding our custom buffer to the FB ConVar pp_motionblur_drawalpha("pp_motionblur_drawalpha", "1", 0, "Motion Blur Draw Alpha"); // Delay to add between capturing the FB ConVar pp_motionblur_time("pp_motionblur_time", "0.05", 0, "The amount of time to wait until updating the FB");
All pretty self explanatory.
void CViewRender::DoMotionBlur( void ) { if ( pp_motionblur.GetInt() == 0 ) return; static float fNextDrawTime = 0.0f; bool found; IMaterialVar* mv = NULL; IMaterial *pMatScreen = NULL; ITexture *pMoBlur = NULL; ITexture *pOriginalTexture = NULL; // Get the front buffer material pMatScreen = materials->FindMaterial( "frontbuffer", TEXTURE_GROUP_OTHER, true ); // Get our custom render target pMoBlur = GetMoBlurTex0(); // Store the current render target ITexture *pOriginalRenderTarget = materials->GetRenderTarget(); // Set the camera up so we can draw the overlay int oldX, oldY, oldW, oldH; materials->GetViewport( oldX, oldY, oldW, oldH ); materials->MatrixMode( MATERIAL_PROJECTION ); materials->PushMatrix(); materials->LoadIdentity(); materials->MatrixMode( MATERIAL_VIEW ); materials->PushMatrix(); materials->LoadIdentity(); if( gpGlobals->curtime >= fNextDrawTime ) { UpdateScreenEffectTexture( 0 ); // Set the alpha to whatever our console variable is mv = pMatScreen->FindVar( "$alpha", &found, false ); if (found) { if ( fNextDrawTime == 0 ) { mv->SetFloatValue( 1.0f ); } else { mv->SetFloatValue( pp_motionblur_addalpha.GetFloat() ); } } materials->SetRenderTarget( pMoBlur ); materials->DrawScreenSpaceQuad( pMatScreen ); // Set the next draw time according to the convar fNextDrawTime = gpGlobals->curtime + pp_motionblur_time.GetFloat(); } // Set the alpha mv = pMatScreen->FindVar( "$alpha", &found, false ); if (found) { mv->SetFloatValue( pp_motionblur_drawalpha.GetFloat() ); } // Set the texture to our buffer mv = pMatScreen->FindVar( "$basetexture", &found, false ); if (found) { pOriginalTexture = mv->GetTextureValue(); mv->SetTextureValue( pMoBlur ); } // Pretend we were never here, set everything back materials->SetRenderTarget( pOriginalRenderTarget ); materials->DrawScreenSpaceQuad( pMatScreen ); // Set our texture back to _rt_FullFrameFB if (found) { mv->SetTextureValue( pOriginalTexture ); } materials->DepthRange( 0.0f, 1.0f ); materials->MatrixMode( MATERIAL_PROJECTION ); materials->PopMatrix(); materials->MatrixMode( MATERIAL_VIEW ); materials->PopMatrix(); }
I hope that all makes sense.
Now in the CViewRender::RenderView( const CViewSetup &view, bool drawViewModel ) function which is located in view_scene.cpp, add this:
DoMotionBlur();
Just before
// Draw the 2D graphics
Lastly Add the line:
void CViewRender::DoMotionBlur( void );
Before the end in the viewrender.h file.
Single Player
If your working on a Single Player Mod, you may have found that the front buffer wont update on a map change, or player death, this is because when you change the map, the gpGlobals->curtime variable resets to 0.0 seconds, but your fNextDrawTime retains its value.
So when the blur performs the check:
if ( fNextDrawTime < gpGlobals->curtime )
It will not return true until gpGlobals->curtime catches up - so if you played for 30 mins on the last map, the blur will start working 30 minutes into the new map. To do this you need to reset your fNextDrawTime on a map change by using the hack below, or don't use this check at all.
if ( fNextDrawTime - gpGlobals->curtime > 1.0f) { fNextDrawTime = 0.0f; }
This code sits above this line
if( gpGlobals->curtime >= fNextDrawTime )
Material Files
That should do it. You need to add a material file called frontbuffer.vmt in your materials folder - although you could probably use an existing one. Here's what mine looks like:
"UnlitGeneric" { "$basetexture" "_rt_FullFrameFB" "$ignorez" 1 }
Finished
I chose this tutorial to do because it covered a few things that people continually ask me about. I hope it helps someone.
Garry Newman