Viewmodel Blood Splatter Overlay: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Restored the article)
 
(Cleanup and QoL changes)
Line 1: Line 1:
=Introduction=
== Introduction ==


This is a tutorial for a simple material proxy system which we will use to switch viewmodel textures dependent on CQB.
This is a tutorial for a simple material proxy system that we will use to switch viewmodel textures dependent on CQB.
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===
Final results that could 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.
* Beginning/Intermediate knowledge of C++;
* Source SDK 2007 engine branch (2013 compatable version in process of writing);
* 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 ==


* Beginner/Intermediate knowledge of C++.
* Source SDK 2007 engine branch (2013 compatible version in process of writing).
* Knowledge and familiarity with textures and materials.




===What You Will Learn===
{{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 community's help with this! '''Please update this page'''.}}
* To create a new material proxy system using a ''$detail'' variable
* To create an overlay of blood onto a viewmodel after shooting flesh or blood materials (NPCs included of course) at close quarters
 
 
 
===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]]
 


=Tutorial=
== What You Will Learn ==


* To create 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.


== Known Bugs ==


==Step 1: Programming==
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.


Create on the Client, ''proxy_blood.cpp''
[[Image:Blood proxy System.jpg]]


'''proxy_blood.cpp'''<br>
== Tutorial ==
=== Step 1: Programming ===
==== proxy_blood.cpp ====
On the client-side, create a file called '''proxy_blood.cpp''':
<source lang=cpp>
<source lang=cpp>
#include "cbase.h"
#include "cbase.h"
Line 44: Line 38:
#include "FunctionProxy.h"
#include "FunctionProxy.h"
#include "toolframework_client.h"
#include "toolframework_client.h"


// memdbgon must be the last include file in a .cpp file!!!
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#include "tier0/memdbgon.h"


// forward declarations
// Forward declarations
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );


ConVar cl_viewmodel_blood("cl_viewmodel_blood", "0");
ConVar cl_viewmodel_blood( "cl_viewmodel_blood", "0" );


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose:  
// Purpose:  
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool C_BaseEntity::IsCoveredInBlood( void )  
bool C_BaseEntity::IsCoveredInBlood()  
{  
{  
return cl_viewmodel_blood.GetBool();  
return cl_viewmodel_blood.GetBool();  
Line 65: Line 58:
// Purpose:  
// Purpose:  
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
C_BaseEntity *C_BaseViewModel::GetCoveredBloodEntity( void )
C_BaseEntity *C_BaseViewModel::GetCoveredBloodEntity()
{
{
C_BaseEntity *pWeapon = m_hWeapon.Get();
C_BaseEntity *pWeapon = m_hWeapon.Get();
if (pWeapon)
if ( pWeapon )
{
{
return pWeapon;
return pWeapon;
Line 87: Line 80:
void OnBind( void *pC_BaseEntity );
void OnBind( void *pC_BaseEntity );
IMaterial *GetMaterial();
IMaterial *GetMaterial();
virtual void Release( void ) { delete this; }
virtual void Release() { delete this; }


private:
private:
Line 115: Line 108:
bool bFound;
bool bFound;
m_pDetailBlendFactor = pMaterial->FindVar( "$detailblendfactor", &bFound );
m_pDetailBlendFactor = pMaterial->FindVar( "$detailblendfactor", &bFound );
if (!bFound)
if ( !bFound )
{
{
m_pDetailBlendFactor = NULL;
m_pDetailBlendFactor = NULL;
Line 132: Line 125:
{
{
IMaterial *pMaterial = GetMaterial();
IMaterial *pMaterial = GetMaterial();
if (pMaterial)
if ( pMaterial )
{
{
if (pRenderable)
if ( pRenderable )
{
{
IClientRenderable *pRend = ( IClientRenderable* )pRenderable;
IClientRenderable *pRend = (IClientRenderable *)pRenderable;
C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity();
C_BaseEntity *pEnt = pRend->GetIClientUnknown()->GetBaseEntity();
if ( pEnt )
if ( pEnt )
Line 142: Line 135:
C_BaseEntity *pWeapon = pEnt->GetCoveredBloodEntity();
C_BaseEntity *pWeapon = pEnt->GetCoveredBloodEntity();


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


 
if ( pWeapon->IsCoveredInBlood() )
if (pWeapon->IsCoveredInBlood())
{
{
m_pDetailBlendFactor->SetFloatValue( 1.0f );
m_pDetailBlendFactor->SetFloatValue( 1.0f );
Line 157: Line 149:
else
else
{
{
  m_pDetailBlendFactor->SetFloatValue( 0.0f );
m_pDetailBlendFactor->SetFloatValue( 0.0f );
  }
}
}
}
else
else
Line 185: Line 177:
</source>
</source>


In ''c_baseentity.h'' add the following lines
==== c_baseentity.h ====
 
In '''c_baseentity.h''', inside the '''public:''' section add the following lines:
'''c_baseentity.h'''<br>
<source lang=cpp>
<source lang=cpp>
virtual C_BaseEntity   *GetCoveredBloodEntity( void ) { return this; }
virtual C_BaseEntity *GetCoveredBloodEntity() { return this; }
virtual bool     IsCoveredInBlood( void );
virtual bool IsCoveredInBlood();
</source>
</source>
In ''class C_BaseEntity : public IClientEntity'' above ''virtual ~C_BaseEntity();'' in ''public''


Tutorial being made now...
Tutorial being made now...


[[Category: Programming]] [[Category: Tutorials]] [[Category: Weapons]] [[Category: Miscellaneous]]
[[Category: Programming]] [[Category: Tutorials]] [[Category: Weapons]] [[Category: Miscellaneous]]

Revision as of 17:30, 8 January 2021

Introduction

This is a tutorial for a simple material proxy system that we will use to switch viewmodel textures dependent on CQB.

Final results that could 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

  • Beginner/Intermediate knowledge of C++.
  • Source SDK 2007 engine branch (2013 compatible version in process of writing).
  • Knowledge and familiarity with textures and materials.


Note.pngNote: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 community's help with this! Please update this page.

What You Will Learn

  • To create 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.

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.

Blood proxy System.jpg

Tutorial

Step 1: Programming

proxy_blood.cpp

On the client-side, create a file called proxy_blood.cpp:

#include "cbase.h"
#include <KeyValues.h>
#include "materialsystem/IMaterialVar.h"
#include "materialsystem/IMaterial.h"
#include "materialsystem/ITexture.h"
#include "materialsystem/IMaterialSystem.h"
#include "FunctionProxy.h"
#include "toolframework_client.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

// Forward declarations
void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );

ConVar cl_viewmodel_blood( "cl_viewmodel_blood", "0" );

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

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
C_BaseEntity *C_BaseViewModel::GetCoveredBloodEntity()
{
	C_BaseEntity *pWeapon = m_hWeapon.Get();
	if ( pWeapon )
	{
		return pWeapon;
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// 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() { delete this; }

private:
	IMaterialVar	*m_pDetailBlendFactor;

	IMaterial		*m_pMaterial;

	bool			m_bPlayerBlood;
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CViewmodelBlood_Proxy::CViewmodelBlood_Proxy()
{
	m_pMaterial = NULL;
	m_pDetailBlendFactor = NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CViewmodelBlood_Proxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
{
	m_pMaterial = pMaterial;

	bool bFound;
	m_pDetailBlendFactor = pMaterial->FindVar( "$detailblendfactor", &bFound );
	if ( !bFound )
	{
		m_pDetailBlendFactor = NULL;
		return false;
	}

	m_bPlayerBlood = pKeyValues->GetInt( "player", 0 ) > 0;

	return true;
}

//-----------------------------------------------------------------------------
// 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 )
				{
					CBaseEntity *pTarget = m_bPlayerBlood ? pWeapon->GetOwnerEntity() : NULL;
					if ( pTarget )
					{
						pWeapon = pTarget;
					}

					if ( pWeapon->IsCoveredInBlood() )
					{
						m_pDetailBlendFactor->SetFloatValue( 1.0f );
					}
					else
					{
						m_pDetailBlendFactor->SetFloatValue( 0.0f );
					}
				}
				else
				{
					m_pDetailBlendFactor->SetFloatValue( 0.0f );
				}
			}
		}

		if ( ToolsEnabled() )
		{
			ToolFramework_RecordMaterialParams( GetMaterial() );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
IMaterial *CViewmodelBlood_Proxy::GetMaterial()
{
	return m_pMaterial;
}

EXPOSE_INTERFACE( CViewmodelBlood_Proxy, IMaterialProxy, "ViewmodelBloodProxy" IMATERIAL_PROXY_INTERFACE_VERSION );

c_baseentity.h

In c_baseentity.h, inside the public: section add the following lines:

	virtual C_BaseEntity *GetCoveredBloodEntity() { return this; }
	virtual bool IsCoveredInBlood();

Tutorial being made now...