NightVision Tutorial

From Valve Developer Community
Revision as of 07:47, 9 November 2005 by Amckern (talk | contribs) (New Page - YAY!)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
Garry motionblur.jpg

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 );


 static CTextureReference s_pMoBlurTex0;
 
 ITexture *GetMoBlurTex0( void )
 {
 	if( !s_pMoBlurTex0 )
 	{
 		s_pMoBlurTex0.InitRenderTarget( 256, 256, RT_SIZE_FULL_FRAME_BUFFER,
			IMAGE_FORMAT_ARGB8888, MATERIAL_RT_DEPTH_NONE );
 		Assert( !IsErrorTexture( s_pMoBlurTex0 ) );
 	}
 
 	return s_pMoBlurTex0;
 }

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