Viewmodel Blood Splatter Overlay: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (GamerDude27 moved page Blood spray on weapons and hands to Viewmodel Blood Splatter Overlay: Title and capitalization doesn't match the rest of the wiki)
m (Replaced "WL_Feet" with "WL_Waist", so the blood doesn't instantly clean when touching *any* water. Now it only does it when fully underwater + added a note to the external links section.)
 
(7 intermediate revisions by 3 users not shown)
Line 3: Line 3:
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.
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.


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.
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 ==
== Requirements ==
Line 13: Line 13:
== What You Will Learn ==
== What You Will Learn ==


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


== The Implementation ==
== The Implementation ==


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


On the client-side, create a file called '''bloodoverlayproxy.cpp''' and copypaste the following in it:
<source lang=cpp>
//========= Copyright Bernt Andreas Eide, All rights reserved. ============//
//
// Purpose: When you take damage or you damage someone and blood splats on
//          you then you'll draw that blood on your hands/weapon. (overlay)
//
//=============================================================================//
#include "cbase.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imaterialproxy.h"
#include "baseviewmodel_shared.h"
#include "c_baseplayer.h"
#include "toolframework_client.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class C_BloodyTextureProxy : public IMaterialProxy
{
public:
C_BloodyTextureProxy();
virtual ~C_BloodyTextureProxy();
virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
C_BaseEntity *BindArgToEntity( void *pArg );
virtual void OnBind( void *pC_BaseEntity );
virtual void Release() { delete this; }
IMaterial *GetMaterial();
private:
IMaterialVar *m_pBlendFactor;
};
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
C_BloodyTextureProxy::C_BloodyTextureProxy()
{
m_pBlendFactor = NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
C_BloodyTextureProxy::~C_BloodyTextureProxy()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_BloodyTextureProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
bool found;


m_pBlendFactor = pMaterial->FindVar( "$detailblendfactor", &found, false );
Which you'll put into '''<src code directory>/src/game/client/'''
if ( !found )
return false;
 
return true;
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
C_BaseEntity *C_BloodyTextureProxy::BindArgToEntity( void *pArg )
{
IClientRenderable *pRend = (IClientRenderable *)pArg;
return pRend ? pRend->GetIClientUnknown()->GetBaseEntity() : NULL;
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BloodyTextureProxy::OnBind( void *pC_BaseEntity )
{
if ( !pC_BaseEntity )
return;
 
C_BaseEntity *pEntity = BindArgToEntity( pC_BaseEntity );
C_BaseViewModel *pViewModel = dynamic_cast<C_BaseViewModel *>( pEntity );
if ( pViewModel )
{
C_BasePlayer *pOwner = ToBasePlayer( pViewModel->GetOwner() );
if ( pOwner )
m_pBlendFactor->SetFloatValue( pOwner->m_bShouldDrawBloodOverlay ? 1.0f : 0.0f );
}
 
if ( ToolsEnabled() )
ToolFramework_RecordMaterialParams( GetMaterial() );
}
 
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
IMaterial *C_BloodyTextureProxy::GetMaterial()
{
return m_pBlendFactor->GetOwningMaterial();
}
 
EXPOSE_INTERFACE( C_BloodyTextureProxy, IMaterialProxy, "BloodyTexture" IMATERIAL_PROXY_INTERFACE_VERSION );
</source>


=== c_baseplayer.h ===
=== c_baseplayer.h ===
Line 136: Line 35:
In '''c_baseplayer.cpp''', below <code>RecvPropString( RECVINFO(m_szLastPlaceName) ),</code> add:
In '''c_baseplayer.cpp''', below <code>RecvPropString( RECVINFO(m_szLastPlaceName) ),</code> add:
<source lang=cpp>
<source lang=cpp>
RecvPropBool( RECVINFO(m_bShouldDrawBloodOverlay) ),
RecvPropBool( RECVINFO( m_bShouldDrawBloodOverlay ) ),
</source>
</source>


Line 153: Line 52:
=== player.cpp ===
=== player.cpp ===


In '''player.cpp''', inside the constructor and below <code>m_bitsDamageType = 0;</code>, add:
In '''player.cpp''', somewhere inside of <code>BEGIN_DATADESC( CBasePlayer )</code>, add:
<source lang=cpp>
<source lang=cpp>
m_bShouldDrawBloodOverlay = false;
DEFINE_FIELD( m_bShouldDrawBloodOverlay, FIELD_BOOLEAN ),
</source>
</source>


At the bottom of the <code>InitialSpawn( void )</code> function, add:
Inside the constructor and below <code>m_bitsDamageType = 0;</code>, add:
<source lang=cpp>
<source lang=cpp>
m_bShouldDrawBloodOverlay = false; // Reset blood overlay
m_bShouldDrawBloodOverlay = false;
</source>
</source>


Line 216: Line 115:
== Conclusion ==
== Conclusion ==


Now all that remains is making your own blood detail texture for this, but for convenience/testing sake, you can download the included placeholder texture found in the "External links" section down below.
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 <code>WaterMove()</code> function.
 
Inside it, at the bottom, but before <code>UpdateUnderwaterState();</code> add this:
<source lang=cpp>
if ( GetWaterLevel() > WL_Waist )
m_bShouldDrawBloodOverlay = false;
</source>


And that's all there is to it, if set up right you should now be able to see the blood splatter texture appear on your viewmodel based on the snippet of code we placed into '''basecombatcharacter.cpp'''.
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>
m_bShouldDrawBloodOverlay = false;
</source>


== External links ==
== External links ==


[https://mega.nz/file/RsNm2DgK#qHysT50ZYbX_WyXg8FcnyO7Bwb88lfHE1qlXo__9FAI Download BloodDetailTexture.rar] - A placeholder blood detail texture by [[User:GamerDude27|Ian B.]]
[https://mega.nz/file/RsNm2DgK#qHysT50ZYbX_WyXg8FcnyO7Bwb88lfHE1qlXo__9FAI Download BloodDetailTexture.rar] - A placeholder blood detail texture by [[User:GamerDude27|Ian B.]]
{{Note|If you're gonna use this placeholder texture, make sure to rename "blood_hands" to "blood_detail".}}


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

Latest revision as of 14:15, 11 October 2023

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 SDK 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 c_baseplayer.h, inside the public: section, add the following line:

	bool m_bShouldDrawBloodOverlay;

c_baseplayer.cpp

In 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 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 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 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.

Note.pngNote:If you're gonna use this placeholder texture, make sure to rename "blood_hands" to "blood_detail".