Viewmodel Blood Splatter Overlay: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Restored the article)
 
m (Link to "material proxies" page)
 
(13 intermediate revisions by 4 users not shown)
Line 1: Line 1:
=Introduction=
[[File:viewmodel blood proxy system.jpg|440px|thumb|right|<center>'''Example'''</center>]]


This is a tutorial for a simple material proxy system which we will use to switch viewmodel textures dependent on CQB.
== Introduction ==
Final results that could be made from this are for example, hands and weapon covered in goo, acid, blood, water, animated raindrop water normals, and many more visual design elements.


===Requirements===
In this tutorial, we'll be setting up a simple [[Material_proxies|material proxy system]] that we can use to overlay a blood detail texture onto our viewmodel based on close-quarters combat.
* Beginning/Intermediate knowledge of C++;
 
* Source SDK 2007 engine branch (2013 compatable version in process of writing);
The final results that can be made from this are, for example, hands and weapons covered in goo, acid, blood, water, animated raindrop water normals, and many more visual design elements.
* Knowledge and familiarity with textures and materials.


{{note|This tutorial was written on the 2007 engine branch due to many issues within the port of this gameplay mechanic to the 2013 branch. I am seeking the communitie's help with this! *Please update this page*}}
== Requirements ==


* A {{src13|3.1}} engine branch mod.
* Beginner/Intermediate knowledge of C++.
* Knowledge and familiarity with textures and materials.


== What You Will Learn ==


===What You Will Learn===
* Creating a new material proxy system using the [[$detail]] parameter.
* To create a new material proxy system using a ''$detail'' variable
* Creating an overlay of blood onto a viewmodel after shooting flesh or blood materials (NPCs included, of course) at close quarters.
* To create an overlay of blood onto a viewmodel after shooting flesh or blood materials (NPCs included of course) at close quarters


== The Implementation ==


Before starting, we'll need this file:
* [[Viewmodel Blood Splatter Overlay/bloodoverlayproxy.cpp|bloodoverlayproxy.cpp]]


===Known Bugs===
Without using different names for materials and textures relating to multiple weapons, a blood overlay switch will happen to all weapons -
Compile models with different material name associations.


[[Image:Blood proxy System]]
Which you'll put into {{path|<src code directory>/src/game/client/}}


=== c_baseplayer.h ===


=Tutorial=
In {{path|src/game/client/c_baseplayer|h}}, inside the <code>public:</code> section, add the following line:
<syntaxhighlight lang=cpp>
bool m_bShouldDrawBloodOverlay;
</syntaxhighlight>


=== c_baseplayer.cpp ===


In {{path|src/game/client/c_baseplayer|cpp}}, below <code>RecvPropString( RECVINFO(m_szLastPlaceName) ),</code> add:
<syntaxhighlight lang=cpp>
RecvPropBool( RECVINFO( m_bShouldDrawBloodOverlay ) ),
</syntaxhighlight>


==Step 1: Programming==
Then in the constructor, below <code>ListenForGameEvent( "base_player_teleported" );</code>, add:
<syntaxhighlight lang=cpp>
m_bShouldDrawBloodOverlay = false;
</syntaxhighlight>


Create on the Client, ''proxy_blood.cpp''
=== player.h ===


'''proxy_blood.cpp'''<br>
In {{path|src/game/server/player|h}}, inside the <code>public:</code> section, add the following line:
<source lang=cpp>
<syntaxhighlight lang=cpp>
#include "cbase.h"
CNetworkVar( bool, m_bShouldDrawBloodOverlay ); // Have we been hit or have blood splatted on us?
#include <KeyValues.h>
</syntaxhighlight>
#include "materialsystem/IMaterialVar.h"
#include "materialsystem/IMaterial.h"
#include "materialsystem/ITexture.h"
#include "materialsystem/IMaterialSystem.h"
#include "FunctionProxy.h"
#include "toolframework_client.h"


=== player.cpp ===


// memdbgon must be the last include file in a .cpp file!!!
In {{path|src/game/server/player|cpp}}, somewhere inside of <code>BEGIN_DATADESC( CBasePlayer )</code>, add:
#include "tier0/memdbgon.h"
<syntaxhighlight lang=cpp>
DEFINE_FIELD( m_bShouldDrawBloodOverlay, FIELD_BOOLEAN ),
</syntaxhighlight>


// forward declarations
Inside the constructor and below <code>m_bitsDamageType = 0;</code>, add:
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
<syntaxhighlight lang=cpp>
m_bShouldDrawBloodOverlay = false;
</syntaxhighlight>


ConVar cl_viewmodel_blood("cl_viewmodel_blood", "0");
Then inside <code>IMPLEMENT_SERVERCLASS_ST( CBasePlayer, DT_BasePlayer )</code>, below <code>SendPropString (SENDINFO(m_szLastPlaceName) ),</code>, add:
<syntaxhighlight lang=cpp>
SendPropBool( SENDINFO(m_bShouldDrawBloodOverlay) ),
</syntaxhighlight>


//-----------------------------------------------------------------------------
=== basecombatcharacter.cpp ===
// Purpose:
//-----------------------------------------------------------------------------
bool C_BaseEntity::IsCoveredInBlood( void )
{
return cl_viewmodel_blood.GetBool();
}


//-----------------------------------------------------------------------------
In {{path|src/game/server/basecombatcharacter|cpp}}, inside the <code>OnTakeDamage_Alive( const CTakeDamageInfo &info )</code> function above <code>return 1;</code>, add:
// Purpose:  
<syntaxhighlight lang=cpp>
//-----------------------------------------------------------------------------
// Handle the viewmodel blood splatter overlay effect here:
C_BaseEntity *C_BaseViewModel::GetCoveredBloodEntity( void )
if ( ( info.GetDamageType() & ( DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_BUCKSHOT ) ) )
{
C_BaseEntity *pWeapon = m_hWeapon.Get();
if (pWeapon)
{
{
return pWeapon;
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
if ( pPlayer == this )
pPlayer->m_bShouldDrawBloodOverlay = true;
 
pPlayer = ToBasePlayer( info.GetAttacker() );
if ( pPlayer && ( this->BloodColor() != BLOOD_COLOR_MECH ) )
{
if ( pPlayer->GetAbsOrigin().DistTo( this->GetAbsOrigin() ) < 200.0f )
pPlayer->m_bShouldDrawBloodOverlay = true;
}
}
}
</syntaxhighlight>


return NULL;
== The Materials ==
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CViewmodelBlood_Proxy : public IMaterialProxy
{
public:
CViewmodelBlood_Proxy();
 
bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
void OnBind( void *pC_BaseEntity );
IMaterial *GetMaterial();
virtual void Release( void ) { delete this; }
 
private:
IMaterialVar *m_pDetailBlendFactor;
 
IMaterial *m_pMaterial;


bool m_bPlayerBlood;
=== Viewmodel VMTs ===
};


//-----------------------------------------------------------------------------
Now you'll have to edit every single viewmodel [[VMT|'''.vmt''']] to include the following snippet of code:
// Purpose:  
<syntaxhighlight lang="text" highlight=5-15>
//-----------------------------------------------------------------------------
"VertexLitGeneric"
CViewmodelBlood_Proxy::CViewmodelBlood_Proxy()
{
{
m_pMaterial = NULL;
...
m_pDetailBlendFactor = NULL;
}


//-----------------------------------------------------------------------------
"$detail" "detail/blood_detail"
// Purpose:
"$detailblendmode" "2"
//-----------------------------------------------------------------------------
"$detailblendfactor" "0.0"
bool CViewmodelBlood_Proxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
"$detailscale" "1.0"
{
m_pMaterial = pMaterial;


bool bFound;
"Proxies"
m_pDetailBlendFactor = pMaterial->FindVar( "$detailblendfactor", &bFound );
if (!bFound)
{
{
m_pDetailBlendFactor = NULL;
"BloodyTexture"
return false;
{
}
}
}
m_bPlayerBlood = pKeyValues->GetInt( "player", 0 ) > 0;
return true;
}
}
</syntaxhighlight>


//-----------------------------------------------------------------------------
Notice how we're calling for {{path|materials/detail/blood_detail|vtf}}, this is the blood detail texture that gets placed on top of our viewmodel.
// Purpose:
//-----------------------------------------------------------------------------
void CViewmodelBlood_Proxy::OnBind( void *pRenderable )
{
IMaterial *pMaterial = GetMaterial();
if (pMaterial)
{
if (pRenderable)
{
IClientRenderable *pRend = ( IClientRenderable* )pRenderable;
C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity();
if ( pEnt )
{
C_BaseEntity *pWeapon = pEnt->GetCoveredBloodEntity();


if (pWeapon)
== Conclusion ==
{
CBaseEntity *pTarget = m_bPlayerBlood ? pWeapon->GetOwnerEntity() : NULL;
if (pTarget)
{
pWeapon = pTarget;
}


Now all that remains is making your blood detail texture for this. Still, for convenience/testing sake, you can download the included placeholder texture found in the [[#External links|"External links"]] section below.


if (pWeapon->IsCoveredInBlood())
And that's all there is to it. If set up correctly, you should now be able to see the blood splatter texture on your viewmodel based on the snippet of code we placed into {{path|basecombatcharacter|cpp}}.
{
m_pDetailBlendFactor->SetFloatValue( 1.0f );
}
else
{
  m_pDetailBlendFactor->SetFloatValue( 0.0f );
  }
}
else
{
m_pDetailBlendFactor->SetFloatValue( 0.0f );
}
}
}


if ( ToolsEnabled() )
== Additional Functionality (Optional) ==
{
ToolFramework_RecordMaterialParams( GetMaterial() );
}
}
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
IMaterial *CViewmodelBlood_Proxy::GetMaterial()
{
return m_pMaterial;
}


EXPOSE_INTERFACE( CViewmodelBlood_Proxy, IMaterialProxy, "ViewmodelBloodProxy" IMATERIAL_PROXY_INTERFACE_VERSION );
If you want to be able to clean off the blood by going into water, head over to {{path|player|cpp}} and find the <code>WaterMove()</code> function.
</source>


In ''c_baseentity.h'' add the following lines
Inside it, at the bottom, but before <code>UpdateUnderwaterState();</code> add this:
<syntaxhighlight lang=cpp>
if ( GetWaterLevel() > WL_Waist )
m_bShouldDrawBloodOverlay = false;
</syntaxhighlight>


'''c_baseentity.h'''<br>
And if you want to be able to clean off the blood when consuming a health kit, find the <code>TakeHealth( float flHealth, int bitsDamageType )</code> function and add this inside of it:
<source lang=cpp>
<syntaxhighlight lang=cpp>
virtual C_BaseEntity  *GetCoveredBloodEntity( void ) { return this; }
m_bShouldDrawBloodOverlay = false;
virtual bool    IsCoveredInBlood( void );
</syntaxhighlight>
</source>


In ''class C_BaseEntity : public IClientEntity'' above ''virtual ~C_BaseEntity();'' in ''public''
== External links ==


Tutorial being made now...
[https://mega.nz/file/Z4FWiCqZ#XADDJemzg94BkV6B-6EAdYlUIv2Es0yx4mSIde4ZA8o Download BloodDetailTexture.rar] - A placeholder blood detail texture by [[User:GamerDude27|Ian B.]]


[[Category: Programming]] [[Category: Tutorials]] [[Category: Weapons]] [[Category: Miscellaneous]]
[[Category:Programming]]
[[Category:Tutorials]]
[[Category:Free source code]]

Latest revision as of 11:03, 28 September 2025

Example

Introduction

In this tutorial, we'll be setting up a simple material proxy system that we can use to overlay a blood detail texture onto our viewmodel based on close-quarters combat.

The final results that can be made from this are, for example, hands and weapons covered in goo, acid, blood, water, animated raindrop water normals, and many more visual design elements.

Requirements

  • A Source 2013 engine branch mod.
  • Beginner/Intermediate knowledge of C++.
  • Knowledge and familiarity with textures and materials.

What You Will Learn

  • Creating a new material proxy system using the $detail parameter.
  • Creating an overlay of blood onto a viewmodel after shooting flesh or blood materials (NPCs included, of course) at close quarters.

The Implementation

Before starting, we'll need this file:


Which you'll put into 🖿<src code directory>/src/game/client/

c_baseplayer.h

In 🖿src/game/client/c_baseplayer.h, inside the public: section, add the following line:

	bool m_bShouldDrawBloodOverlay;

c_baseplayer.cpp

In 🖿src/game/client/c_baseplayer.cpp, below RecvPropString( RECVINFO(m_szLastPlaceName) ), add:

		RecvPropBool( RECVINFO( m_bShouldDrawBloodOverlay ) ),

Then in the constructor, below ListenForGameEvent( "base_player_teleported" );, add:

	m_bShouldDrawBloodOverlay = false;

player.h

In 🖿src/game/server/player.h, inside the public: section, add the following line:

	CNetworkVar( bool, m_bShouldDrawBloodOverlay ); // Have we been hit or have blood splatted on us?

player.cpp

In 🖿src/game/server/player.cpp, somewhere inside of BEGIN_DATADESC( CBasePlayer ), add:

	DEFINE_FIELD( m_bShouldDrawBloodOverlay, FIELD_BOOLEAN ),

Inside the constructor and below m_bitsDamageType = 0;, add:

	m_bShouldDrawBloodOverlay = false;

Then inside IMPLEMENT_SERVERCLASS_ST( CBasePlayer, DT_BasePlayer ), below SendPropString (SENDINFO(m_szLastPlaceName) ),, add:

	SendPropBool( SENDINFO(m_bShouldDrawBloodOverlay) ),

basecombatcharacter.cpp

In 🖿src/game/server/basecombatcharacter.cpp, inside the OnTakeDamage_Alive( const CTakeDamageInfo &info ) function above return 1;, add:

	// Handle the viewmodel blood splatter overlay effect here:
	if ( ( info.GetDamageType() & ( DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_BUCKSHOT ) ) )
	{
		CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
		if ( pPlayer == this )
			pPlayer->m_bShouldDrawBloodOverlay = true;

		pPlayer = ToBasePlayer( info.GetAttacker() );
		if ( pPlayer && ( this->BloodColor() != BLOOD_COLOR_MECH ) )
		{
			if ( pPlayer->GetAbsOrigin().DistTo( this->GetAbsOrigin() ) < 200.0f )
				pPlayer->m_bShouldDrawBloodOverlay = true;
		}
	}

The Materials

Viewmodel VMTs

Now you'll have to edit every single viewmodel .vmt to include the following snippet of code:

"VertexLitGeneric"
{
	...

	"$detail" "detail/blood_detail"
	"$detailblendmode" "2"
	"$detailblendfactor" "0.0"
	"$detailscale" "1.0"

	"Proxies"
	{
		"BloodyTexture"
		{
		}
	}
}

Notice how we're calling for 🖿materials/detail/blood_detail.vtf, this is the blood detail texture that gets placed on top of our viewmodel.

Conclusion

Now all that remains is making your blood detail texture for this. Still, for convenience/testing sake, you can download the included placeholder texture found in the "External links" section below.

And that's all there is to it. If set up correctly, you should now be able to see the blood splatter texture on your viewmodel based on the snippet of code we placed into 🖿basecombatcharacter.cpp.

Additional Functionality (Optional)

If you want to be able to clean off the blood by going into water, head over to 🖿player.cpp and find the WaterMove() function.

Inside it, at the bottom, but before UpdateUnderwaterState(); add this:

	if ( GetWaterLevel() > WL_Waist )
		m_bShouldDrawBloodOverlay = false;

And if you want to be able to clean off the blood when consuming a health kit, find the TakeHealth( float flHealth, int bitsDamageType ) function and add this inside of it:

	m_bShouldDrawBloodOverlay = false;

External links

Download BloodDetailTexture.rar - A placeholder blood detail texture by Ian B.