Viewmodel Blood Splatter Overlay

From Valve Developer Community
Revision as of 14:23, 8 January 2021 by GamerDude27 (talk | contribs) (Restored the article)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Introduction

This is a tutorial for a simple material proxy system which 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

  • 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.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 communitie's help with this! *Please update this page*


What You Will Learn

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

File:Blood proxy System


Tutorial

Step 1: Programming

Create on the Client, proxy_blood.cpp

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( void ) 
{ 
	return cl_viewmodel_blood.GetBool(); 
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
C_BaseEntity *C_BaseViewModel::GetCoveredBloodEntity( void )
{
	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( void ) { 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 );

In c_baseentity.h add the following lines

c_baseentity.h

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

In class C_BaseEntity : public IClientEntity above virtual ~C_BaseEntity(); in public

Tutorial being made now...