This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+238
View File
@@ -0,0 +1,238 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "flexrenderdata.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CCachedRenderData::CCachedRenderData() : m_CurrentTag(0), m_pFirstFlexIndex(0),
m_pFirstWorldIndex(0)
{
#ifdef _DEBUG
int i;
float val = VEC_T_NAN;
for( i = 0; i < MAXSTUDIOFLEXVERTS; i++ )
{
m_pFlexVerts[i].m_Position[0] = val;
m_pFlexVerts[i].m_Position[1] = val;
m_pFlexVerts[i].m_Position[2] = val;
m_pFlexVerts[i].m_Normal[0] = val;
m_pFlexVerts[i].m_Normal[1] = val;
m_pFlexVerts[i].m_Normal[2] = val;
m_pThinFlexVerts[i].m_Position[0] = val;
m_pThinFlexVerts[i].m_Position[1] = val;
m_pThinFlexVerts[i].m_Position[2] = val;
m_pThinFlexVerts[i].m_Normal[0] = val;
m_pThinFlexVerts[i].m_Normal[1] = val;
m_pThinFlexVerts[i].m_Normal[2] = val;
m_pFlexVerts[i].m_TangentS[0] = val;
m_pFlexVerts[i].m_TangentS[1] = val;
m_pFlexVerts[i].m_TangentS[2] = val;
m_pFlexVerts[i].m_TangentS[3] = val;
}
#endif
}
//-----------------------------------------------------------------------------
// Call this before rendering the model
//-----------------------------------------------------------------------------
void CCachedRenderData::StartModel()
{
++m_CurrentTag;
m_IndexCount = 0;
m_FlexVertexCount = 0;
m_ThinFlexVertexCount = 0;
m_WorldVertexCount = 0;
m_pFirstFlexIndex = 0;
m_pFirstThinFlexIndex = 0;
m_pFirstWorldIndex = 0;
}
//-----------------------------------------------------------------------------
// Used to hook ourselves into a particular body part, model, and mesh
//-----------------------------------------------------------------------------
void CCachedRenderData::SetBodyPart( int bodypart )
{
m_Body = bodypart;
m_CacheDict.EnsureCount(m_Body+1);
m_Model = m_Mesh = -1;
m_pFirstFlexIndex = 0;
m_pFirstThinFlexIndex = 0;
m_pFirstWorldIndex = 0;
}
void CCachedRenderData::SetModel( int model )
{
Assert(m_Body >= 0);
m_Model = model;
m_CacheDict[m_Body].EnsureCount(m_Model+1);
m_Mesh = -1;
m_pFirstFlexIndex = 0;
m_pFirstThinFlexIndex = 0;
m_pFirstWorldIndex = 0;
}
void CCachedRenderData::SetMesh( int mesh )
{
Assert((m_Model >= 0) && (m_Body >= 0));
m_Mesh = mesh;
m_CacheDict[m_Body][m_Model].EnsureCount(m_Mesh+1);
// At this point, we should have all 3 defined.
CacheDict_t& dict = m_CacheDict[m_Body][m_Model][m_Mesh];
if (dict.m_Tag == m_CurrentTag)
{
m_pFirstFlexIndex = &m_pFlexIndex[dict.m_FirstIndex];
m_pFirstThinFlexIndex = &m_pThinFlexIndex[dict.m_FirstIndex];
m_pFirstWorldIndex = &m_pWorldIndex[dict.m_FirstIndex];
}
else
{
m_pFirstFlexIndex = 0;
m_pFirstThinFlexIndex = 0;
m_pFirstWorldIndex = 0;
}
}
//-----------------------------------------------------------------------------
// Used to set up a flex computation
//-----------------------------------------------------------------------------
bool CCachedRenderData::IsFlexComputationDone( ) const
{
Assert((m_Model >= 0) && (m_Body >= 0) && (m_Mesh >= 0));
// Lets create the dictionary entry
// If the tags match, that means we're doing the computation twice!!!
CacheDict_t const& dict = m_CacheDict[m_Body][m_Model][m_Mesh];
return (dict.m_FlexTag == m_CurrentTag);
}
//-----------------------------------------------------------------------------
// Used to set up a computation (modifies vertex data)
//-----------------------------------------------------------------------------
void CCachedRenderData::SetupComputation( mstudiomesh_t *pMesh, bool flexComputation )
{
Assert((m_Model >= 0) && (m_Body >= 0) && (m_Mesh >= 0));
// Assert( !m_pFirstIndex );
// Lets create the dictionary entry
// If the tags match, that means we're doing the computation twice!!!
CacheDict_t& dict = m_CacheDict[m_Body][m_Model][m_Mesh];
if (dict.m_Tag != m_CurrentTag)
{
dict.m_FirstIndex = m_IndexCount;
dict.m_IndexCount = pMesh->numvertices;
dict.m_Tag = m_CurrentTag;
m_IndexCount += dict.m_IndexCount;
}
if (flexComputation)
dict.m_FlexTag = m_CurrentTag;
m_pFirstFlexIndex = &m_pFlexIndex[dict.m_FirstIndex];
m_pFirstThinFlexIndex = &m_pThinFlexIndex[dict.m_FirstIndex];
m_pFirstWorldIndex = &m_pWorldIndex[dict.m_FirstIndex];
}
//-----------------------------------------------------------------------------
// Creates a new flexed vertex to be associated with a vertex
//-----------------------------------------------------------------------------
CachedPosNormTan_t* CCachedRenderData::CreateFlexVertex( int vertex )
{
Assert( m_pFirstFlexIndex );
Assert( m_pFirstFlexIndex[vertex].m_Tag != m_CurrentTag );
Assert ( m_FlexVertexCount < MAXSTUDIOFLEXVERTS );
if ( m_FlexVertexCount >= MAXSTUDIOFLEXVERTS )
return NULL;
// Point the flex list to the new flexed vertex
m_pFirstFlexIndex[vertex].m_Tag = m_CurrentTag;
m_pFirstFlexIndex[vertex].m_VertexIndex = m_FlexVertexCount;
// Add a new flexed vert to the flexed vertex list
++m_FlexVertexCount;
return GetFlexVertex( vertex );
}
//-----------------------------------------------------------------------------
// Creates a new flexed vertex to be associated with a vertex
//-----------------------------------------------------------------------------
CachedPosNorm_t* CCachedRenderData::CreateThinFlexVertex( int vertex )
{
Assert( m_pFirstThinFlexIndex );
Assert( m_pFirstThinFlexIndex[vertex].m_Tag != m_CurrentTag );
Assert ( m_ThinFlexVertexCount < MAXSTUDIOFLEXVERTS );
if ( m_ThinFlexVertexCount >= MAXSTUDIOFLEXVERTS )
return NULL;
// Point the flex list to the new flexed vertex
m_pFirstThinFlexIndex[vertex].m_Tag = m_CurrentTag;
m_pFirstThinFlexIndex[vertex].m_VertexIndex = m_ThinFlexVertexCount;
// Add a new flexed vert to the thin flexed vertex list
++m_ThinFlexVertexCount;
return GetThinFlexVertex( vertex );
}
//-----------------------------------------------------------------------------
// Re-normalize the surface normals and tangents of the flexed vertices
// No thin ones since they're intended to be deltas, not unit vectors
//-----------------------------------------------------------------------------
void CCachedRenderData::RenormalizeFlexVertices( bool bHasTangentData )
{
int i;
for (i = 0; i < m_FlexVertexCount; i++)
{
m_pFlexVerts[ i ].m_Normal.NormalizeInPlace();
if (bHasTangentData)
{
m_pFlexVerts[ i ].m_TangentS.AsVector3D().NormalizeInPlace();
}
}
}
//-----------------------------------------------------------------------------
// Creates a new flexed vertex to be associated with a vertex
//-----------------------------------------------------------------------------
CachedPosNorm_t* CCachedRenderData::CreateWorldVertex( int vertex )
{
Assert( m_pFirstWorldIndex );
if ( m_pFirstWorldIndex[vertex].m_Tag != m_CurrentTag )
{
// Point the world list to the new world vertex
Assert( m_WorldVertexCount < MAXSTUDIOVERTS );
m_pFirstWorldIndex[vertex].m_Tag = m_CurrentTag;
m_pFirstWorldIndex[vertex].m_VertexIndex = m_WorldVertexCount;
// Add a new world vert to the world vertex list
++m_WorldVertexCount;
}
return GetWorldVertex( vertex );
}
+335
View File
@@ -0,0 +1,335 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef FLEXRENDERDATA_H
#define FLEXRENDERDATA_H
#ifdef _WIN32
#pragma once
#endif
#include "mathlib/vector.h"
#include "utlvector.h"
#include "studio.h"
//-----------------------------------------------------------------------------
// forward declarations
//-----------------------------------------------------------------------------
struct mstudiomesh_t;
//-----------------------------------------------------------------------------
// Used by flex vertex data cache
//-----------------------------------------------------------------------------
struct CachedPosNormTan_t
{
Vector m_Position;
Vector m_Normal;
Vector4D m_TangentS;
CachedPosNormTan_t() {}
CachedPosNormTan_t( CachedPosNormTan_t const& src )
{
VectorCopy( src.m_Position, m_Position );
VectorCopy( src.m_Normal, m_Normal );
Vector4DCopy( src.m_TangentS, m_TangentS );
Assert( m_TangentS.w == 1.0f || m_TangentS.w == -1.0f );
}
};
//-----------------------------------------------------------------------------
// Used by world (decal) vertex data cache
//-----------------------------------------------------------------------------
struct CachedPosNorm_t
{
Vector4DAligned m_Position;
Vector4DAligned m_Normal;
CachedPosNorm_t() {}
CachedPosNorm_t( CachedPosNorm_t const& src )
{
Vector4DCopy( src.m_Position, m_Position );
Vector4DCopy( src.m_Normal, m_Normal );
}
};
//-----------------------------------------------------------------------------
// Stores flex vertex data and world (decal) vertex data for the lifetime of the model rendering
//-----------------------------------------------------------------------------
class CCachedRenderData
{
public:
// Constructor
CCachedRenderData();
// Call this when we start to render a new model
void StartModel();
// Used to hook ourselves into a particular body part, model, and mesh
void SetBodyPart( int bodypart );
void SetModel( int model );
void SetMesh( int mesh );
// For faster setup in the decal code
void SetBodyModelMesh( int body, int model, int mesh );
// Used to set up a flex computation
bool IsFlexComputationDone( ) const;
// Used to set up a computation (for world or flex data)
void SetupComputation( mstudiomesh_t *pMesh, bool flexComputation = false );
// Is a particular vertex flexed?
bool IsVertexFlexed( int vertex ) const;
bool IsThinVertexFlexed( int vertex ) const;
// Checks to see if the vertex is defined
bool IsVertexPositionCached( int vertex ) const;
// Gets a flexed vertex
CachedPosNormTan_t* GetFlexVertex( int vertex );
// Gets a flexed vertex
CachedPosNorm_t* GetThinFlexVertex( int vertex );
// Creates a new flexed vertex to be associated with a vertex
CachedPosNormTan_t* CreateFlexVertex( int vertex );
// Creates a new flexed vertex to be associated with a vertex
CachedPosNorm_t* CreateThinFlexVertex( int vertex );
// Renormalizes the normals and tangents of the flex verts
void RenormalizeFlexVertices( bool bHasTangentData );
// Gets a decal vertex
CachedPosNorm_t* GetWorldVertex( int vertex );
// Creates a new decal vertex to be associated with a vertex
CachedPosNorm_t* CreateWorldVertex( int vertex );
template< class T >
void ComputeFlexedVertex_StreamOffset( studiohdr_t *pStudioHdr, mstudioflex_t *pflex, T *pvanim, int vertCount, float w1, float w2, float w3, float w4 );
#ifdef PLATFORM_WINDOWS
void ComputeFlexedVertex_StreamOffset_Optimized( studiohdr_t *pStudioHdr, mstudioflex_t *pflex, mstudiovertanim_t *pvanim, int vertCount, float w1, float w2, float w3, float w4);
void ComputeFlexedVertexWrinkle_StreamOffset_Optimized( studiohdr_t *pStudioHdr, mstudioflex_t *pflex, mstudiovertanim_wrinkle_t *pvanim, int vertCount, float w1, float w2, float w3, float w4);
#endif // PLATFORM_WINDOWS
private:
// Used to create the flex render data. maps
struct CacheIndex_t
{
unsigned short m_Tag;
unsigned short m_VertexIndex;
};
// A dictionary for the cached data
struct CacheDict_t
{
unsigned short m_FirstIndex;
unsigned short m_IndexCount;
unsigned short m_Tag;
unsigned short m_FlexTag;
CacheDict_t() : m_Tag(0), m_FlexTag(0) {}
};
typedef CUtlVector< CacheDict_t > CacheMeshDict_t;
typedef CUtlVector< CacheMeshDict_t > CacheModelDict_t;
typedef CUtlVector< CacheModelDict_t > CacheBodyPartDict_t;
// Flex data, allocated for the lifespan of rendering
// Can't use UtlVector due to alignment issues
int m_FlexVertexCount;
CachedPosNormTan_t m_pFlexVerts[MAXSTUDIOFLEXVERTS+1];
// Flex data, allocated for the lifespan of rendering
// Can't use UtlVector due to alignment issues
int m_ThinFlexVertexCount;
CachedPosNorm_t m_pThinFlexVerts[MAXSTUDIOFLEXVERTS+1];
// World data, allocated for the lifespan of rendering
// Can't use UtlVector due to alignment issues
int m_WorldVertexCount;
CachedPosNorm_t m_pWorldVerts[MAXSTUDIOVERTS+1];
// Maps actual mesh vertices into flex cache + world cache indices
int m_IndexCount;
CacheIndex_t m_pFlexIndex[MAXSTUDIOVERTS+1];
CacheIndex_t m_pThinFlexIndex[MAXSTUDIOVERTS+1];
CacheIndex_t m_pWorldIndex[MAXSTUDIOVERTS+1];
CacheBodyPartDict_t m_CacheDict;
// The flex tag
unsigned short m_CurrentTag;
// the current body, model, and mesh
int m_Body;
int m_Model;
int m_Mesh;
// mapping for the current mesh to flex data
CacheIndex_t* m_pFirstFlexIndex;
CacheIndex_t* m_pFirstThinFlexIndex;
CacheIndex_t* m_pFirstWorldIndex;
friend class CStudioRender;
};
//-----------------------------------------------------------------------------
// Checks to see if the vertex is defined
//-----------------------------------------------------------------------------
inline bool CCachedRenderData::IsVertexFlexed( int vertex ) const
{
return (m_pFirstFlexIndex && (m_pFirstFlexIndex[vertex].m_Tag == m_CurrentTag));
}
inline bool CCachedRenderData::IsThinVertexFlexed( int vertex ) const
{
return (m_pFirstThinFlexIndex && (m_pFirstThinFlexIndex[vertex].m_Tag == m_CurrentTag));
}
//-----------------------------------------------------------------------------
// Gets an existing flexed vertex associated with a vertex
//-----------------------------------------------------------------------------
inline CachedPosNormTan_t* CCachedRenderData::GetFlexVertex( int vertex )
{
Assert( m_pFirstFlexIndex );
Assert( m_pFirstFlexIndex[vertex].m_Tag == m_CurrentTag );
return &m_pFlexVerts[ m_pFirstFlexIndex[vertex].m_VertexIndex ];
}
inline CachedPosNorm_t* CCachedRenderData::GetThinFlexVertex( int vertex )
{
Assert( m_pFirstThinFlexIndex );
Assert( m_pFirstThinFlexIndex[vertex].m_Tag == m_CurrentTag );
return &m_pThinFlexVerts[ m_pFirstThinFlexIndex[vertex].m_VertexIndex ];
}
//-----------------------------------------------------------------------------
// Checks to see if the vertex is defined
//-----------------------------------------------------------------------------
inline bool CCachedRenderData::IsVertexPositionCached( int vertex ) const
{
return (m_pFirstWorldIndex && (m_pFirstWorldIndex[vertex].m_Tag == m_CurrentTag));
}
//-----------------------------------------------------------------------------
// Gets an existing world vertex associated with a vertex
//-----------------------------------------------------------------------------
inline CachedPosNorm_t* CCachedRenderData::GetWorldVertex( int vertex )
{
Assert( m_pFirstWorldIndex );
Assert( m_pFirstWorldIndex[vertex].m_Tag == m_CurrentTag );
return &m_pWorldVerts[ m_pFirstWorldIndex[vertex].m_VertexIndex ];
}
//-----------------------------------------------------------------------------
// For faster setup in the decal code
//-----------------------------------------------------------------------------
inline void CCachedRenderData::SetBodyModelMesh( int body, int model, int mesh)
{
m_Body = body;
m_Model = model;
m_Mesh = mesh;
Assert((m_Model >= 0) && (m_Body >= 0));
m_CacheDict[m_Body][m_Model].EnsureCount(m_Mesh+1);
// At this point, we should have all 3 defined.
CacheDict_t& dict = m_CacheDict[m_Body][m_Model][m_Mesh];
if (dict.m_Tag == m_CurrentTag)
{
m_pFirstFlexIndex = &m_pFlexIndex[dict.m_FirstIndex];
m_pFirstThinFlexIndex = &m_pThinFlexIndex[dict.m_FirstIndex];
m_pFirstWorldIndex = &m_pWorldIndex[dict.m_FirstIndex];
}
else
{
m_pFirstFlexIndex = 0;
m_pFirstThinFlexIndex = 0;
m_pFirstWorldIndex = 0;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//
// ** Only execute this function if device supports stream offset **
//
// Input : pmesh - pointer to a studio mesh
// lod - integer lod (0 is most detailed)
// Output : none
//-----------------------------------------------------------------------------
template< class T >
void CCachedRenderData::ComputeFlexedVertex_StreamOffset( studiohdr_t *pStudioHdr, mstudioflex_t *pflex,
T *pvanim, int vertCount, float w1, float w2, float w3, float w4 )
{
float w12 = w1 - w2;
float w34 = w3 - w4;
float flVertAnimFixedPointScale = pStudioHdr->VertAnimFixedPointScale();
CachedPosNorm_t *pFlexedVertex = NULL;
for (int j = 0; j < pflex->numverts; j++)
{
int n = pvanim[j].index;
// only flex the indices that are (still) part of this mesh at this lod
if ( n >= vertCount )
continue;
float s = pvanim[j].speed;
float b = pvanim[j].side;
Vector4DAligned vPosition, vNormal;
pvanim[j].GetDeltaFixed4DAligned( &vPosition, flVertAnimFixedPointScale );
pvanim[j].GetNDeltaFixed4DAligned( &vNormal, flVertAnimFixedPointScale );
if ( !IsThinVertexFlexed(n) )
{
// Add a new flexed vert to the flexed vertex list
pFlexedVertex = CreateThinFlexVertex(n);
Assert( pFlexedVertex != NULL);
pFlexedVertex->m_Position.InitZero();
pFlexedVertex->m_Normal.InitZero();
}
else
{
pFlexedVertex = GetThinFlexVertex(n);
}
s *= 1.0f / 255.0f;
b *= 1.0f / 255.0f;
float wa = w2 + w12 * s;
float wb = w4 + w34 * s;
float w = wa + ( wb - wa ) * b;
Vector4DWeightMAD( w, vPosition, pFlexedVertex->m_Position, vNormal, pFlexedVertex->m_Normal );
}
}
#endif // FLEXRENDERDATA_H
+13
View File
@@ -0,0 +1,13 @@
$infile = shift;
$outfile = shift;
open INFILE, "<$infile";
@infile = <INFILE>;
close INFILE;
open OUTFILE, ">$outfile";
while( shift @infile )
{
print OUTFILE $_;
}
close OUTFILE;
+392
View File
@@ -0,0 +1,392 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// r_studio.cpp: routines for setting up to draw 3DStudio models
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#include "studio.h"
#include "studiorender.h"
#include "studiorendercontext.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "tier0/vprof.h"
#include "tier3/tier3.h"
#include "datacache/imdlcache.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Figures out what kind of lighting we're gonna want
//-----------------------------------------------------------------------------
FORCEINLINE StudioModelLighting_t CStudioRender::R_StudioComputeLighting( IMaterial *pMaterial, int materialFlags, ColorMeshInfo_t *pColorMeshes )
{
// Here, we only do software lighting when the following conditions are met.
// 1) The material is vertex lit and we don't have hardware lighting
// 2) We're drawing an eyeball
// 3) We're drawing mouth-lit stuff
// FIXME: When we move software lighting into the material system, only need to
// test if it's vertex lit
Assert( pMaterial );
bool doMouthLighting = materialFlags && (m_pStudioHdr->nummouths >= 1);
if ( IsX360() )
{
// 360 does not do software lighting
return doMouthLighting ? LIGHTING_MOUTH : LIGHTING_HARDWARE;
}
bool doSoftwareLighting = doMouthLighting ||
(pMaterial->IsVertexLit() && pMaterial->NeedsSoftwareLighting() );
if ( !m_pRC->m_Config.m_bSupportsVertexAndPixelShaders )
{
if ( !doSoftwareLighting && pColorMeshes )
{
pMaterial->SetUseFixedFunctionBakedLighting( true );
}
else
{
doSoftwareLighting = true;
pMaterial->SetUseFixedFunctionBakedLighting( false );
}
}
StudioModelLighting_t lighting = LIGHTING_HARDWARE;
if ( doMouthLighting )
lighting = LIGHTING_MOUTH;
else if ( doSoftwareLighting )
lighting = LIGHTING_SOFTWARE;
return lighting;
}
IMaterial* CStudioRender::R_StudioSetupSkinAndLighting( IMatRenderContext *pRenderContext, int index, IMaterial **ppMaterials, int materialFlags,
void /*IClientRenderable*/ *pClientRenderable, ColorMeshInfo_t *pColorMeshes, StudioModelLighting_t &lighting )
{
VPROF( "R_StudioSetupSkin" );
IMaterial *pMaterial = NULL;
bool bCheckForConVarDrawTranslucentSubModels = false;
if( m_pRC->m_Config.bWireframe && !m_pRC->m_pForcedMaterial )
{
if ( m_pRC->m_Config.bDrawZBufferedWireframe )
pMaterial = m_pMaterialMRMWireframeZBuffer;
else
pMaterial = m_pMaterialMRMWireframe;
}
else if( m_pRC->m_Config.bShowEnvCubemapOnly )
{
pMaterial = m_pMaterialModelEnvCubemap;
}
else
{
if ( !m_pRC->m_pForcedMaterial && ( m_pRC->m_nForcedMaterialType != OVERRIDE_DEPTH_WRITE && m_pRC->m_nForcedMaterialType != OVERRIDE_SSAO_DEPTH_WRITE ) )
{
pMaterial = ppMaterials[index];
if ( !pMaterial )
{
Assert( 0 );
return 0;
}
}
else
{
materialFlags = 0;
pMaterial = m_pRC->m_pForcedMaterial;
if (m_pRC->m_nForcedMaterialType == OVERRIDE_BUILD_SHADOWS)
{
// Connect the original material up to the shadow building material
// Also bind the original material so its proxies are in the correct state
static unsigned int translucentCache = 0;
IMaterialVar* pOriginalMaterialVar = pMaterial->FindVarFast( "$translucent_material", &translucentCache );
Assert( pOriginalMaterialVar );
IMaterial *pOriginalMaterial = ppMaterials[index];
if ( pOriginalMaterial )
{
// Disable any alpha modulation on the original material that was left over from when it was last rendered
pOriginalMaterial->AlphaModulate( 1.0f );
pRenderContext->Bind( pOriginalMaterial, pClientRenderable );
if ( pOriginalMaterial->IsTranslucent() || pOriginalMaterial->IsAlphaTested() )
{
if ( pOriginalMaterialVar )
pOriginalMaterialVar->SetMaterialValue( pOriginalMaterial );
}
else
{
if ( pOriginalMaterialVar )
pOriginalMaterialVar->SetMaterialValue( NULL );
}
}
else
{
if ( pOriginalMaterialVar )
pOriginalMaterialVar->SetMaterialValue( NULL );
}
}
else if ( m_pRC->m_nForcedMaterialType == OVERRIDE_DEPTH_WRITE || m_pRC->m_nForcedMaterialType == OVERRIDE_SSAO_DEPTH_WRITE )
{
// Disable any alpha modulation on the original material that was left over from when it was last rendered
ppMaterials[index]->AlphaModulate( 1.0f );
// Bail if the material is still considered translucent after setting the AlphaModulate to 1.0
if ( ppMaterials[index]->IsTranslucent() )
{
return NULL;
}
static unsigned int originalTextureVarCache = 0;
IMaterialVar *pOriginalTextureVar = ppMaterials[index]->FindVarFast( "$basetexture", &originalTextureVarCache );
// Select proper override material
int nAlphaTest = (int) ( ppMaterials[index]->IsAlphaTested() && pOriginalTextureVar->IsTexture() ); // alpha tested base texture
int nNoCull = (int) ppMaterials[index]->IsTwoSided();
if ( m_pRC->m_nForcedMaterialType == OVERRIDE_SSAO_DEPTH_WRITE )
{
pMaterial = m_pSSAODepthWrite[nAlphaTest][nNoCull];
}
else
{
pMaterial = m_pDepthWrite[nAlphaTest][nNoCull];
}
// If we're alpha tested, we should set up the texture variables from the original material
if ( nAlphaTest != 0 )
{
static unsigned int originalTextureFrameVarCache = 0;
IMaterialVar *pOriginalTextureFrameVar = ppMaterials[index]->FindVarFast( "$frame", &originalTextureFrameVarCache );
static unsigned int originalAlphaRefCache = 0;
IMaterialVar *pOriginalAlphaRefVar = ppMaterials[index]->FindVarFast( "$AlphaTestReference", &originalAlphaRefCache );
static unsigned int textureVarCache = 0;
IMaterialVar *pTextureVar = pMaterial->FindVarFast( "$basetexture", &textureVarCache );
static unsigned int textureFrameVarCache = 0;
IMaterialVar *pTextureFrameVar = pMaterial->FindVarFast( "$frame", &textureFrameVarCache );
static unsigned int alphaRefCache = 0;
IMaterialVar *pAlphaRefVar = pMaterial->FindVarFast( "$AlphaTestReference", &alphaRefCache );
if ( pOriginalTextureVar->IsTexture() ) // If $basetexture is defined
{
if( pTextureVar && pOriginalTextureVar )
{
pTextureVar->SetTextureValue( pOriginalTextureVar->GetTextureValue() );
}
if( pTextureFrameVar && pOriginalTextureFrameVar )
{
pTextureFrameVar->SetIntValue( pOriginalTextureFrameVar->GetIntValue() );
}
if( pAlphaRefVar && pOriginalAlphaRefVar )
{
pAlphaRefVar->SetFloatValue( pOriginalAlphaRefVar->GetFloatValue() );
}
}
}
}
}
// Set this bool to check after the bind below
bCheckForConVarDrawTranslucentSubModels = true;
if ( m_pRC->m_nForcedMaterialType != OVERRIDE_DEPTH_WRITE && m_pRC->m_nForcedMaterialType != OVERRIDE_SSAO_DEPTH_WRITE)
{
// Try to set the alpha based on the blend
pMaterial->AlphaModulate( m_pRC->m_AlphaMod );
// Try to set the color based on the colormod
pMaterial->ColorModulate( m_pRC->m_ColorMod[0], m_pRC->m_ColorMod[1], m_pRC->m_ColorMod[2] );
}
}
lighting = R_StudioComputeLighting( pMaterial, materialFlags, pColorMeshes );
if ( lighting == LIGHTING_MOUTH )
{
if ( !m_pRC->m_Config.bTeeth || !R_TeethAreVisible() )
return NULL;
// skin it and light it, but only if we need to.
if ( m_pRC->m_Config.m_bSupportsVertexAndPixelShaders )
{
R_MouthSetupVertexShader( pMaterial );
}
}
// TODO: It's possible we don't want to use the color texels--for example because of a convar.
// We should check that here in addition to whether or not we have the data available.
static unsigned int lightmapVarCache = 0;
IMaterialVar *pLightmapVar = pMaterial->FindVarFast( "$lightmap", &lightmapVarCache );
if ( pLightmapVar )
{
ITexture* newTex = pColorMeshes ? pColorMeshes->m_pLightmap : NULL;
if (newTex)
pLightmapVar->SetTextureValue(newTex);
else
pLightmapVar->SetUndefined();
}
pRenderContext->Bind( pMaterial, pClientRenderable );
if ( bCheckForConVarDrawTranslucentSubModels )
{
bool translucent = pMaterial->IsTranslucent();
if (( m_bDrawTranslucentSubModels && !translucent ) ||
( !m_bDrawTranslucentSubModels && translucent ))
{
m_bSkippedMeshes = true;
return NULL;
}
}
return pMaterial;
}
//=============================================================================
/*
=================
R_StudioSetupModel
based on the body part, figure out which mesh it should be using.
inputs:
outputs:
pstudiomesh
pmdl
=================
*/
int R_StudioSetupModel( int bodypart, int entity_body, mstudiomodel_t **ppSubModel,
const studiohdr_t *pStudioHdr )
{
int index;
mstudiobodyparts_t *pbodypart;
if (bodypart > pStudioHdr->numbodyparts)
{
ConDMsg ("R_StudioSetupModel: no such bodypart %d\n", bodypart);
bodypart = 0;
}
pbodypart = pStudioHdr->pBodypart( bodypart );
if ( pbodypart->base == 0 )
{
Warning( "Model has missing body part: %s\n", pStudioHdr->pszName() );
Assert( 0 );
}
index = entity_body / pbodypart->base;
index = index % pbodypart->nummodels;
Assert( ppSubModel );
*ppSubModel = pbodypart->pModel( index );
return index;
}
//-----------------------------------------------------------------------------
// Generates the PoseToBone Matrix nessecary to align the given bone with the
// world.
//-----------------------------------------------------------------------------
static void ScreenAlignBone( matrix3x4_t *pPoseToWorld, mstudiobone_t *pCurBone,
const Vector& vecViewOrigin, const matrix3x4_t &boneToWorld )
{
// Grab the world translation:
Vector vT( boneToWorld[0][3], boneToWorld[1][3], boneToWorld[2][3] );
// Construct the coordinate frame:
// Initialized to get rid of compiler
Vector vX, vY, vZ;
if( pCurBone->flags & BONE_SCREEN_ALIGN_SPHERE )
{
vX = vecViewOrigin - vT;
VectorNormalize(vX);
vZ = Vector(0,0,1);
vY = vZ.Cross(vX);
VectorNormalize(vY);
vZ = vX.Cross(vY);
VectorNormalize(vZ);
}
else
{
Assert( pCurBone->flags & BONE_SCREEN_ALIGN_CYLINDER );
vX.Init( boneToWorld[0][0], boneToWorld[1][0], boneToWorld[2][0] );
vZ = vecViewOrigin - vT;
VectorNormalize(vZ);
vY = vZ.Cross(vX);
VectorNormalize(vY);
vZ = vX.Cross(vY);
VectorNormalize(vZ);
}
matrix3x4_t matBoneBillboard(
vX.x, vY.x, vZ.x, vT.x,
vX.y, vY.y, vZ.y, vT.y,
vX.z, vY.z, vZ.z, vT.z );
ConcatTransforms( matBoneBillboard, pCurBone->poseToBone, *pPoseToWorld );
}
//-----------------------------------------------------------------------------
// Computes PoseToWorld from BoneToWorld
//-----------------------------------------------------------------------------
void ComputePoseToWorld( matrix3x4_t *pPoseToWorld, studiohdr_t *pStudioHdr, int boneMask, const Vector& vecViewOrigin, const matrix3x4_t *pBoneToWorld )
{
if ( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP )
{
// by definition, these always have an identity poseToBone transform
MatrixCopy( pBoneToWorld[ 0 ], pPoseToWorld[ 0 ] );
return;
}
if ( !pStudioHdr->pLinearBones() )
{
// convert bone to world transformations into pose to world transformations
for (int i = 0; i < pStudioHdr->numbones; i++)
{
mstudiobone_t *pCurBone = pStudioHdr->pBone( i );
if ( !(pCurBone->flags & boneMask) )
continue;
ConcatTransforms( pBoneToWorld[ i ], pCurBone->poseToBone, pPoseToWorld[ i ] );
}
}
else
{
mstudiolinearbone_t *pLinearBones = pStudioHdr->pLinearBones();
// convert bone to world transformations into pose to world transformations
for (int i = 0; i < pStudioHdr->numbones; i++)
{
if ( !(pLinearBones->flags(i) & boneMask) )
continue;
ConcatTransforms( pBoneToWorld[ i ], pLinearBones->poseToBone(i), pPoseToWorld[ i ] );
}
}
#if 0
// These don't seem to be used in any existing QC file, re-enable in a future project?
// Pretransform
if( !( pCurBone->flags & ( BONE_SCREEN_ALIGN_SPHERE | BONE_SCREEN_ALIGN_CYLINDER )))
{
ConcatTransforms( pBoneToWorld[ i ], pCurBone->poseToBone, pPoseToWorld[ i ] );
}
else
{
// If this bone is screen aligned, then generate a PoseToWorld matrix that billboards the bone
ScreenAlignBone( &pPoseToWorld[i], pCurBone, vecViewOrigin, pBoneToWorld[i] );
}
#endif
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+928
View File
@@ -0,0 +1,928 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#include "studio.h"
#include "studiorendercontext.h"
#include "bitmap/imageformat.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/itexture.h"
#include "materialsystem/imesh.h"
#include "mathlib/mathlib.h"
#include "studiorender.h"
#include "pixelwriter.h"
#include "vtf/vtf.h"
#include "tier1/convar.h"
#include "tier1/KeyValues.h"
#include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define sign( a ) (((a) < 0) ? -1 : (((a) > 0) ? 1 : 0 ))
void CStudioRender::R_StudioEyeballPosition( const mstudioeyeball_t *peyeball, eyeballstate_t *pstate )
{
// Vector forward;
// Vector org, right, up;
pstate->peyeball = peyeball;
Vector tmp;
// move eyeball into worldspace
{
// ConDMsg("%.2f %.2f %.2f\n", peyeball->org[0], peyeball->org[1], peyeball->org[2] );
VectorCopy( peyeball->org, tmp );
tmp[0] += m_pRC->m_Config.fEyeShiftX * sign( tmp[0] );
tmp[1] += m_pRC->m_Config.fEyeShiftY * sign( tmp[1] );
tmp[2] += m_pRC->m_Config.fEyeShiftZ * sign( tmp[2] );
}
VectorTransform( tmp, m_pBoneToWorld[peyeball->bone], pstate->org );
VectorRotate( peyeball->up, m_pBoneToWorld[peyeball->bone], pstate->up );
// look directly at target
VectorSubtract( m_pRC->m_ViewTarget, pstate->org, pstate->forward );
VectorNormalize( pstate->forward );
if ( !m_pRC->m_Config.bEyeMove )
{
VectorRotate( peyeball->forward, m_pBoneToWorld[peyeball->bone], pstate->forward );
VectorScale( pstate->forward, -1 ,pstate->forward ); // ???
}
CrossProduct( pstate->forward, pstate->up, pstate->right );
VectorNormalize( pstate->right );
// shift N degrees off of the target
float dz;
dz = peyeball->zoffset;
VectorMA( pstate->forward, peyeball->zoffset + dz, pstate->right, pstate->forward );
#if 0
// add random jitter
VectorMA( forward, RandomFloat( -0.02, 0.02 ), right, forward );
VectorMA( forward, RandomFloat( -0.02, 0.02 ), up, forward );
#endif
VectorNormalize( pstate->forward );
// re-aim eyes
CrossProduct( pstate->forward, pstate->up, pstate->right );
VectorNormalize( pstate->right );
CrossProduct( pstate->right, pstate->forward, pstate->up );
VectorNormalize( pstate->up );
float scale = (1.0 / peyeball->iris_scale) + m_pRC->m_Config.fEyeSize;
if (scale > 0)
scale = 1.0 / scale;
VectorScale( &pstate->right[0], -scale, pstate->mat[0] );
VectorScale( &pstate->up[0], -scale, pstate->mat[1] );
pstate->mat[0][3] = -DotProduct( &pstate->org[0], pstate->mat[0] ) + 0.5f;
pstate->mat[1][3] = -DotProduct( &pstate->org[0], pstate->mat[1] ) + 0.5f;
// FIXME: push out vertices for cornea
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CStudioRender::R_StudioEyelidFACS( const mstudioeyeball_t *peyeball, const eyeballstate_t *pstate )
{
if ( peyeball->m_bNonFACS )
return;
Vector headup;
Vector headforward;
Vector pos;
float upperlid = DEG2RAD( 9.5 );
float lowerlid = DEG2RAD( -26.4 );
// FIXME: Crash workaround
Vector vecNormTarget;
vecNormTarget.Init( peyeball->uppertarget[0], peyeball->uppertarget[1], peyeball->uppertarget[2] );
vecNormTarget /= peyeball->radius;
vecNormTarget.x = clamp( vecNormTarget.x, -1.0f, 1.0f );
vecNormTarget.y = clamp( vecNormTarget.y, -1.0f, 1.0f );
vecNormTarget.z = clamp( vecNormTarget.z, -1.0f, 1.0f );
// get weighted position of eyeball angles based on the "raiser", "neutral", and "lowerer" controls
upperlid = m_pFlexWeights[peyeball->upperflexdesc[0]] * asin( vecNormTarget.x );
upperlid += m_pFlexWeights[peyeball->upperflexdesc[1]] * asin( vecNormTarget.y );
upperlid += m_pFlexWeights[peyeball->upperflexdesc[2]] * asin( vecNormTarget.z );
vecNormTarget.Init( peyeball->lowertarget[0], peyeball->lowertarget[1], peyeball->lowertarget[2] );
vecNormTarget /= peyeball->radius;
vecNormTarget.x = clamp( vecNormTarget.x, -1.0f, 1.0f );
vecNormTarget.y = clamp( vecNormTarget.y, -1.0f, 1.0f );
vecNormTarget.z = clamp( vecNormTarget.z, -1.0f, 1.0f );
lowerlid = m_pFlexWeights[peyeball->lowerflexdesc[0]] * asin( vecNormTarget.x );
lowerlid += m_pFlexWeights[peyeball->lowerflexdesc[1]] * asin( vecNormTarget.y );
lowerlid += m_pFlexWeights[peyeball->lowerflexdesc[2]] * asin( vecNormTarget.z );
// ConDMsg("%.1f %.1f\n", RAD2DEG( upperlid ), RAD2DEG( lowerlid ) );
float sinupper, cosupper, sinlower, coslower;
SinCos( upperlid, &sinupper, &cosupper );
SinCos( lowerlid, &sinlower, &coslower );
// convert to head relative space
VectorIRotate( pstate->up, m_pBoneToWorld[peyeball->bone], headup );
VectorIRotate( pstate->forward, m_pBoneToWorld[peyeball->bone], headforward );
// upper lid
VectorScale( headup, sinupper * peyeball->radius, pos );
VectorMA( pos, cosupper * peyeball->radius, headforward, pos );
m_pFlexWeights[peyeball->upperlidflexdesc] = DotProduct( pos, peyeball->up );
// lower lid
VectorScale( headup, sinlower * peyeball->radius, pos );
VectorMA( pos, coslower * peyeball->radius, headforward, pos );
m_pFlexWeights[peyeball->lowerlidflexdesc] = DotProduct( pos, peyeball->up );
// ConDMsg("%.4f %.4f\n", m_pRC->m_FlexWeights[peyeball->upperlidflex], m_pRC->m_FlexWeights[peyeball->lowerlidflex] );
}
void CStudioRender::MaterialPlanerProjection( const matrix3x4_t& mat, int count, const Vector *psrcverts, Vector2D *pdesttexcoords )
{
for (int i = 0; i < count; i++)
{
pdesttexcoords[i][0] = DotProduct( &psrcverts[i].x, mat[0] ) + mat[0][3];
pdesttexcoords[i][1] = DotProduct( &psrcverts[i].x, mat[1] ) + mat[1][3];
}
}
//-----------------------------------------------------------------------------
// Ramp and clamp the flex weight
//-----------------------------------------------------------------------------
float CStudioRender::RampFlexWeight( mstudioflex_t &flex, float w )
{
if (w <= flex.target0 || w >= flex.target3)
{
// value outside of range
w = 0.0;
}
else if (w < flex.target1)
{
// 0 to 1 ramp
w = (w - flex.target0) / (flex.target1 - flex.target0);
}
else if (w > flex.target2)
{
// 1 to 0 ramp
w = (flex.target3 - w) / (flex.target3 - flex.target2);
}
else
{
// plat
w = 1.0;
}
return w;
}
//-----------------------------------------------------------------------------
// Setup the flex verts for this rendering
//-----------------------------------------------------------------------------
void CStudioRender::R_StudioFlexVerts( mstudiomesh_t *pmesh, int lod )
{
VPROF_BUDGET( "CStudioRender::R_StudioFlexVerts", VPROF_BUDGETGROUP_MODEL_RENDERING );
Assert( pmesh );
const float flVertAnimFixedPointScale = m_pStudioHdr->VertAnimFixedPointScale();
// There's a chance we can actually do the flex twice on a single mesh
// since there's flexed HW + SW portions of the mesh.
if (m_VertexCache.IsFlexComputationDone())
return;
// get pointers to geometry
if ( !pmesh->pModel()->CacheVertexData( m_pStudioHdr ) )
{
// not available yet
return;
}
const mstudio_meshvertexdata_t *vertData = pmesh->GetVertexData( m_pStudioHdr );
Assert( vertData );
if ( !vertData )
{
static unsigned int warnCount = 0;
if ( warnCount++ < 20 )
Warning( "ERROR: R_StudioFlexVerts, model verts have been compressed, cannot render! (use \"-no_compressed_vvds\")" );
return;
}
// The flex data should have been converted to the new (fixed-point) format on load:
Assert( m_pStudioHdr->flags & STUDIOHDR_FLAGS_FLEXES_CONVERTED );
if ( ( m_pStudioHdr->flags & STUDIOHDR_FLAGS_FLEXES_CONVERTED ) == 0 )
{
static unsigned int flexConversionTimesWarned = 0;
if ( flexConversionTimesWarned++ < 6 )
Warning( "ERROR: flex verts have not been converted (queued loader refcount bug?) - expect to see 'exploded' faces" );
}
mstudiovertex_t *pVertices = vertData->Vertex( 0 );
Vector4D *pStudioTangentS;
if ( vertData->HasTangentData() )
{
pStudioTangentS = vertData->TangentS( 0 );
}
else
{
pStudioTangentS = NULL;
}
mstudioflex_t *pflex = pmesh->pFlex( 0 );
m_VertexCache.SetupComputation( pmesh, true );
// apply flex weights
int i, j, n;
for (i = 0; i < pmesh->numflexes; i++)
{
float w1 = RampFlexWeight( pflex[i], m_pFlexWeights[ pflex[i].flexdesc ] );
float w2 = RampFlexWeight( pflex[i], m_pFlexDelayedWeights[ pflex[i].flexdesc ] );
float w3, w4;
if ( pflex[i].flexpair != 0)
{
w3 = RampFlexWeight( pflex[i], m_pFlexWeights[ pflex[i].flexpair ] );
w4 = RampFlexWeight( pflex[i], m_pFlexDelayedWeights[ pflex[i].flexpair ] );
}
else
{
w3 = w1;
w4 = w2;
}
if ( w1 > -0.001 && w1 < 0.001 && w2 > -0.001 && w2 < 0.001 )
{
if ( w3 > -0.001 && w3 < 0.001 && w4 > -0.001 && w4 < 0.001 )
{
continue;
}
}
// We may have wrinkle information for this flex, but if we're software skinning
// we're going to ignore it.
byte *pvanim = pflex[i].pBaseVertanim();
int nVAnimSizeBytes = pflex[i].VertAnimSizeBytes();
for (j = 0; j < pflex[i].numverts; j++)
{
mstudiovertanim_t *pAnim = (mstudiovertanim_t*)( pvanim + j * nVAnimSizeBytes );
n = pAnim->index;
// Only flex the indices that are (still) part of this mesh
// need lod restriction here
if (n < pmesh->vertexdata.numLODVertexes[lod])
{
mstudiovertex_t &vert = pVertices[n];
CachedPosNormTan_t* pFlexedVertex;
if (!m_VertexCache.IsVertexFlexed(n))
{
// Add a new flexed vert to the flexed vertex list
pFlexedVertex = m_VertexCache.CreateFlexVertex(n);
// skip processing if no more flexed verts can be allocated
if (pFlexedVertex == NULL)
continue;
VectorCopy( vert.m_vecPosition, pFlexedVertex->m_Position );
VectorCopy( vert.m_vecNormal, pFlexedVertex->m_Normal );
if (pStudioTangentS)
{
Vector4DCopy( pStudioTangentS[n], pFlexedVertex->m_TangentS );
Assert( pFlexedVertex->m_TangentS.w == -1.0f || pFlexedVertex->m_TangentS.w == 1.0f );
}
}
else
{
pFlexedVertex = m_VertexCache.GetFlexVertex(n);
}
float s = pAnim->speed * (1.0F/255.0F);
float b = pAnim->side * (1.0F/255.0F);
float w = (w1 * s + (1.0f - s) * w2) * (1.0f - b) + b * (w3 * s + (1.0f - s) * w4);
// Accumulate weighted deltas
pFlexedVertex->m_Position += pAnim->GetDeltaFixed( flVertAnimFixedPointScale ) * w;
pFlexedVertex->m_Normal += pAnim->GetNDeltaFixed( flVertAnimFixedPointScale ) * w;
if ( pStudioTangentS )
{
pFlexedVertex->m_TangentS.AsVector3D() += pAnim->GetNDeltaFixed( flVertAnimFixedPointScale ) * w;
Assert( pFlexedVertex->m_TangentS.w == -1.0f || pFlexedVertex->m_TangentS.w == 1.0f );
}
}
}
}
m_VertexCache.RenormalizeFlexVertices( vertData->HasTangentData() );
}
// REMOVED!! Look in version 32 if you need it.
//static void R_StudioEyeballNormals( const mstudioeyeball_t *peyeball, int count, const Vector *psrcverts, Vector *pdestnorms )
#define KERNEL_DIAMETER 2
#define KERNEL_TEXELS (KERNEL_DIAMETER)
#define KERNEL_TEXEL_RADIUS (KERNEL_TEXELS / 2)
inline float GlintGaussSpotCoefficient( float dx, float dy /*, float *table */ )
{
const float radius = KERNEL_DIAMETER / 2;
const float rsq = 1.0f / (radius * radius);
float r2 = (dx * dx + dy * dy) * rsq;
if (r2 <= 1.0f)
{
return exp( -25.0 * r2 );
// NOTE: This optimization doesn't make much of a difference
//int index = r2 * (GLINT_TABLE_ENTRIES-1);
//return table[index];
}
return 0;
}
void CStudioRender::AddGlint( CPixelWriter &pixelWriter, float x, float y, const Vector& color )
{
x = (x + 0.5f) * m_GlintWidth;
y = (y + 0.5f) * m_GlintHeight;
const float texelRadius = KERNEL_DIAMETER / 2;
int x0 = (int)x;
int y0 = (int)y;
int x1 = x0 + texelRadius;
int y1 = y0 + texelRadius;
x0 -= texelRadius;
y0 -= texelRadius;
// clip light to texture
if ( (x0 >= m_GlintWidth) || (x1 < 0) || (y0 >= m_GlintHeight) || (y1 < 0) )
return;
// clamp coordinates
if ( x0 < 0 )
{
x0 = 0;
}
if ( y0 < 0 )
{
y0 = 0;
}
if ( x1 >= m_GlintWidth )
{
x1 = m_GlintWidth-1;
}
if ( y1 >= m_GlintHeight )
{
y1 = m_GlintHeight-1;
}
for (int v = y0; v <= y1; ++v )
{
pixelWriter.Seek( x0, v );
for (int u = x0; u <= x1; ++u )
{
float fu = ((float)u) - x;
float fv = ((float)v) - y;
const float offset = 0.25;
float intensity = GlintGaussSpotCoefficient( fu-offset, fv-offset ) +
GlintGaussSpotCoefficient( fu+offset, fv-offset ) +
5 * GlintGaussSpotCoefficient( fu, fv ) +
GlintGaussSpotCoefficient( fu-offset, fv+offset ) +
GlintGaussSpotCoefficient( fu+offset, fv+offset );
// NOTE: Old filter code multiplies the signal by 8X, so we will too
intensity *= (4.0f/9.0f);
// NOTE: It's much faster to do the work in the dest texture than to touch the memory more
// or make more buffers
Vector outColor = intensity * color;
int r, g, b, a;
pixelWriter.ReadPixelNoAdvance( r, g, b, a );
outColor.x += TextureToLinear(r);
outColor.y += TextureToLinear(g);
outColor.z += TextureToLinear(b);
pixelWriter.WritePixel( LinearToTexture(outColor.x), LinearToTexture(outColor.y), LinearToTexture(outColor.z) );
}
}
}
//-----------------------------------------------------------------------------
// glint
//-----------------------------------------------------------------------------
// test/stub code
#if 0
class CEmptyTextureRegen : public ITextureRegenerator
{
public:
virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
{
// get the texture
unsigned char *pTextureData = pVTFTexture->ImageData( 0, 0, 0 );
int nImageSize = pVTFTexture->ComputeMipSize( 0 );
memset( pTextureData, 0, nImageSize );
}
// We've got a global instance, no need to delete it
virtual void Release() {}
};
static CEmptyTextureRegen s_GlintTextureRegen;
#endif
class CGlintTextureRegenerator : public ITextureRegenerator
{
public:
virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
{
// We don't need to reconstitute the bits after a task switch
// since we reconstitute them every frame they are used anyways
if ( !m_pStudioRender )
return;
if ( ( m_pStudioRender->m_GlintWidth != pVTFTexture->Width() ) ||
( m_pStudioRender->m_GlintHeight != pVTFTexture->Height() ) )
{
m_pStudioRender->m_GlintWidth = pVTFTexture->Width();
m_pStudioRender->m_GlintHeight = pVTFTexture->Height();
}
CStudioRender::GlintRenderData_t pRenderData[16];
int nGlintCount = m_pStudioRender->BuildGlintRenderData( pRenderData,
ARRAYSIZE(pRenderData), m_pState, *m_pVRight, *m_pVUp, *m_pROrigin );
// setup glint texture
unsigned char *pTextureData = pVTFTexture->ImageData( 0, 0, 0 );
CPixelWriter pixelWriter;
pixelWriter.SetPixelMemory( pVTFTexture->Format(), pTextureData, pVTFTexture->RowSizeInBytes( 0 ) );
int nImageSize = pVTFTexture->ComputeMipSize( 0 );
memset( pTextureData, 0, nImageSize );
// Put in glints due to the lights in the scene
for ( int i = 0; i < nGlintCount; ++i )
{
// NOTE: AddGlint is a more expensive solution but it looks better close-up
m_pStudioRender->AddGlint( pixelWriter, pRenderData[i].m_vecPosition[0],
pRenderData[i].m_vecPosition[1], pRenderData[i].m_vecIntensity );
}
}
// We've got a global instance, no need to delete it
virtual void Release() {}
const eyeballstate_t *m_pState;
const Vector *m_pVRight;
const Vector *m_pVUp;
const Vector *m_pROrigin;
CStudioRender *m_pStudioRender;
};
static CGlintTextureRegenerator s_GlintTextureRegen;
static ITexture *s_pProcGlint = NULL;
void CStudioRender::PrecacheGlint()
{
if ( !m_pGlintTexture )
{
// Begin block in which all render targets should be allocated
g_pMaterialSystem->BeginRenderTargetAllocation();
// Get the texture that we are going to be updating procedurally.
m_pGlintTexture = g_pMaterialSystem->CreateNamedRenderTargetTextureEx2(
"_rt_eyeglint", 32, 32, RT_SIZE_NO_CHANGE, IMAGE_FORMAT_BGRA8888, MATERIAL_RT_DEPTH_NONE );
m_pGlintTexture->IncrementReferenceCount();
// Begin block in which all render targets should be allocated
g_pMaterialSystem->EndRenderTargetAllocation();
if ( !IsX360() )
{
// Get the texture that we are going to be updating procedurally.
s_pProcGlint = g_pMaterialSystem->CreateProceduralTexture(
"proc_eyeglint", TEXTURE_GROUP_MODEL, 32, 32, IMAGE_FORMAT_BGRA8888, TEXTUREFLAGS_NOMIP|TEXTUREFLAGS_NOLOD );
s_pProcGlint->SetTextureRegenerator( &s_GlintTextureRegen );
}
// JAY: I don't see this pattern in the code often. It looks like the material system
// would rather than I deal exclusively with IMaterials instead.
// So maybe we should bake the LOD texture into the eyes shader.
// For now, just hardcode one
// UNDONE: Add a $lodtexture to the eyes shader. Maybe add a $lodsize too.
// UNDONE: Make eyes texture load $lodtexture and switch to that here instead of black
m_pGlintLODTexture = g_pMaterialSystem->FindTexture( IsX360() ? "black" : "vgui/black", NULL, false );
m_pGlintLODTexture->IncrementReferenceCount();
}
}
void CStudioRender::UncacheGlint()
{
if ( m_pGlintTexture )
{
if ( s_pProcGlint )
{
s_pProcGlint->SetTextureRegenerator( NULL );
s_pProcGlint->DecrementReferenceCount();
s_pProcGlint = NULL;
}
m_pGlintTexture->DecrementReferenceCount();
m_pGlintTexture = NULL;
m_pGlintLODTexture->DecrementReferenceCount();
m_pGlintLODTexture = NULL;
}
}
int CStudioRender::BuildGlintRenderData( GlintRenderData_t *pData, int nMaxGlints,
const eyeballstate_t *pState, const Vector& vright, const Vector& vup, const Vector& r_origin )
{
// NOTE: See version 25 for lots of #if 0ed out stuff I removed
Vector viewdelta;
VectorSubtract( r_origin, pState->org, viewdelta );
VectorNormalize( viewdelta );
// hack cornea position
float iris_radius = pState->peyeball->radius * (6.0 / 12.0);
float cornea_radius = pState->peyeball->radius * (8.0 / 12.0);
Vector cornea;
// position on eyeball that matches iris radius
float er = ( iris_radius / pState->peyeball->radius );
er = FastSqrt( 1 - er * er );
// position on cornea sphere that matches iris radius
float cr = ( iris_radius / cornea_radius );
cr = FastSqrt( 1 - cr * cr );
float r = ( er * pState->peyeball->radius - cr * cornea_radius );
VectorScale( pState->forward, r, cornea );
// get offset for center of cornea
float dx, dy;
dx = DotProduct( vright, cornea );
dy = DotProduct( vup, cornea );
// move cornea to world space
VectorAdd( cornea, pState->org, cornea );
Vector delta, intensity;
Vector reflection, coord;
// Put in glints due to the lights in the scene
int nGlintCount = 0;
for ( int i = 0; R_LightGlintPosition( i, cornea, delta, intensity ); ++i )
{
VectorNormalize( delta );
if ( DotProduct( delta, pState->forward ) <= 0 )
continue;
VectorAdd( delta, viewdelta, reflection );
VectorNormalize( reflection );
pData[nGlintCount].m_vecPosition[0] = dx + cornea_radius * DotProduct( vright, reflection );
pData[nGlintCount].m_vecPosition[1] = dy + cornea_radius * DotProduct( vup, reflection );
pData[nGlintCount].m_vecIntensity = intensity;
if ( ++nGlintCount >= nMaxGlints )
return nMaxGlints;
if ( !R_LightGlintPosition( i, pState->org, delta, intensity ) )
continue;
VectorNormalize( delta );
if ( DotProduct( delta, pState->forward ) >= er )
continue;
pData[nGlintCount].m_vecPosition[0] = pState->peyeball->radius * DotProduct( vright, reflection );
pData[nGlintCount].m_vecPosition[1] = pState->peyeball->radius * DotProduct( vup, reflection );
pData[nGlintCount].m_vecIntensity = intensity;
if ( ++nGlintCount >= nMaxGlints )
return nMaxGlints;
}
return nGlintCount;
}
//-----------------------------------------------------------------------------
// Renders a glint texture procedurally
//-----------------------------------------------------------------------------
ITexture* CStudioRender::RenderGlintTexture( const eyeballstate_t *pState,
const Vector& vright, const Vector& vup, const Vector& r_origin )
{
GlintRenderData_t pRenderData[16];
int nGlintCount = BuildGlintRenderData( pRenderData, ARRAYSIZE(pRenderData),
pState, vright, vup, r_origin );
if ( nGlintCount == 0 )
return m_pGlintLODTexture;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->PushRenderTargetAndViewport( m_pGlintTexture );
IMaterial *pPrevMaterial = pRenderContext->GetCurrentMaterial();
void *pPrevProxy = pRenderContext->GetCurrentProxy();
int nPrevBoneCount = pRenderContext->GetCurrentNumBones();
MaterialHeightClipMode_t nPrevClipMode = pRenderContext->GetHeightClipMode( );
bool bPrevClippingEnabled = pRenderContext->EnableClipping( false );
bool bInFlashlightMode = pRenderContext->GetFlashlightMode();
if ( bInFlashlightMode )
{
DisableScissor();
}
pRenderContext->ClearColor4ub( 0, 0, 0, 0 );
pRenderContext->ClearBuffers( true, false, false );
pRenderContext->SetFlashlightMode( false );
pRenderContext->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_DISABLE );
pRenderContext->SetNumBoneWeights( 0 );
pRenderContext->Bind( m_pGlintBuildMaterial );
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
CMeshBuilder meshBuilder;
IMesh *pMesh = pRenderContext->GetDynamicMesh( );
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nGlintCount * 4, nGlintCount * 6 );
const float epsilon = 0.5f / 32.0f;
int nIndex = 0;
for ( int i = 0; i < nGlintCount; ++i )
{
const GlintRenderData_t &glint = pRenderData[i];
// Position of glint 0..31 range
float x = (glint.m_vecPosition.x + 0.5f) * m_GlintWidth;
float y = (glint.m_vecPosition.y + 0.5f) * m_GlintHeight;
Vector vGlintCenter = Vector( x, y, 0.0f );
float ooWidth = 1.0f / (float)m_GlintWidth;
float ooHeight = 1.0f / (float)m_GlintHeight;
int x0 = floor(x);
int y0 = floor(y);
int x1 = x0 + 1.0f;
int y1 = y0 + 1.0f;
x0 -= 2.0f; // Fill rules make us pad this out more than the procedural version
y0 -= 2.0f;
float screenX0 = x0 * 2 * ooWidth + epsilon - 1;
float screenX1 = x1 * 2 * ooWidth + epsilon - 1;
float screenY0 = -(y0 * 2 * ooHeight + epsilon - 1);
float screenY1 = -(y1 * 2 * ooHeight + epsilon - 1);
meshBuilder.Position3f( screenX0, screenY0, 0.0f );
meshBuilder.TexCoord2f( 0, x0, y0 );
meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( screenX1, screenY0, 0.0f );
meshBuilder.TexCoord2f( 0, x1, y0 );
meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( screenX1, screenY1, 0.0f );
meshBuilder.TexCoord2f( 0, x1, y1 );
meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.Position3f( screenX0, screenY1, 0.0f );
meshBuilder.TexCoord2f( 0, x0, y1 );
meshBuilder.TexCoord2fv( 1, vGlintCenter.Base() );
meshBuilder.TexCoord3fv( 2, glint.m_vecIntensity.Base() );
meshBuilder.AdvanceVertex();
meshBuilder.FastIndex( nIndex );
meshBuilder.FastIndex( nIndex+1 );
meshBuilder.FastIndex( nIndex+2 );
meshBuilder.FastIndex( nIndex );
meshBuilder.FastIndex( nIndex+2 );
meshBuilder.FastIndex( nIndex+3 );
nIndex += 4;
}
meshBuilder.End();
pMesh->Draw();
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
if ( IsX360() )
{
pRenderContext->CopyRenderTargetToTextureEx( m_pGlintTexture, 0, NULL, NULL );
}
pRenderContext->PopRenderTargetAndViewport( );
pRenderContext->Bind( pPrevMaterial, pPrevProxy );
pRenderContext->SetNumBoneWeights( nPrevBoneCount );
pRenderContext->SetHeightClipMode( nPrevClipMode );
pRenderContext->EnableClipping( bPrevClippingEnabled );
pRenderContext->SetFlashlightMode( bInFlashlightMode );
return m_pGlintTexture;
}
static ConVar r_glint_procedural( "r_glint_procedural", "0" );
static ConVar r_glint_alwaysdraw( "r_glint_alwaysdraw", "0" );
void CStudioRender::R_StudioEyeballGlint( const eyeballstate_t *pstate, IMaterialVar *pGlintVar,
const Vector& vright, const Vector& vup, const Vector& r_origin )
{
// Kick off a PIX event, since this process encompasses a bunch of locks etc...
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
PIXEVENT( pRenderContext, "GenerateEyeballGlint" );
// Don't do a procedural glint texture if there are enough pixels covered by the eyeball onscreen,
// and the eye isn't backfaced.
if ( m_pGlintLODTexture && r_glint_alwaysdraw.GetInt() == 0 )
{
// backfaced or too small to bother?
float pixelArea = pRenderContext->ComputePixelWidthOfSphere( pstate->org, pstate->peyeball->radius );
if(
// FIXME: this backface doesn't work for something that isn't a plane.
// DotProduct( pstate->forward, m_ViewPlaneNormal ) > 0.0f ||
pixelArea < m_pRC->m_Config.fEyeGlintPixelWidthLODThreshold )
{
// use black glint texture
pGlintVar->SetTextureValue( m_pGlintLODTexture );
return;
}
}
// Legacy method for DX8
if ( !IsX360() && ( r_glint_procedural.GetInt() || g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 ) )
{
// Set up the texture regenerator
s_GlintTextureRegen.m_pVRight = &vright;
s_GlintTextureRegen.m_pVUp = &vup;
s_GlintTextureRegen.m_pROrigin = &r_origin;
s_GlintTextureRegen.m_pState = pstate;
s_GlintTextureRegen.m_pStudioRender = this;
// This will cause the glint texture to be re-generated and then downloaded
s_pProcGlint->Download( );
// This is necessary to make sure we don't reconstitute the bits
// after coming back from a task switch
s_GlintTextureRegen.m_pStudioRender = NULL;
// Use the normal glint instead of the black glint
pGlintVar->SetTextureValue( s_pProcGlint );
}
else // Queued hardware version
{
// Make sure we know the correct size of the glint texture
m_GlintWidth = m_pGlintTexture->GetActualWidth();
m_GlintHeight = m_pGlintTexture->GetActualHeight();
// Render glint render target
ITexture *pUseGlintTexture = RenderGlintTexture( pstate, vright, vup, r_origin );
// Use the normal glint instead of the black glint
pGlintVar->SetTextureValue( pUseGlintTexture );
}
}
void CStudioRender::ComputeGlintTextureProjection( eyeballstate_t const* pState,
const Vector& vright, const Vector& vup, matrix3x4_t& mat )
{
// project eyeball into screenspace texture
float scale = 1.0 / (pState->peyeball->radius * 2);
VectorScale( &vright.x, scale, mat[0] );
VectorScale( &vup.x, scale, mat[1] );
mat[0][3] = -DotProduct( pState->org.Base(), mat[0] ) + 0.5;
mat[1][3] = -DotProduct( pState->org.Base(), mat[1] ) + 0.5;
}
/*
void R_MouthLighting( int count, const Vector *psrcverts, const Vector *psrcnorms, Vector4D *pdestlightvalues )
{
Vector forward;
if (m_pStudioHdr->nummouths < 1) return;
mstudiomouth_t *pMouth = r_pstudiohdr->pMouth( 0 ); // FIXME: this needs to get the mouth index from the shader
float fIllum = m_FlexWeights[pMouth->flexdesc];
if (fIllum < 0) fIllum = 0;
if (fIllum > 1) fIllum = 1;
fIllum = LinearToTexture( fIllum ) / 255.0;
VectorRotate( pMouth->forward, g_StudioInternalState.boneToWorld[ pMouth->bone ], forward );
for (int i = 0; i < count; i++)
{
float dot = -DotProduct( psrcnorms[i], forward );
if (dot > 0)
{
dot = LinearToTexture( dot ) / 255.0; // FIXME: this isn't robust
VectorScale( pdestlightvalues[i], dot, pdestlightvalues[i] );
}
else
VectorFill( pdestlightvalues[i], 0 );
VectorScale( pdestlightvalues[i], fIllum, pdestlightvalues[i] );
}
}
*/
void CStudioRender::R_MouthComputeLightingValues( float& fIllum, Vector& forward )
{
// FIXME: this needs to get the mouth index from the shader
mstudiomouth_t *pMouth = m_pStudioHdr->pMouth( 0 );
fIllum = m_pFlexWeights[pMouth->flexdesc];
if (fIllum < 0) fIllum = 0;
if (fIllum > 1) fIllum = 1;
fIllum = LinearToTexture( fIllum ) / 255.0;
VectorRotate( pMouth->forward, m_pBoneToWorld[ pMouth->bone ], forward );
}
void CStudioRender::R_MouthLighting( float fIllum, const Vector& normal, const Vector& forward, Vector &light )
{
float dot = -DotProduct( normal, forward );
if (dot > 0)
{
VectorScale( light, dot * fIllum, light );
}
else
{
VectorFill( light, 0 );
}
}
static unsigned int illumVarCache = 0;
static unsigned int forwardVarCache = 0;
void CStudioRender::R_MouthSetupVertexShader( IMaterial* pMaterial )
{
if (!pMaterial)
return;
// FIXME: this needs to get the mouth index from the shader
mstudiomouth_t *pMouth = m_pStudioHdr->pMouth( 0 );
// Don't deal with illum gamma, we apply it at a different point
// for vertex shaders
float fIllum = m_pFlexWeights[pMouth->flexdesc];
if (fIllum < 0) fIllum = 0;
if (fIllum > 1) fIllum = 1;
Vector forward;
VectorRotate( pMouth->forward, m_pBoneToWorld[ pMouth->bone ], forward );
forward *= -1;
IMaterialVar* pIllumVar = pMaterial->FindVarFast( "$illumfactor", &illumVarCache );
if (pIllumVar)
{
pIllumVar->SetFloatValue( fIllum );
}
IMaterialVar* pFowardVar = pMaterial->FindVarFast( "$forward", &forwardVarCache );
if (pFowardVar)
{
pFowardVar->SetVecValue( forward.Base(), 3 );
}
}
+166
View File
@@ -0,0 +1,166 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "studiorendercontext.h"
#include "optimize.h"
#include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
void CStudioRenderContext::GetTriangles( const DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, GetTriangles_Output_t &out )
{
VPROF( "CStudioRender::GetTriangles");
out.m_MaterialBatches.RemoveAll(); // clear out data.
if( !info.m_pStudioHdr || !info.m_pHardwareData ||
!info.m_pHardwareData->m_NumLODs || !info.m_pHardwareData->m_pLODs )
{
return;
}
int lod = info.m_Lod;
int lastlod = info.m_pHardwareData->m_NumLODs - 1;
if ( lod == USESHADOWLOD )
{
lod = lastlod;
}
else
{
lod = clamp( lod, 0, lastlod );
}
// clamp to root lod
if ( lod < info.m_pHardwareData->m_RootLOD)
{
lod = info.m_pHardwareData->m_RootLOD;
}
int nSkin = info.m_Skin;
if ( nSkin >= info.m_pStudioHdr->numskinfamilies )
{
nSkin = 0;
}
short *pSkinRef = info.m_pStudioHdr->pSkinref( nSkin * info.m_pStudioHdr->numskinref );
studiomeshdata_t *pStudioMeshes = info.m_pHardwareData->m_pLODs[lod].m_pMeshData;
IMaterial **ppMaterials = info.m_pHardwareData->m_pLODs[lod].ppMaterials;
// Bone to world must be set before calling this function; it uses it here
int boneMask = BONE_USED_BY_VERTEX_AT_LOD(lod);
ComputePoseToWorld( out.m_PoseToWorld, info.m_pStudioHdr, boneMask, m_RC.m_ViewOrigin, pBoneToWorld );
int i;
for (i=0 ; i < info.m_pStudioHdr->numbodyparts ; i++)
{
mstudiomodel_t *pModel = NULL;
R_StudioSetupModel( i, info.m_Body, &pModel, info.m_pStudioHdr );
// Iterate over all the meshes.... each mesh is a new material
int k;
for ( k = 0; k < pModel->nummeshes; ++k )
{
GetTriangles_MaterialBatch_t &materialBatch = out.m_MaterialBatches[out.m_MaterialBatches.AddToTail()];
mstudiomesh_t *pMesh = pModel->pMesh(k);
if ( !pModel->CacheVertexData( info.m_pStudioHdr ) )
{
// not available yet
continue;
}
const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( info.m_pStudioHdr );
Assert( vertData ); // This can only return NULL on X360 for now
// add the verts from this mesh to the materialBatch
materialBatch.m_Verts.SetCount( pMesh->numvertices );
for ( int vertID = 0; vertID < pMesh->numvertices; vertID++ )
{
GetTriangles_Vertex_t& vert = materialBatch.m_Verts[vertID];
vert.m_Position = *vertData->Position( vertID );
vert.m_Normal = *vertData->Normal( vertID );
vert.m_TexCoord = *vertData->Texcoord( vertID );
if (vertData->HasTangentData())
{
vert.m_TangentS = *vertData->TangentS( vertID );
}
#if _DEBUG
else
{
// ensure any unintended access faults
vert.m_TangentS.Init( VEC_T_NAN, VEC_T_NAN, VEC_T_NAN, VEC_T_NAN );
}
#endif
vert.m_NumBones = vertData->BoneWeights( vertID )->numbones;
int j;
for ( j = 0; j < vert.m_NumBones; j++ )
{
vert.m_BoneWeight[j] = vertData->BoneWeights( vertID )->weight[j];
vert.m_BoneIndex[j] = vertData->BoneWeights( vertID )->bone[j];
}
}
IMaterial *pMaterial = ppMaterials[pSkinRef[pMesh->material]];
Assert( pMaterial );
materialBatch.m_pMaterial = pMaterial;
studiomeshdata_t *pMeshData = &pStudioMeshes[pMesh->meshid];
if ( pMeshData->m_NumGroup == 0 )
continue;
// Clear out indices
materialBatch.m_TriListIndices.SetCount( 0 );
// Iterate over all stripgroups
int stripGroupID;
for ( stripGroupID = 0; stripGroupID < pMeshData->m_NumGroup; stripGroupID++ )
{
studiomeshgroup_t *pMeshGroup = &pMeshData->m_pMeshGroup[stripGroupID];
// bool bIsFlexed = ( pMeshGroup->m_Flags & MESHGROUP_IS_FLEXED ) != 0;
// bool bIsHWSkinned = ( pMeshGroup->m_Flags & MESHGROUP_IS_HWSKINNED ) != 0;
// Iterate over all strips. . . each strip potentially changes bones states.
int stripID;
for ( stripID = 0; stripID < pMeshGroup->m_NumStrips; stripID++ )
{
OptimizedModel::StripHeader_t *pStripData = &pMeshGroup->m_pStripData[stripID];
// int boneID;
// for( boneID = 0; boneID < pStripData->numBoneStateChanges; boneID++ )
// {
// OptimizedModel::BoneStateChangeHeader_t *pBoneStateChange = pStripData->pBoneStateChange( boneID );
// hardwareBoneToGlobalBone[pBoneStateChange->hardwareID] = pBoneStateChange->newBoneID;
// }
if ( pStripData->flags & OptimizedModel::STRIP_IS_TRILIST )
{
for ( int i = 0; i < pStripData->numIndices; i += 3 )
{
int idx = pStripData->indexOffset + i;
materialBatch.m_TriListIndices.AddToTail( pMeshGroup->MeshIndex( idx ) );
materialBatch.m_TriListIndices.AddToTail( pMeshGroup->MeshIndex( idx + 1 ) );
materialBatch.m_TriListIndices.AddToTail( pMeshGroup->MeshIndex( idx + 2 ) );
}
}
else
{
Assert( pStripData->flags & OptimizedModel::STRIP_IS_TRISTRIP );
for (int i = 0; i < pStripData->numIndices - 2; ++i)
{
int idx = pStripData->indexOffset + i;
bool ccw = (i & 0x1) == 0;
materialBatch.m_TriListIndices.AddToTail( pMeshGroup->MeshIndex( idx ) );
materialBatch.m_TriListIndices.AddToTail( pMeshGroup->MeshIndex( idx + 1 + ccw ) );
materialBatch.m_TriListIndices.AddToTail( pMeshGroup->MeshIndex( idx + 2 - ccw ) );
}
}
}
}
}
}
}
+542
View File
@@ -0,0 +1,542 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#include "r_studiolight.h"
#include "studiorender.h"
#include "studiorendercontext.h"
#include "studio.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "mathlib/vector.h"
#include "mathlib/mathlib.h"
#include <float.h>
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
void R_WorldLightDelta( const LightDesc_t *wl, const Vector& org, Vector& delta );
//-----------------------------------------------------------------------------
// Copies lighting state
//-----------------------------------------------------------------------------
int CopyLocalLightingState( int nMaxLights, LightDesc_t *pDest, int nLightCount, const LightDesc_t *pSrc )
{
// ensure we write within array bounds
if ( nLightCount > nMaxLights )
{
nLightCount = nMaxLights;
}
for( int i = 0; i < nLightCount; i++ )
{
LightDesc_t *pLight = &pDest[i];
memcpy( pLight, &pSrc[i], sizeof( LightDesc_t ) );
pLight->m_Flags = 0;
if( pLight->m_Attenuation0 != 0.0f )
{
pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0;
}
if( pLight->m_Attenuation1 != 0.0f )
{
pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1;
}
if( pLight->m_Attenuation2 != 0.0f )
{
pLight->m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2;
}
}
return nLightCount;
}
//-----------------------------------------------------------------------------
// Computes the ambient term
//-----------------------------------------------------------------------------
void R_LightAmbient_4D( const Vector& normal, Vector4D* pLightBoxColor, Vector &lv )
{
VectorScale( normal[0] > 0.f ? pLightBoxColor[0].AsVector3D() : pLightBoxColor[1].AsVector3D(), normal[0]*normal[0], lv );
VectorMA( lv, normal[1]*normal[1], normal[1] > 0.f ? pLightBoxColor[2].AsVector3D() : pLightBoxColor[3].AsVector3D(), lv );
VectorMA( lv, normal[2]*normal[2], normal[2] > 0.f ? pLightBoxColor[4].AsVector3D() : pLightBoxColor[5].AsVector3D(), lv );
}
#if defined( _WIN32 ) && !defined( _X360 )
void R_LightAmbient_4D( const FourVectors& normal, Vector4D* pLightBoxColor, FourVectors &lv )
{
// VPROF( "R_LightAmbient" );
// !!speed!! compute ambient color cube in sse format
static fltx4 FourZeros={0.,0.,0.,.0};
// find the contributions from each axis
fltx4 NegMask=CmpLtSIMD(normal.x,FourZeros);
fltx4 ColorSelect0=ReplicateX4(pLightBoxColor[0].AsVector3D().x);
fltx4 ColorSelect1=ReplicateX4(pLightBoxColor[1].AsVector3D().x);
fltx4 DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
fltx4 NormCompSquared=MulSIMD(normal.x,normal.x);
lv.x=MulSIMD(DirectionalColor,NormCompSquared);
ColorSelect0=ReplicateX4(pLightBoxColor[0].AsVector3D().y);
ColorSelect1=ReplicateX4(pLightBoxColor[1].AsVector3D().y);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
lv.y=MulSIMD(DirectionalColor,NormCompSquared);
ColorSelect0=ReplicateX4(pLightBoxColor[0].AsVector3D().z);
ColorSelect1=ReplicateX4(pLightBoxColor[1].AsVector3D().z);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
lv.z=MulSIMD(DirectionalColor,NormCompSquared);
NegMask=CmpLtSIMD(normal.y,FourZeros);
ColorSelect0=ReplicateX4(pLightBoxColor[2].AsVector3D().x);
ColorSelect1=ReplicateX4(pLightBoxColor[3].AsVector3D().x);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
NormCompSquared=MulSIMD(normal.y,normal.y);
lv.x=AddSIMD(lv.x,MulSIMD(DirectionalColor,NormCompSquared));
ColorSelect0=ReplicateX4(pLightBoxColor[2].AsVector3D().y);
ColorSelect1=ReplicateX4(pLightBoxColor[3].AsVector3D().y);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
lv.y=AddSIMD(lv.y,MulSIMD(DirectionalColor,NormCompSquared));
ColorSelect0=ReplicateX4(pLightBoxColor[2].AsVector3D().z);
ColorSelect1=ReplicateX4(pLightBoxColor[3].AsVector3D().z);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
lv.z=AddSIMD(lv.z,MulSIMD(DirectionalColor,NormCompSquared));
NegMask=CmpLtSIMD(normal.z,FourZeros);
ColorSelect0=ReplicateX4(pLightBoxColor[4].AsVector3D().x);
ColorSelect1=ReplicateX4(pLightBoxColor[5].AsVector3D().x);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
NormCompSquared=MulSIMD(normal.z,normal.z);
lv.x=AddSIMD(lv.x,MulSIMD(DirectionalColor,NormCompSquared));
ColorSelect0=ReplicateX4(pLightBoxColor[4].AsVector3D().y);
ColorSelect1=ReplicateX4(pLightBoxColor[5].AsVector3D().y);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
lv.y=AddSIMD(lv.y,MulSIMD(DirectionalColor,NormCompSquared));
ColorSelect0=ReplicateX4(pLightBoxColor[4].AsVector3D().z);
ColorSelect1=ReplicateX4(pLightBoxColor[5].AsVector3D().z);
DirectionalColor=OrSIMD(AndSIMD(ColorSelect1,NegMask),AndNotSIMD(NegMask,ColorSelect0));
lv.z=AddSIMD(lv.z,MulSIMD(DirectionalColor,NormCompSquared));
}
#endif
//-----------------------------------------------------------------------------
// Computes the ambient term, parameters are 3D Vectors for optimization
//-----------------------------------------------------------------------------
void R_LightAmbient_3D( const Vector& normal, const Vector* pLightBoxColor, Vector &lv )
{
VectorScale( normal[0] > 0.f ? pLightBoxColor[0] : pLightBoxColor[1], normal[0]*normal[0], lv );
VectorMA( lv, normal[1]*normal[1], normal[1] > 0.f ? pLightBoxColor[2] : pLightBoxColor[3], lv );
VectorMA( lv, normal[2]*normal[2], normal[2] > 0.f ? pLightBoxColor[4] : pLightBoxColor[5], lv );
}
//-----------------------------------------------------------------------------
// Set up light[i].dot, light[i].falloff, and light[i].delta for all lights given
// a vertex position "vert".
//-----------------------------------------------------------------------------
void R_LightStrengthWorld( const Vector& vert, int lightcount, LightDesc_t* pDesc, lightpos_t *light )
{
// VPROF( "R_LightStrengthWorld" );
// NJS: note to self, maybe switch here based on lightcount, so multiple squareroots can be done simeltaneously?
for ( int i = 0; i < lightcount; i++)
{
R_WorldLightDelta( &pDesc[i], vert, light[i].delta );
light[i].falloff = R_WorldLightDistanceFalloff( &pDesc[i], light[i].delta );
VectorNormalizeFast( light[i].delta );
light[i].dot = DotProduct( light[i].delta, pDesc[i].m_Direction );
}
}
//-----------------------------------------------------------------------------
// Calculate the delta between a light and position
//-----------------------------------------------------------------------------
void R_WorldLightDelta( const LightDesc_t *wl, const Vector& org, Vector& delta )
{
switch (wl->m_Type)
{
case MATERIAL_LIGHT_POINT:
case MATERIAL_LIGHT_SPOT:
VectorSubtract( wl->m_Position, org, delta );
break;
case MATERIAL_LIGHT_DIRECTIONAL:
VectorMultiply( wl->m_Direction, -1, delta );
break;
default:
// Bug: need to return an error
Assert( 0 );
break;
}
}
//#define NO_AMBIENT_CUBE 1
// TODO: cone clipping calc's wont work for boxlight since the player asks for a single point. Not sure what the volume is.
TEMPLATE_FUNCTION_TABLE( void, R_LightEffectsWorldFunctionTable, ( const LightDesc_t* pLightDesc, const lightpos_t *light, const Vector& normal, Vector &dest ), 256 )
{
enum
{
LightType1 = ( nArgument & 0xC0 ) >> 6,
LightType2 = ( nArgument & 0x30 ) >> 4,
LightType3 = ( nArgument & 0x0C ) >> 2,
LightType4 = ( nArgument & 0x03 )
};
// VPROF( "R_LightEffectsWorld" );
#ifdef NO_AMBIENT_CUBE
dest[0] = dest[1] = dest[2] = 0.0f;
#endif
// FIXME: lighting effects for normal and position are independent!
// FIXME: these can be pre-calculated per normal
if( (LightType_t)LightType1 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[0].falloff * CWorldLightAngleWrapper<LightType1>::WorldLightAngle( &pLightDesc[0], pLightDesc[0].m_Direction, normal, light[0].delta );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[0].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
if( (LightType_t)LightType2 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[1].falloff * CWorldLightAngleWrapper<LightType2>::WorldLightAngle( &pLightDesc[1], pLightDesc[1].m_Direction, normal, light[1].delta );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[1].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
if( (LightType_t)LightType3 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[2].falloff * CWorldLightAngleWrapper<LightType3>::WorldLightAngle( &pLightDesc[2], pLightDesc[2].m_Direction, normal, light[2].delta );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[2].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
if( (LightType_t)LightType4 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[3].falloff * CWorldLightAngleWrapper<LightType4>::WorldLightAngle( &pLightDesc[3], pLightDesc[3].m_Direction, normal, light[3].delta );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[3].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
}
TEMPLATE_FUNCTION_TABLE( void, R_LightEffectsWorldFunctionTableConstDirectional, ( const LightDesc_t* pLightDesc, const lightpos_t *light, const Vector& normal, Vector &dest, float flDirectionalConstant ), 256 )
{
enum
{
LightType1 = ( nArgument & 0xC0 ) >> 6,
LightType2 = ( nArgument & 0x30 ) >> 4,
LightType3 = ( nArgument & 0x0C ) >> 2,
LightType4 = ( nArgument & 0x03 )
};
// VPROF( "R_LightEffectsWorld" );
#ifdef NO_AMBIENT_CUBE
dest[0] = dest[1] = dest[2] = 0.0f;
#endif
// FIXME: lighting effects for normal and position are independent!
// FIXME: these can be pre-calculated per normal
if( (LightType_t)LightType1 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[0].falloff *
CWorldLightAngleWrapperConstDirectional<LightType1>::WorldLightAngle( &pLightDesc[0],
pLightDesc[0].m_Direction, normal, light[0].delta, flDirectionalConstant );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[0].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
if( (LightType_t)LightType2 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[1].falloff *
CWorldLightAngleWrapperConstDirectional<LightType2>::WorldLightAngle( &pLightDesc[1],
pLightDesc[1].m_Direction, normal, light[1].delta, flDirectionalConstant );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[1].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
if( (LightType_t)LightType3 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[2].falloff *
CWorldLightAngleWrapperConstDirectional<LightType3>::WorldLightAngle( &pLightDesc[2],
pLightDesc[2].m_Direction, normal, light[2].delta, flDirectionalConstant );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[2].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
if( (LightType_t)LightType4 != MATERIAL_LIGHT_DISABLE )
{
float ratio = light[3].falloff *
CWorldLightAngleWrapperConstDirectional<LightType4>::WorldLightAngle( &pLightDesc[3],
pLightDesc[3].m_Direction, normal, light[3].delta, flDirectionalConstant );
if (ratio > 0)
{
const float* pColor = (float*)&pLightDesc[3].m_Color;
dest[0] += pColor[0] * ratio;
dest[1] += pColor[1] * ratio;
dest[2] += pColor[2] * ratio;
}
}
}
//-----------------------------------------------------------------------------
// Get the function table index
//-----------------------------------------------------------------------------
static int s_pLightMask[ 5 ] =
{
0, // No lights
0xC0, // 1 light
0xF0, // 2 lights
0xFC, // 3 lights
0xFF, // 4 lights
};
inline int R_LightEffectsWorldIndex(const LightDesc_t* pLightDesc, int nNumLights)
{
if ( nNumLights > 4 )
{
nNumLights = 4;
}
int nIndex = ((pLightDesc[0].m_Type & 0x3) << 6) | ((pLightDesc[1].m_Type & 0x3) << 4) | ( (pLightDesc[2].m_Type & 0x3) << 2) | (pLightDesc[3].m_Type & 0x3);
nIndex &= s_pLightMask[ nNumLights ];
Assert( nIndex >= 0 && nIndex < R_LightEffectsWorldFunctionTable::count );
return nIndex;
}
/*
light_direction (light_pos - vertex_pos)
*/
// TODO: move cone calcs to position
// TODO: cone clipping calc's wont work for boxlight since the player asks for a single point. Not sure what the volume is.
TEMPLATE_FUNCTION_TABLE( float, R_WorldLightDistanceFalloffFunctionTable, ( const LightDesc_t *wl, const Vector& delta ), 8)
{
Assert( nArgument != 0 );
float dist2 = DotProduct( delta, delta );
// Cull out light beyond this radius
if (wl->m_Range != 0.f)
{
if (dist2 > wl->m_Range * wl->m_Range)
return 0.0f;
}
// The general purpose equation:
float fTotal = FLT_EPSILON;
if( nArgument & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0 )
{
fTotal = wl->m_Attenuation0;
}
if( nArgument & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1 )
{
fTotal += wl->m_Attenuation1 * FastSqrt( dist2 );
}
if( nArgument & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2 )
{
fTotal += wl->m_Attenuation2 * dist2;
}
return 1.0f / fTotal;
}
//-----------------------------------------------------------------------------
// Calculate the falloff from the world lights
//-----------------------------------------------------------------------------
float FASTCALL R_WorldLightDistanceFalloff( const LightDesc_t *wl, const Vector& delta )
{
// Ensure no invalid flags are set
Assert( ! ( wl->m_Flags & ~(LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0|LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1|LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2|LIGHTTYPE_OPTIMIZATIONFLAGS_DERIVED_VALUES_CALCED) ) );
// calculate falloff
int flags = wl->m_Flags & (LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0|LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1|LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2);
return R_WorldLightDistanceFalloffFunctionTable::functions[flags](wl, delta);
}
#if defined( _WIN32 ) && !defined( _X360 )
fltx4 FASTCALL R_WorldLightDistanceFalloff( const LightDesc_t *wl, const FourVectors &delta )
{
// !!speed!!: lights could store m_Attenuation2,m_Attenuation1, and m_Range^2 copies in replicated SSE format.
// Ensure no invalid flags are set
Assert( ! ( wl->m_Flags & ~(LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0|LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1|LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2|LIGHTTYPE_OPTIMIZATIONFLAGS_DERIVED_VALUES_CALCED) ) );
fltx4 dist2 = delta*delta;
fltx4 fTotal;
if( wl->m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0 )
{
fTotal = ReplicateX4(wl->m_Attenuation0);
}
else
fTotal= ReplicateX4(FLT_EPSILON); // !!speed!! replicate
if( wl->m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1 )
{
fTotal=AddSIMD(fTotal,MulSIMD(ReplicateX4(wl->m_Attenuation1),SqrtEstSIMD(dist2)));
}
if( wl->m_Flags & LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2 )
{
fTotal=AddSIMD(fTotal,MulSIMD(ReplicateX4(wl->m_Attenuation2),dist2));
}
fTotal=ReciprocalEstSIMD(fTotal);
// Cull out light beyond this radius
// now, zero out elements for which dist2 was > range^2. !!speed!! lights should store dist^2 in sse format
if (wl->m_Range != 0.f)
{
fltx4 RangeSquared = ReplicateX4(wl->m_Range*wl->m_Range); // !!speed!!
fTotal=AndSIMD(fTotal,CmpLtSIMD(dist2,RangeSquared));
}
return fTotal;
}
#endif
int CStudioRender::R_LightGlintPosition( int index, const Vector& org, Vector& delta, Vector& intensity )
{
if (index >= m_pRC->m_NumLocalLights)
return false;
R_WorldLightDelta( &m_pRC->m_LocalLights[index], org, delta );
float falloff = R_WorldLightDistanceFalloff( &m_pRC->m_LocalLights[index], delta );
VectorMultiply( m_pRC->m_LocalLights[index].m_Color, falloff, intensity );
return true;
}
//-----------------------------------------------------------------------------
// Setup up the function table
//-----------------------------------------------------------------------------
void CStudioRender::R_InitLightEffectsWorld3()
{
// set the function pointer
int index = R_LightEffectsWorldIndex( m_pRC->m_LocalLights, m_pRC->m_NumLocalLights );
R_LightEffectsWorld3 = R_LightEffectsWorldFunctionTable::functions[index];
}
//-----------------------------------------------------------------------------
// Performs lighting functions common to the ComputeLighting and ComputeLightingConstantDirectional
// returns the index of the LightEffectsWorldFunction to use
//-----------------------------------------------------------------------------
static int ComputeLightingCommon( const Vector* pAmbient, int lightCount,
LightDesc_t* pLights, const Vector& pt, const Vector& normal, lightpos_t *pLightPos, Vector& lighting )
{
// Set up lightpos[i].dot, lightpos[i].falloff, and lightpos[i].delta for all lights
R_LightStrengthWorld( pt, lightCount, pLights, pLightPos );
// calculate ambient values from the ambient cube given a normal.
R_LightAmbient_3D( normal, pAmbient, lighting );
return R_LightEffectsWorldIndex( pLights, lightCount );
}
//-----------------------------------------------------------------------------
// Compute the lighting at a point and normal
// Final Lighting is in linear space
//-----------------------------------------------------------------------------
void CStudioRenderContext::ComputeLighting( const Vector* pAmbient, int lightCount,
LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting )
{
if ( m_RC.m_Config.fullbright )
{
lighting.Init( 1.0f, 1.0f, 1.0f );
return;
}
if ( lightCount > ARRAYSIZE( m_pLightPos ) )
{
AssertMsg( 0, "Light count out of range in ComputeLighting\n" );
lightCount = ARRAYSIZE( m_pLightPos );
}
// Calculate color given lightpos_t lightpos, a normal, and the ambient
// color from the ambient cube calculated in ComputeLightingCommon
int index = ComputeLightingCommon( pAmbient, lightCount, pLights, pt, normal, m_pLightPos, lighting );
R_LightEffectsWorldFunctionTable::functions[index]( pLights, m_pLightPos, normal, lighting );
}
//-----------------------------------------------------------------------------
// Compute the lighting at a point and normal
// Final Lighting is in linear space
// Uses flDirectionalAmount instead of directional components of lights
//-----------------------------------------------------------------------------
void CStudioRenderContext::ComputeLightingConstDirectional( const Vector* pAmbient, int lightCount,
LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting, float flDirectionalAmount )
{
if ( m_RC.m_Config.fullbright )
{
lighting.Init( 1.0f, 1.0f, 1.0f );
return;
}
if ( lightCount > ARRAYSIZE( m_pLightPos ) )
{
AssertMsg( 0, "Light count out of range in ComputeLighting\n" );
lightCount = ARRAYSIZE( m_pLightPos );
}
// Calculate color given lightpos_t lightpos, a normal, and the ambient
// color from the ambient cube calculated in ComputeLightingCommon
int index = ComputeLightingCommon( pAmbient, lightCount, pLights, pt, normal, m_pLightPos, lighting );
R_LightEffectsWorldFunctionTableConstDirectional::functions[index]( pLights, m_pLightPos, normal, lighting, flDirectionalAmount );
}
+49
View File
@@ -0,0 +1,49 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Stateless light computation routines
//
//===========================================================================//
#ifndef R_STUDIOLIGHT_H
#define R_STUDIOLIGHT_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/platform.h"
#if defined( _WIN32 ) && !defined( _X360 )
#include <xmmintrin.h>
#endif
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class Vector;
class Vector4D;
class FourVectors;
struct lightpos_t;
struct LightDesc_t;
//-----------------------------------------------------------------------------
// Stateless light computation routines
//-----------------------------------------------------------------------------
// Computes the ambient term
void R_LightAmbient_4D( const Vector& normal, Vector4D* pLightBoxColor, Vector &lv );
void R_LightStrengthWorld( const Vector& vert, int lightcount, LightDesc_t* pLightDesc, lightpos_t *light );
float FASTCALL R_WorldLightDistanceFalloff( const LightDesc_t *wl, const Vector& delta );
// Copies lighting state into a buffer, returns number of lights copied
int CopyLocalLightingState( int nMaxLights, LightDesc_t *pDest, int nLightCount, const LightDesc_t *pSrc );
#if defined( _WIN32 ) && !defined( _X360 )
// SSE optimized versions
void R_LightAmbient_4D( const FourVectors& normal, Vector4D* pLightBoxColor, FourVectors &lv );
__m128 FASTCALL R_WorldLightDistanceFalloff( const LightDesc_t *wl, const FourVectors& delta );
#endif
#endif // R_STUDIOLIGHT_H
+389
View File
@@ -0,0 +1,389 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "studiorender.h"
#include "studiorendercontext.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imesh.h"
#include "optimize.h"
#include "mathlib/vmatrix.h"
#include "tier0/vprof.h"
#include "tier1/strtools.h"
#include "tier1/KeyValues.h"
#include "tier0/memalloc.h"
#include "convar.h"
#include "materialsystem/itexture.h"
#include "tier2/tier2.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static ConVar r_studio_stats( "r_studio_stats", "0", FCVAR_CHEAT );
inline float TriangleArea( const Vector &v0, const Vector &v1, const Vector &v2 )
{
Vector vecEdge0, vecEdge1, vecCross;
VectorSubtract( v1, v0, vecEdge0 );
VectorSubtract( v2, v0, vecEdge1 );
CrossProduct( vecEdge0, vecEdge1, vecCross );
return ( VectorLength( vecCross ) * 0.5f );
}
void CStudioRender::R_GatherStats( studiomeshgroup_t *pGroup, CMeshBuilder &MeshBuilder, IMesh *pMesh, IMaterial *pMaterial )
{
int nCount = 0;
float flSurfaceArea = 0.0f;
float flTriSurfaceArea = 0.0f;
float flTextureSurfaceArea = 0.0f;
int nFrontFacing = 0;
CUtlVector< Vector > Positions;
CUtlVector< Vector2D > TextureCoords;
CUtlVector< short > Indexes;
CUtlVector< float > TriAreas;
CUtlVector< float > TextureAreas;
int nTextureWidth = 0;
int nTextureHeight = 0;
IMaterialVar **pMaterialVar = pMaterial->GetShaderParams();
for( int i = 0; i < pMaterial->ShaderParamCount(); i++ )
{
if ( pMaterialVar[ i ]->IsTexture() == false )
{
continue;
}
ITexture *pTexture = pMaterialVar[ i ]->GetTextureValue();
if ( pTexture == NULL )
{
continue;
}
int nWidth = pTexture->GetActualWidth();
if ( nWidth > nTextureWidth )
{
nTextureWidth = nWidth;
}
int nHeight = pTexture->GetActualHeight();
if ( nHeight > nTextureHeight )
{
nTextureHeight = nHeight;
}
}
Vector2D vTextureSize( nTextureWidth, nTextureHeight );
VMatrix m_ViewMatrix, m_ProjectionMatrix, m_ViewProjectionMatrix;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->GetMatrix( MATERIAL_VIEW, &m_ViewMatrix );
pRenderContext->GetMatrix( MATERIAL_PROJECTION, &m_ProjectionMatrix );
MatrixMultiply( m_ProjectionMatrix, m_ViewMatrix, m_ViewProjectionMatrix );
Positions.EnsureCapacity( MeshBuilder.VertexCount() );
Positions.SetCount( MeshBuilder.VertexCount() );
TextureCoords.EnsureCapacity( MeshBuilder.VertexCount() );
TextureCoords.SetCount( MeshBuilder.VertexCount() );
for( int i = 0; i < MeshBuilder.VertexCount(); i++ )
{
MeshBuilder.SelectVertex( i );
Positions[ i ] = *( const Vector * )MeshBuilder.Position();
TextureCoords[ i ] = ( *( const Vector2D * )MeshBuilder.TexCoord( 0 ) ) * vTextureSize;
}
int nNumIndexes = 0;
for (int j = 0; j < pGroup->m_NumStrips; ++j)
{
OptimizedModel::StripHeader_t *pStrip = &pGroup->m_pStripData[ j ];
nNumIndexes += pStrip->numIndices;
}
Indexes.EnsureCapacity( nNumIndexes );
Indexes.SetCount( nNumIndexes );
TriAreas.EnsureCapacity( nNumIndexes / 3 );
TriAreas.SetCount( nNumIndexes / 3 );
TextureAreas.EnsureCapacity( nNumIndexes / 3 );
TextureAreas.SetCount( nNumIndexes / 3 );
for (int j = 0; j < pGroup->m_NumStrips; ++j)
{
OptimizedModel::StripHeader_t *pStrip = &pGroup->m_pStripData[ j ];
for( int i = 0; i < pStrip->numIndices; i += 3 )
{
Indexes[ pStrip->indexOffset + i ] = pGroup->m_pIndices[ pStrip->indexOffset + i ];
Indexes[ pStrip->indexOffset + i + 1 ] = pGroup->m_pIndices[ pStrip->indexOffset + i + 1 ];
Indexes[ pStrip->indexOffset + i + 2 ] = pGroup->m_pIndices[ pStrip->indexOffset + i + 2 ];
TriAreas[ ( pStrip->indexOffset + i ) / 3 ] = 0.0f;
TextureAreas[ ( pStrip->indexOffset + i ) / 3 ] = 0.0f;
}
}
const float UNIFORM_SCREEN_WIDTH = 1600.0f;
const float UNIFORM_SCREEN_HEIGHT = 1200.0f;
for (int j = 0; j < pGroup->m_NumStrips; ++j)
{
OptimizedModel::StripHeader_t *pStrip = &pGroup->m_pStripData[ j ];
for( int i = 0; i < pStrip->numIndices; i += 3 )
{
int nIndex1 = pGroup->m_pIndices[ pStrip->indexOffset + i ];
int nIndex2 = pGroup->m_pIndices[ pStrip->indexOffset + i + 1 ];
int nIndex3 = pGroup->m_pIndices[ pStrip->indexOffset + i + 2 ];
MeshBuilder.SelectVertex( nIndex1 );
const float *pPos1 = MeshBuilder.Position();
MeshBuilder.SelectVertex( nIndex2 );
const float *pPos2 = MeshBuilder.Position();
MeshBuilder.SelectVertex( nIndex3 );
const float *pPos3 = MeshBuilder.Position();
float flTriArea = TriangleArea( *( const Vector * )( pPos1 ), *( const Vector * )( pPos2 ), *( const Vector * )( pPos3 ) );
flSurfaceArea += flTriArea;
Vector V1View, V2View, V3View;
m_ViewProjectionMatrix.V3Mul( *( const Vector * )( pPos1 ), V1View );
m_ViewProjectionMatrix.V3Mul( *( const Vector * )( pPos2 ), V2View );
m_ViewProjectionMatrix.V3Mul( *( const Vector * )( pPos3 ), V3View );
Vector vNormal;
float flIntercept;
ComputeTrianglePlane( V1View, V2View, V3View, vNormal, flIntercept );
V1View = ( V1View * 0.5f ) + Vector( 0.5f, 0.5f, 0.5f );
V1View *= Vector( UNIFORM_SCREEN_WIDTH, UNIFORM_SCREEN_HEIGHT, 1.0f );
V2View = ( V2View * 0.5f ) + Vector( 0.5f, 0.5f, 0.5f );
V2View *= Vector( UNIFORM_SCREEN_WIDTH, UNIFORM_SCREEN_HEIGHT, 1.0f );
V3View = ( V3View * 0.5f ) + Vector( 0.5f, 0.5f, 0.5f );
V3View *= Vector( UNIFORM_SCREEN_WIDTH, UNIFORM_SCREEN_HEIGHT, 1.0f );
flTriArea = -TriArea2D( V1View, V2View, V3View );
if ( flTriArea > 0.0f )
{
nFrontFacing++;
flTriSurfaceArea += flTriArea;
TriAreas[ ( pStrip->indexOffset + i ) / 3 ] = flTriArea;
Vector2D TexV1 = TextureCoords[ nIndex1 ];
Vector2D TexV2 = TextureCoords[ nIndex2 ];
Vector2D TexV3 = TextureCoords[ nIndex3 ];
flTriArea = fabs( TriArea2D( TexV1, TexV2, TexV3 ) );
flTextureSurfaceArea += flTriArea;
TextureAreas[ ( pStrip->indexOffset + i ) / 3 ] = flTriArea;
}
}
nCount += pStrip->numIndices;
}
// Msg( "%d / %d / %g / %g ||| %d / %g\n", MeshBuilder.VertexCount(), nCount, flSurfaceArea, flTriSurfaceArea, nFrontFacing, flTriSurfaceArea / (float)nFrontFacing );
for( int i = 0; i < MeshBuilder.VertexCount(); i++ )
{
MeshBuilder.SelectVertex( i );
MeshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
}
MeshBuilder.End();
pMesh->MarkAsDrawn();
pMaterial = materials->FindMaterial( "debug/modelstats", TEXTURE_GROUP_OTHER );
pRenderContext->Bind( pMaterial );
int nRenderCount = -1;
for (int j = 0; j < pGroup->m_NumStrips; ++j)
{
OptimizedModel::StripHeader_t *pStrip = &pGroup->m_pStripData[ j ];
for( int i = 0; i < pStrip->numIndices; i += 3 )
{
if ( nRenderCount >= 10000 || nRenderCount == -1 )
{
if ( nRenderCount >= 0 )
{
MeshBuilder.End( false, true );
}
pMesh = pRenderContext->GetDynamicMeshEx( false );
nRenderCount = 0;
if ( nFrontFacing > 10000 )
{
MeshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 10000 );
nFrontFacing -= 10000;
}
else
{
MeshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nFrontFacing );
}
}
int nIndex1 = Indexes[ pStrip->indexOffset + i ];
int nIndex2 = Indexes[ pStrip->indexOffset + i + 1 ];
int nIndex3 = Indexes[ pStrip->indexOffset + i + 2 ];
float flArea = TriAreas[ ( pStrip->indexOffset + i ) / 3 ];
if ( flArea > 0.0f )
{
Vector vColor;
if ( r_studio_stats.GetInt() == 1 )
{
if ( flArea < 20.0f )
{
vColor.Init( 1.0f, 0.0f, 0.0f );
}
else if ( flArea < 50.0f )
{
vColor.Init( 1.0f, 0.565f, 0.0f );
}
else if ( flArea < 100.0f )
{
vColor.Init( 1.0f, 0.871f, 0.0f );
}
else if ( flArea < 200.0f )
{
vColor.Init( 0.701f, 1.0f, 0.0f );
}
else
{
vColor.Init( 0.0f, 1.0f, 0.0f );
}
}
else
{
float flArea = TextureAreas[ ( pStrip->indexOffset + i ) / 3 ] / TriAreas[ ( pStrip->indexOffset + i ) / 3 ];
if ( flArea >= 16.0f )
{
vColor.Init( 1.0f, 0.0f, 0.0f );
}
else if ( flArea >= 8.0f )
{
vColor.Init( 1.0f, 0.565f, 0.0f );
}
else if ( flArea >= 4.0f )
{
vColor.Init( 1.0f, 0.871f, 0.0f );
}
else if ( flArea >= 2.0f )
{
vColor.Init( 0.701f, 1.0f, 0.0f );
}
else if ( flArea >= 1.0f )
{
vColor.Init( 0.0f, 1.0f, 0.0f );
}
else
{
vColor.Init( 0.0f, 0.871f, 1.0f );
}
}
MeshBuilder.Position3fv( Positions[ nIndex1 ].Base() );
MeshBuilder.Color3fv( vColor.Base() );
MeshBuilder.AdvanceVertex();
MeshBuilder.Position3fv( Positions[ nIndex2 ].Base() );
MeshBuilder.Color3fv( vColor.Base() );
MeshBuilder.AdvanceVertex();
MeshBuilder.Position3fv( Positions[ nIndex3 ].Base() );
MeshBuilder.Color3fv( vColor.Base() );
MeshBuilder.AdvanceVertex();
nRenderCount++;
}
}
}
if ( nRenderCount >= 0 )
{
MeshBuilder.End( false, true );
}
}
//-----------------------------------------------------------------------------
// Main model rendering entry point
//-----------------------------------------------------------------------------
void CStudioRender::ModelStats( const DrawModelInfo_t& info, const StudioRenderContext_t &rc,
matrix3x4_t *pBoneToWorld, const FlexWeights_t &flex, int flags )
{
StudioRenderContext_t StatsRC = rc;
StatsRC.m_Config.m_bStatsMode = true;
m_pRC = const_cast< StudioRenderContext_t* >( &StatsRC );
m_pFlexWeights = flex.m_pFlexWeights;
m_pFlexDelayedWeights = flex.m_pFlexDelayedWeights;
m_pBoneToWorld = pBoneToWorld;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// Disable flex if we're told to...
bool flexConfig = m_pRC->m_Config.bFlex;
if (flags & STUDIORENDER_DRAW_NO_FLEXES)
{
m_pRC->m_Config.bFlex = false;
}
// Enable wireframe if we're told to...
bool bWireframe = m_pRC->m_Config.bWireframe;
if ( flags & STUDIORENDER_DRAW_WIREFRAME )
{
m_pRC->m_Config.bWireframe = true;
}
int boneMask = BONE_USED_BY_VERTEX_AT_LOD( info.m_Lod );
// Preserve the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
m_VertexCache.StartModel();
m_pStudioHdr = info.m_pStudioHdr;
m_pStudioMeshes = info.m_pHardwareData->m_pLODs[info.m_Lod].m_pMeshData;
// Bone to world must be set before calling drawmodel; it uses that here
ComputePoseToWorld( m_PoseToWorld, m_pStudioHdr, boneMask, m_pRC->m_ViewOrigin, pBoneToWorld );
R_StudioRenderModel( pRenderContext, info.m_Skin, info.m_Body, info.m_HitboxSet, info.m_pClientEntity,
info.m_pHardwareData->m_pLODs[info.m_Lod].ppMaterials,
info.m_pHardwareData->m_pLODs[info.m_Lod].pMaterialFlags, flags, boneMask, info.m_Lod, info.m_pColorMeshes);
// Restore the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
// Restore the configs
m_pRC->m_Config.bFlex = flexConfig;
m_pRC->m_Config.bWireframe = bWireframe;
#ifdef REPORT_FLEX_STATS
GetFlexStats();
#endif
StatsRC.m_Config.m_bStatsMode = false;
pRenderContext->SetNumBoneWeights( 0 );
m_pRC = NULL;
m_pBoneToWorld = NULL;
m_pFlexWeights = NULL;
m_pFlexDelayedWeights = NULL;
}
+762
View File
@@ -0,0 +1,762 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include <stdlib.h>
#include "studiorender.h"
#include "studiorendercontext.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imesh.h"
#include "optimize.h"
#include "mathlib/vmatrix.h"
#include "tier0/vprof.h"
#include "tier1/strtools.h"
#include "tier1/KeyValues.h"
#include "tier0/memalloc.h"
#include "convar.h"
#include "materialsystem/itexture.h"
#include "tier2/tier2.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
CStudioRender g_StudioRender;
CStudioRender *g_pStudioRenderImp = &g_StudioRender;
//-----------------------------------------------------------------------------
// Activate to get stats
//-----------------------------------------------------------------------------
//#define REPORT_FLEX_STATS 1
#ifdef REPORT_FLEX_STATS
static int s_nModelsDrawn = 0;
static int s_nActiveFlexCount = 0;
static ConVar r_flexstats( "r_flexstats", "0", FCVAR_CHEAT );
#endif
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CStudioRender::CStudioRender()
{
m_pRC = NULL;
m_pBoneToWorld = NULL;
m_pFlexWeights = NULL;
m_pFlexDelayedWeights = NULL;
m_pStudioHdr = NULL;
m_pStudioMeshes = NULL;
m_pSubModel = NULL;
m_pGlintTexture = NULL;
m_GlintWidth = 0;
m_GlintHeight = 0;
// Cache-align our important matrices
MemAlloc_PushAllocDbgInfo( __FILE__, __LINE__ );
m_PoseToWorld = (matrix3x4_t*)MemAlloc_AllocAligned( MAXSTUDIOBONES * sizeof(matrix3x4_t), 32 );
m_PoseToDecal = (matrix3x4_t*)MemAlloc_AllocAligned( MAXSTUDIOBONES * sizeof(matrix3x4_t), 32 );
MemAlloc_PopAllocDbgInfo();
m_nDecalId = 1;
}
CStudioRender::~CStudioRender()
{
MemAlloc_FreeAligned(m_PoseToWorld);
MemAlloc_FreeAligned(m_PoseToDecal);
}
void CStudioRender::InitDebugMaterials( void )
{
m_pMaterialMRMWireframe =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true );
m_pMaterialMRMWireframe->IncrementReferenceCount();
m_pMaterialMRMWireframeZBuffer =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframezbuffer", TEXTURE_GROUP_OTHER, true );
m_pMaterialMRMWireframeZBuffer->IncrementReferenceCount();
m_pMaterialMRMNormals =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmnormals", TEXTURE_GROUP_OTHER, true );
m_pMaterialMRMNormals->IncrementReferenceCount();
m_pMaterialTangentFrame =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugvertexcolor", TEXTURE_GROUP_OTHER, true );
m_pMaterialTangentFrame->IncrementReferenceCount();
m_pMaterialTranslucentModelHulls =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugtranslucentmodelhulls", TEXTURE_GROUP_OTHER, true );
m_pMaterialTranslucentModelHulls->IncrementReferenceCount();
m_pMaterialSolidModelHulls =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugsolidmodelhulls", TEXTURE_GROUP_OTHER, true );
m_pMaterialSolidModelHulls->IncrementReferenceCount();
m_pMaterialAdditiveVertexColorVertexAlpha =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/additivevertexcolorvertexalpha", TEXTURE_GROUP_OTHER, true );
m_pMaterialAdditiveVertexColorVertexAlpha->IncrementReferenceCount();
m_pMaterialModelBones =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmodelbones", TEXTURE_GROUP_OTHER, true );
m_pMaterialModelBones->IncrementReferenceCount();
m_pMaterialModelEnvCubemap =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/env_cubemap_model", TEXTURE_GROUP_OTHER, true );
m_pMaterialModelEnvCubemap->IncrementReferenceCount();
m_pMaterialWorldWireframe =
g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugworldwireframe", TEXTURE_GROUP_OTHER, true );
m_pMaterialWorldWireframe->IncrementReferenceCount();
if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 )
{
KeyValues *pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 0 );
m_pDepthWrite[0][0] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite00", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pDepthWrite[0][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 1 );
m_pDepthWrite[0][1] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite01", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pDepthWrite[0][1]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 0 );
m_pDepthWrite[1][0] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite10", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pDepthWrite[1][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 1 );
m_pDepthWrite[1][1] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite11", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pDepthWrite[1][1]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[0][0] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite00", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[0][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 0 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[0][1] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite01", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[0][1]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 0 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[1][0] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite10", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[1][0]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "DepthWrite" );
pVMTKeyValues->SetInt( "$no_fullbright", 1 );
pVMTKeyValues->SetInt( "$alphatest", 1 );
pVMTKeyValues->SetInt( "$nocull", 1 );
pVMTKeyValues->SetInt( "$color_depth", 1 );
m_pSSAODepthWrite[1][1] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite11", TEXTURE_GROUP_OTHER, pVMTKeyValues );
m_pSSAODepthWrite[1][1]->IncrementReferenceCount();
pVMTKeyValues = new KeyValues( "EyeGlint" );
m_pGlintBuildMaterial = g_pMaterialSystem->CreateMaterial( "___glintbuildmaterial", pVMTKeyValues );
}
}
void CStudioRender::ShutdownDebugMaterials( void )
{
#ifdef _WIN32
if ( m_pMaterialMRMWireframe )
{
m_pMaterialMRMWireframe->DecrementReferenceCount();
m_pMaterialMRMWireframe = NULL;
}
if ( m_pMaterialMRMWireframeZBuffer )
{
m_pMaterialMRMWireframeZBuffer->DecrementReferenceCount();
m_pMaterialMRMWireframeZBuffer = NULL;
}
if ( m_pMaterialMRMNormals )
{
m_pMaterialMRMNormals->DecrementReferenceCount();
m_pMaterialMRMNormals = NULL;
}
if ( m_pMaterialTangentFrame )
{
m_pMaterialTangentFrame->DecrementReferenceCount();
m_pMaterialTangentFrame = NULL;
}
if ( m_pMaterialTranslucentModelHulls )
{
m_pMaterialTranslucentModelHulls->DecrementReferenceCount();
m_pMaterialTranslucentModelHulls = NULL;
}
if ( m_pMaterialSolidModelHulls )
{
m_pMaterialSolidModelHulls->DecrementReferenceCount();
m_pMaterialSolidModelHulls = NULL;
}
if ( m_pMaterialAdditiveVertexColorVertexAlpha )
{
m_pMaterialAdditiveVertexColorVertexAlpha->DecrementReferenceCount();
m_pMaterialAdditiveVertexColorVertexAlpha = NULL;
}
if ( m_pMaterialModelBones )
{
m_pMaterialModelBones->DecrementReferenceCount();
m_pMaterialModelBones = NULL;
}
if ( m_pMaterialModelEnvCubemap )
{
m_pMaterialModelEnvCubemap->DecrementReferenceCount();
m_pMaterialModelEnvCubemap = NULL;
}
if ( m_pMaterialWorldWireframe )
{
m_pMaterialWorldWireframe->DecrementReferenceCount();
m_pMaterialWorldWireframe = NULL;
}
// DepthWrite materials
for ( int32 i = 0; i < 4; i++ )
{
if ( m_pDepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] )
{
m_pDepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ]->DecrementReferenceCount();
m_pDepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] = NULL;
}
if ( m_pSSAODepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] )
{
m_pSSAODepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ]->DecrementReferenceCount();
m_pSSAODepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] = NULL;
}
}
if ( m_pGlintBuildMaterial )
{
m_pGlintBuildMaterial->DecrementReferenceCount();
m_pGlintBuildMaterial = NULL;
}
#endif
}
static void ReleaseMaterialSystemObjects()
{
// g_StudioRender.UncacheGlint();
}
static void RestoreMaterialSystemObjects( int nChangeFlags )
{
// g_StudioRender.PrecacheGlint();
}
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CStudioRender::Init()
{
if ( g_pMaterialSystem && g_pMaterialSystemHardwareConfig )
{
g_pMaterialSystem->AddReleaseFunc( ReleaseMaterialSystemObjects );
g_pMaterialSystem->AddRestoreFunc( RestoreMaterialSystemObjects );
InitDebugMaterials();
return INIT_OK;
}
return INIT_FAILED;
}
void CStudioRender::Shutdown( void )
{
UncacheGlint();
ShutdownDebugMaterials();
if ( g_pMaterialSystem )
{
g_pMaterialSystem->RemoveReleaseFunc( ReleaseMaterialSystemObjects );
g_pMaterialSystem->RemoveRestoreFunc( RestoreMaterialSystemObjects );
}
}
//-----------------------------------------------------------------------------
// Sets the lighting render state
//-----------------------------------------------------------------------------
void CStudioRender::SetLightingRenderState()
{
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// FIXME: What happens when we use the fixed function pipeline but vertex shaders
// are active? For the time being this only works because everything that does
// vertex lighting does, in fact, have a vertex shader which is used to render it.
pRenderContext->SetAmbientLightCube( m_pRC->m_LightBoxColors );
if ( m_pRC->m_Config.bSoftwareLighting || m_pRC->m_NumLocalLights == 0 )
{
pRenderContext->DisableAllLocalLights();
}
else
{
int nMaxLightCount = g_pMaterialSystemHardwareConfig->MaxNumLights();
LightDesc_t desc;
desc.m_Type = MATERIAL_LIGHT_DISABLE;
int i;
int nLightCount = min( m_pRC->m_NumLocalLights, nMaxLightCount );
for( i = 0; i < nLightCount; ++i )
{
pRenderContext->SetLight( i, m_pRC->m_LocalLights[i] );
}
for( ; i < nMaxLightCount; ++i )
{
pRenderContext->SetLight( i, desc );
}
}
}
//-----------------------------------------------------------------------------
// Shadow state (affects the models as they are rendered)
//-----------------------------------------------------------------------------
void CStudioRender::AddShadow( IMaterial* pMaterial, void* pProxyData, FlashlightState_t *pFlashlightState, VMatrix *pWorldToTexture, ITexture *pFlashlightDepthTexture )
{
int i = m_ShadowState.AddToTail();
ShadowState_t& state = m_ShadowState[i];
state.m_pMaterial = pMaterial;
state.m_pProxyData = pProxyData;
state.m_pFlashlightState = pFlashlightState;
state.m_pWorldToTexture = pWorldToTexture;
state.m_pFlashlightDepthTexture = pFlashlightDepthTexture;
}
void CStudioRender::ClearAllShadows()
{
m_ShadowState.RemoveAll();
}
void CStudioRender::GetFlexStats( )
{
#ifdef REPORT_FLEX_STATS
static bool s_bLastFlexStats = false;
bool bDoStats = r_flexstats.GetInt() != 0;
if ( bDoStats )
{
if ( !s_bLastFlexStats )
{
s_nModelsDrawn = 0;
s_nActiveFlexCount = 0;
}
// Count number of active weights
int nActiveFlexCount = 0;
for ( int i = 0; i < MAXSTUDIOFLEXDESC; ++i )
{
if ( fabs( m_FlexWeights[i] ) >= 0.001f || fabs( m_FlexDelayedWeights[i] ) >= 0.001f )
{
++nActiveFlexCount;
}
}
++s_nModelsDrawn;
s_nActiveFlexCount += nActiveFlexCount;
}
else
{
if ( s_bLastFlexStats )
{
if ( s_nModelsDrawn )
{
Msg( "Average number of flexes/model: %d\n", s_nActiveFlexCount / s_nModelsDrawn );
}
else
{
Msg( "No models rendered to take stats of\n" );
}
s_nModelsDrawn = 0;
s_nActiveFlexCount = 0;
}
}
s_bLastFlexStats = bDoStats;
#endif
}
//-----------------------------------------------------------------------------
// Main model rendering entry point
//-----------------------------------------------------------------------------
void CStudioRender::DrawModel( const DrawModelInfo_t& info, const StudioRenderContext_t &rc,
matrix3x4_t *pBoneToWorld, const FlexWeights_t &flex, int flags )
{
if ( ( flags & STUDIORENDER_GENERATE_STATS ) != 0 )
{
ModelStats( info, rc, pBoneToWorld, flex, flags );
return;
}
VPROF( "CStudioRender::DrawModel");
m_pRC = const_cast< StudioRenderContext_t* >( &rc );
m_pFlexWeights = flex.m_pFlexWeights;
m_pFlexDelayedWeights = flex.m_pFlexDelayedWeights;
m_pBoneToWorld = pBoneToWorld;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// Disable flex if we're told to...
bool flexConfig = m_pRC->m_Config.bFlex;
if (flags & STUDIORENDER_DRAW_NO_FLEXES)
{
m_pRC->m_Config.bFlex = false;
}
// Enable wireframe if we're told to...
bool bWireframe = m_pRC->m_Config.bWireframe;
if ( flags & STUDIORENDER_DRAW_WIREFRAME )
{
m_pRC->m_Config.bWireframe = true;
}
int boneMask = BONE_USED_BY_VERTEX_AT_LOD( info.m_Lod );
// Preserve the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
m_VertexCache.StartModel();
m_pStudioHdr = info.m_pStudioHdr;
if ( !info.m_pHardwareData->m_pLODs )
{
// If we are missing LODs then print the model name before returning
// so we can perhaps correct the underlying problem.
Msg( "Missing LODs for %s, lod index is %d.\n", m_pStudioHdr->pszName(), info.m_Lod );
return;
}
m_pStudioMeshes = info.m_pHardwareData->m_pLODs[info.m_Lod].m_pMeshData;
// Bone to world must be set before calling drawmodel; it uses that here
ComputePoseToWorld( m_PoseToWorld, m_pStudioHdr, boneMask, m_pRC->m_ViewOrigin, pBoneToWorld );
R_StudioRenderModel( pRenderContext, info.m_Skin, info.m_Body, info.m_HitboxSet, info.m_pClientEntity,
info.m_pHardwareData->m_pLODs[info.m_Lod].ppMaterials,
info.m_pHardwareData->m_pLODs[info.m_Lod].pMaterialFlags, flags, boneMask, info.m_Lod, info.m_pColorMeshes);
// Draw all the decals on this model
// If the model is not in memory, this code may not function correctly
// This code assumes the model has been rendered!
// So skip if the model hasn't been rendered
// Also, skip if we're rendering to the shadow depth map
if ( ( m_pStudioMeshes != 0 ) && !( flags & ( STUDIORENDER_SHADOWDEPTHTEXTURE | STUDIORENDER_SSAODEPTHTEXTURE )) )
{
if ((flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY)
{
DrawDecal( info, info.m_Lod, info.m_Body );
}
// Draw shadows
if ( !( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
{
DrawShadows( info, flags, boneMask );
}
if( (flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY &&
!( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
{
DrawFlashlightDecals( info, info.m_Lod );
}
}
// Restore the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
// Restore the configs
m_pRC->m_Config.bFlex = flexConfig;
m_pRC->m_Config.bWireframe = bWireframe;
#ifdef REPORT_FLEX_STATS
GetFlexStats();
#endif
pRenderContext->SetNumBoneWeights( 0 );
m_pRC = NULL;
m_pBoneToWorld = NULL;
m_pFlexWeights = NULL;
m_pFlexDelayedWeights = NULL;
}
void CStudioRender::DrawModelStaticProp( const DrawModelInfo_t& info,
const StudioRenderContext_t &rc, const matrix3x4_t& rootToWorld, int flags )
{
VPROF( "CStudioRender::DrawModelStaticProp");
m_pRC = const_cast<StudioRenderContext_t*>( &rc );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
memcpy( &m_StaticPropRootToWorld, &rootToWorld, sizeof(matrix3x4_t) );
memcpy( &m_PoseToWorld[0], &rootToWorld, sizeof(matrix3x4_t) );
m_pBoneToWorld = &m_StaticPropRootToWorld;
bool flexConfig = m_pRC->m_Config.bFlex;
m_pRC->m_Config.bFlex = false;
bool bWireframe = m_pRC->m_Config.bWireframe;
if ( flags & STUDIORENDER_DRAW_WIREFRAME )
{
m_pRC->m_Config.bWireframe = true;
}
int lod = info.m_Lod;
m_pStudioHdr = info.m_pStudioHdr;
m_pStudioMeshes = info.m_pHardwareData->m_pLODs[lod].m_pMeshData;
if ( ( flags & STUDIORENDER_GENERATE_STATS ) != 0 )
{
FlexWeights_t flex;
ModelStats( info, rc, m_pBoneToWorld, flex, flags | STUDIORENDER_DRAW_NO_FLEXES );
return;
}
R_StudioRenderModel( pRenderContext, info.m_Skin, info.m_Body, info.m_HitboxSet, info.m_pClientEntity,
info.m_pHardwareData->m_pLODs[lod].ppMaterials,
info.m_pHardwareData->m_pLODs[lod].pMaterialFlags, flags, BONE_USED_BY_ANYTHING, lod, info.m_pColorMeshes);
// If we're not shadow depth mapping
if ( ( flags & ( STUDIORENDER_SHADOWDEPTHTEXTURE | STUDIORENDER_SSAODEPTHTEXTURE ) ) == 0 )
{
// FIXME: Should this occur in a separate call?
// Draw all the decals on this model
if ((flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY)
{
DrawDecal( info, lod, info.m_Body );
}
// Draw shadows
if ( !( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
{
DrawShadows( info, flags, BONE_USED_BY_ANYTHING );
}
if( (flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY &&
!( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
{
DrawFlashlightDecals( info, lod );
}
}
// Restore the configs
m_pRC->m_Config.bFlex = flexConfig;
m_pRC->m_Config.bWireframe = bWireframe;
pRenderContext->SetNumBoneWeights( 0 );
m_pBoneToWorld = NULL;
m_pRC = NULL;
}
// UNDONE: Currently no flex supported, no per instance cubemap or other lighting state supported, no eyeballs supported
// NOTE: This is a fast path for simple models with skeletons but not many other features
void CStudioRender::DrawModelArray( const DrawModelInfo_t &drawInfo, const StudioRenderContext_t &rc, int arrayCount, model_array_instance_t *pInstanceData, int instanceStride, int flags )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d", __FUNCTION__, arrayCount );
#ifndef SWDS // no drawing on dedicated server
#if 0
FlexWeights_t flex;
memset(&flex, 0, sizeof(flex));
for ( int i = 0; i < arrayCount; i++ )
{
DrawModel( drawInfo, rc, &pInstanceData[i].modelToWorld, flex, flags );
}
return;
#endif
m_pRC = const_cast< StudioRenderContext_t* >( &rc );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
// Preserve the matrices if we're skinning
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->SetNumBoneWeights( 0 );
// get the studio mesh data for this lod
studiomeshdata_t *pMeshDataBase = drawInfo.m_pHardwareData->m_pLODs[drawInfo.m_Lod].m_pMeshData;
IMaterial **ppMaterials = drawInfo.m_pHardwareData->m_pLODs[drawInfo.m_Lod].ppMaterials;
int *pMaterialFlags = drawInfo.m_pHardwareData->m_pLODs[drawInfo.m_Lod].pMaterialFlags;
studiohdr_t *pStudioHdr = drawInfo.m_pStudioHdr;
m_bDrawTranslucentSubModels = false;
int skin = drawInfo.m_Skin;
short *pskinref = pStudioHdr->pSkinref( 0 );
if ( skin > 0 && skin < pStudioHdr->numskinfamilies )
{
pskinref += ( skin * pStudioHdr->numskinref );
}
for ( int body = 0; body < pStudioHdr->numbodyparts; ++body )
{
mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( body );
int index = drawInfo.m_Body / pbodypart->base;
index = index % pbodypart->nummodels;
mstudiomodel_t *pSubmodel = pbodypart->pModel( index );
for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
{
mstudiomesh_t *pmesh = pSubmodel->pMesh(meshIndex);
studiomeshdata_t *pMeshData = &pMeshDataBase[pmesh->meshid];
Assert( pMeshData );
if ( !pMeshData->m_NumGroup )
continue;
if ( !pMaterialFlags )
continue;
StudioModelLighting_t lighting = LIGHTING_HARDWARE;
int materialFlags = pMaterialFlags[pskinref[pmesh->material]];
IMaterial* pMaterial = R_StudioSetupSkinAndLighting( pRenderContext, pskinref[ pmesh->material ], ppMaterials, materialFlags, drawInfo.m_pClientEntity, NULL, lighting );
if ( !pMaterial )
continue;
// eyeball! can't do those in array mode yet
Assert( pmesh->materialtype != 1 );
//R_StudioDrawMesh( pRenderContext, pmesh, pMeshData, lighting, pMaterial, NULL, drawInfo.m_Lod );
// Draw all the various mesh groups...
for ( int meshGroupIndex = 0; meshGroupIndex < pMeshData->m_NumGroup; ++meshGroupIndex )
{
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[meshGroupIndex];
// Older models are merely flexed while new ones are also delta flexed
Assert(!(pGroup->m_Flags & MESHGROUP_IS_FLEXED));
Assert(!(pGroup->m_Flags & MESHGROUP_IS_DELTA_FLEXED));
IMesh *pMesh = pGroup->m_pMesh;
// Needed when we switch back and forth between hardware + software lighting
if ( IsPC() && pGroup->m_MeshNeedsRestore )
{
VertexCompressionType_t compressionType = CompressionType( pMesh->GetVertexFormat() );
switch ( compressionType )
{
case VERTEX_COMPRESSION_ON:
R_StudioRestoreMesh<VERTEX_COMPRESSION_ON>( pmesh, pGroup );
case VERTEX_COMPRESSION_NONE:
default:
R_StudioRestoreMesh<VERTEX_COMPRESSION_NONE>( pmesh, pGroup );
break;
}
pGroup->m_MeshNeedsRestore = false;
}
pMesh->SetColorMesh( NULL, 0 );
MaterialPrimitiveType_t stripType = MATERIAL_TRIANGLES;
pMesh->SetPrimitiveType(stripType);
if ( pStudioHdr->numbones > 1 )
{
byte *pData = (byte *)pInstanceData;
for ( int i = 0;i < arrayCount; i++, pData += instanceStride )
{
matrix3x4_t *pBones = &( ((model_array_instance_t *)pData)->modelToWorld );
pRenderContext->LoadMatrix( pBones[0] );
for (int j = 0; j < pGroup->m_NumStrips; ++j)
{
OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[j];
// Reset bone state if we're hardware skinning
pRenderContext->SetNumBoneWeights( pStrip->numBones );
for (int k = 0; k < pStrip->numBoneStateChanges; ++k)
{
OptimizedModel::BoneStateChangeHeader_t* pStateChange = pStrip->pBoneStateChange(k);
if ( pStateChange->newBoneID < 0 )
break;
pRenderContext->LoadBoneMatrix( pStateChange->hardwareID, pBones[pStateChange->newBoneID] );
}
MaterialPrimitiveType_t localStripType = pStrip->flags & OptimizedModel::STRIP_IS_TRISTRIP ? MATERIAL_TRIANGLE_STRIP : MATERIAL_TRIANGLES;
if ( localStripType != stripType )
{
pMesh->SetPrimitiveType( localStripType );
stripType = localStripType;
}
pMesh->Draw( pStrip->indexOffset, pStrip->numIndices );
}
}
pRenderContext->SetNumBoneWeights( 0 );
}
else
{
byte *pData = (byte *)pInstanceData;
for ( int i = 0;i < arrayCount; i++, pData += instanceStride )
{
matrix3x4_t *pBones = &( ((model_array_instance_t *)pData)->modelToWorld );
pRenderContext->LoadMatrix( pBones[0] );
for (int j = 0; j < pGroup->m_NumStrips; ++j)
{
OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[j];
MaterialPrimitiveType_t localStripType = pStrip->flags & OptimizedModel::STRIP_IS_TRISTRIP ? MATERIAL_TRIANGLE_STRIP : MATERIAL_TRIANGLES;
if ( localStripType != stripType )
{
pMesh->SetPrimitiveType( localStripType );
stripType = localStripType;
}
pMesh->Draw( pStrip->indexOffset, pStrip->numIndices );
}
}
}
}
}
}
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
#endif
}
+931
View File
@@ -0,0 +1,931 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#ifndef CSTUDIORENDER_H
#define CSTUDIORENDER_H
#ifdef _WIN32
#pragma once
#endif
#include "istudiorender.h"
#include "studio.h"
#include "materialsystem/imaterialsystem.h" // for LightDesc_t
// wouldn't have to include these if it weren't for inlines.
#include "materialsystem/imaterial.h"
#include "mathlib/mathlib.h"
#include "utllinkedlist.h"
#include "utlvector.h"
#include "tier1/utllinkedlist.h"
#include "flexrenderdata.h"
#include "mathlib/compressed_vector.h"
#include "r_studiolight.h"
#if defined( _WIN32 ) && !defined( _X360 )
#include <xmmintrin.h>
#endif
#include "tier0/dbg.h"
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class ITexture;
class CPixelWriter;
class CMeshBuilder;
class IMaterialVar;
struct mstudioeyeball_t;
struct eyeballstate_t;
struct lightpos_t;
struct dworldlight_t;
struct DecalClipState_t;
class CStudioRender;
struct StudioRenderContext_t;
struct FlexWeights_t;
namespace OptimizedModel
{
struct FileHeader_t;
struct MeshHeader_t;
struct StripGroupHeader_t;
struct Vertex_t;
struct ModelLODHeader_t;
}
//-----------------------------------------------------------------------------
// FIXME: Remove
//-----------------------------------------------------------------------------
class IStudioDataCache;
extern IStudioDataCache *g_pStudioDataCache;
//-----------------------------------------------------------------------------
// Singleton
//-----------------------------------------------------------------------------
extern CStudioRender g_StudioRender;
//-----------------------------------------------------------------------------
// Defines + structs
//-----------------------------------------------------------------------------
#define MAXLOCALLIGHTS 4
#define MAXLIGHTCOMPUTE 16
enum StudioModelLighting_t
{
LIGHTING_HARDWARE = 0,
LIGHTING_SOFTWARE,
LIGHTING_MOUTH
};
struct lightpos_t
{
Vector delta; // unit vector from vertex to light
float falloff; // light distance falloff
float dot; // light direction * delta;
lightpos_t() {}
private:
// Copy constructors are not allowed
lightpos_t( const lightpos_t& src );
};
struct eyeballstate_t
{
const mstudioeyeball_t *peyeball;
matrix3x4_t mat;
Vector org; // world center of eyeball
Vector forward;
Vector right;
Vector up;
Vector cornea; // world center of cornea
eyeballstate_t() {}
private:
// Copy constructors are not allowed
eyeballstate_t( const eyeballstate_t& src );
};
//-----------------------------------------------------------------------------
// Store decal vertex data here
//-----------------------------------------------------------------------------
#pragma pack(1)
struct DecalVertex_t
{
mstudiomesh_t *GetMesh( studiohdr_t *pHdr )
{
if ((m_Body == 0xFFFF) || (m_Model == 0xFFFF) || (m_Mesh == 0xFFFF))
return NULL;
mstudiobodyparts_t *pBody = pHdr->pBodypart( m_Body );
mstudiomodel_t *pModel = pBody->pModel( m_Model );
return pModel->pMesh( m_Mesh );
}
IMorph *GetMorph( studiohdr_t *pHdr, studiomeshdata_t *pStudioMeshes )
{
if ( (m_Body == 0xFFFF) || (m_Model == 0xFFFF) || (m_Mesh == 0xFFFF) || (m_Group == 0xFFFF) )
return NULL;
mstudiobodyparts_t *pBody = pHdr->pBodypart( m_Body );
mstudiomodel_t *pModel = pBody->pModel( m_Model );
mstudiomesh_t *pMesh = pModel->pMesh( m_Mesh );
studiomeshdata_t* pMeshData = &pStudioMeshes[pMesh->meshid];
studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[m_Group];
return pGroup->m_pMorph;
}
// NOTE: m_Group + m_GroupIndex is necessary only for decals on
// hardware morphs. If COMPACT_DECAL_VERT is for console, we
// could remove group index + group
#ifdef COMPACT_DECAL_VERT
Vector m_Position; // 12
Vector2d32 m_TexCoord; // 16
Vector48 m_Normal; // 22 (packed to m_Body)
byte m_Body; // 24
byte m_Model;
unsigned short m_MeshVertexIndex; // index into the mesh's vertex list
unsigned short m_Mesh;
unsigned short m_GroupIndex; // index into the mesh's vertex list
unsigned short m_Group;
#else
Vector m_Position;
Vector m_Normal;
Vector2D m_TexCoord;
unsigned short m_MeshVertexIndex; // index into the mesh's vertex list
unsigned short m_Body;
unsigned short m_Model;
unsigned short m_Mesh;
unsigned short m_GroupIndex; // index into the group's index list
unsigned short m_Group;
#endif
DecalVertex_t() {}
DecalVertex_t( const DecalVertex_t& src )
{
m_Position = src.m_Position;
m_Normal = src.m_Normal;
m_TexCoord = src.m_TexCoord;
m_MeshVertexIndex = src.m_MeshVertexIndex;
m_Body = src.m_Body;
m_Model = src.m_Model;
m_Mesh = src.m_Mesh;
m_GroupIndex = src.m_GroupIndex;
m_Group = src.m_Group;
}
};
#pragma pack()
//-----------------------------------------------------------------------------
// Temporary meshes
//-----------------------------------------------------------------------------
struct MeshVertexInfo_t
{
mstudiomesh_t *m_pMesh;
int m_nIndex;
};
//-----------------------------------------------------------------------------
// Vertex prefetch count for software skinning
//-----------------------------------------------------------------------------
enum
{
PREFETCH_VERT_COUNT = 4
};
//-----------------------------------------------------------------------------
// Class that actually renders stuff
//-----------------------------------------------------------------------------
class CStudioRender
{
public:
CStudioRender();
~CStudioRender();
// Init, shutdown
InitReturnVal_t Init();
void Shutdown( void );
void EnableScissor( FlashlightState_t *state );
void DisableScissor();
void DrawModel( const DrawModelInfo_t& info, const StudioRenderContext_t& rc, matrix3x4_t *pBoneToWorld, const FlexWeights_t& flex, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
void DrawModelArray( const DrawModelInfo_t &drawInfo, const StudioRenderContext_t &rc, int arrayCount, model_array_instance_t *pInstanceData, int instanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
// Static-prop related draw methods
void DrawModelStaticProp( const DrawModelInfo_t& info, const StudioRenderContext_t &rc, const matrix3x4_t &modelToWorld, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
void DrawStaticPropShadows( const DrawModelInfo_t &drawInfo, const StudioRenderContext_t &rc, const matrix3x4_t &modelToWorld, int flags );
void DrawStaticPropDecals( const DrawModelInfo_t &drawInfo, const StudioRenderContext_t &rc, const matrix3x4_t &modelToWorld );
void ModelStats( const DrawModelInfo_t& info, const StudioRenderContext_t &rc, matrix3x4_t *pBoneToWorld, const FlexWeights_t &flex, int flags );
// Create, destroy list of decals for a particular model
StudioDecalHandle_t CreateDecalList( studiohwdata_t *pHardwareData );
void DestroyDecalList( StudioDecalHandle_t handle );
// Add decals to a decal list by doing a planar projection along the ray
void AddDecal( StudioDecalHandle_t handle, const StudioRenderContext_t& rc, matrix3x4_t *pBoneToWorld, studiohdr_t *pStudioHdr,
const Ray_t & ray, const Vector& decalUp, IMaterial* pDecalMaterial,
float radius, int body, bool noPokethru, int maxLODToDecal = ADDDECAL_TO_ALL_LODS );
// Shadow state (affects the models as they are rendered)
void AddShadow( IMaterial* pMaterial, void* pProxyData, FlashlightState_t *pFlashlightState, VMatrix *pWorldToTexture, ITexture *pFlashlightDepthTexture );
void ClearAllShadows();
// Release/restore material system objects
void PrecacheGlint();
void UncacheGlint();
// Get the config
void R_MouthComputeLightingValues( float& fIllum, Vector& forward );
void R_MouthLighting( float fIllum, const Vector& normal, const Vector& forward, Vector& light );
// Performs the lighting computation
inline void R_ComputeLightAtPoint3( const Vector &pos, const Vector &norm, Vector &color );
#if defined( _WIN32 ) && !defined( _X360 )
// sse-ized lighting pipeline. lights 4 vertices at once
inline void R_ComputeLightAtPoints3( const FourVectors &pos, const FourVectors &norm, FourVectors &color );
void R_MouthLighting( __m128 fIllum, const FourVectors& normal, const FourVectors& forward, FourVectors& light );
#endif
private:
enum
{
DECAL_DYNAMIC = 0x1,
DECAL_SECONDPASS = 0x2,
};
typedef unsigned short DecalId_t;
struct Decal_t
{
int m_IndexCount;
int m_VertexCount;
float m_FadeStartTime;
float m_FadeDuration;
int m_Flags;
};
struct DecalHistory_t
{
unsigned short m_Material;
unsigned short m_Decal;
DecalId_t m_nId;
unsigned short m_nPad;
};
typedef CUtlLinkedList<DecalVertex_t, unsigned short> DecalVertexList_t;
typedef CUtlVector<unsigned short> DecalIndexList_t;
typedef CUtlLinkedList<Decal_t, unsigned short> DecalList_t;
typedef CUtlLinkedList<DecalHistory_t, unsigned short> DecalHistoryList_t;
struct DecalMaterial_t
{
IMaterial* m_pMaterial;
DecalIndexList_t m_Indices;
DecalVertexList_t m_Vertices;
DecalList_t m_Decals;
};
struct DecalLod_t
{
unsigned short m_FirstMaterial;
DecalHistoryList_t m_DecalHistory;
};
struct DecalModelList_t
{
studiohwdata_t* m_pHardwareData;
DecalLod_t* m_pLod;
int m_nLods; // need to retain because hardware data could be flushed
};
// A temporary structure used to figure out new decal verts
struct DecalBuildVertexInfo_t
{
enum
{
FRONT_FACING = 0x1,
VALID_AREA = 0x2, // If you change this, change ProjectDecalOntoMesh
};
Vector2D m_UV;
unsigned short m_VertexIndex; // index into the DecalVertex_t list
unsigned char m_UniqueID;
unsigned char m_Flags;
private:
// No copy constructors
DecalBuildVertexInfo_t( const DecalBuildVertexInfo_t &src );
};
struct DecalBuildInfo_t
{
IMaterial **m_ppMaterials;
studiohdr_t *m_pStudioHdr;
mstudiomesh_t *m_pMesh;
studiomeshdata_t *m_pMeshData;
DecalMaterial_t *m_pDecalMaterial;
MeshVertexInfo_t *m_pMeshVertices;
const mstudio_meshvertexdata_t *m_pMeshVertexData;
const thinModelVertices_t *m_pMeshThinVertexData;
int m_nGlobalMeshIndex;
DecalBuildVertexInfo_t *m_pVertexBuffer;
float m_Radius;
DecalBuildVertexInfo_t *m_pVertexInfo;
int m_Body;
int m_Model;
int m_Mesh;
int m_Group;
DecalVertexList_t::IndexType_t m_FirstVertex;
unsigned short m_VertexCount;
bool m_UseClipVert;
bool m_NoPokeThru;
};
struct ShadowState_t
{
IMaterial* m_pMaterial;
void* m_pProxyData;
FlashlightState_t * m_pFlashlightState;
VMatrix * m_pWorldToTexture;
ITexture * m_pFlashlightDepthTexture;
};
struct BodyPartInfo_t
{
int m_nSubModelIndex;
mstudiomodel_t *m_pSubModel;
};
struct GlintRenderData_t
{
Vector2D m_vecPosition;
Vector m_vecIntensity;
};
// Global LRU for model decals
struct DecalLRU_t
{
StudioDecalHandle_t m_hDecalHandle;
DecalId_t m_nDecalId;
};
typedef CUtlFixedLinkedList< DecalLRU_t >::IndexType_t DecalLRUListIndex_t;
private:
void SetLightingRenderState();
int R_StudioRenderModel( IMatRenderContext *pRenderContext, int skin, int body, int hitboxset, void /*IClientEntity*/ *pEntity,
IMaterial **ppMaterials, int *pMaterialFlags, int flags, int boneMask, int lod, ColorMeshInfo_t *pColorMeshes = NULL );
IMaterial* R_StudioSetupSkinAndLighting( IMatRenderContext *pRenderContext, int index, IMaterial **ppMaterials, int materialFlags,
void /*IClientEntity*/ *pClientEntity, ColorMeshInfo_t *pColorMeshes, StudioModelLighting_t &lighting );
int R_StudioDrawEyeball( IMatRenderContext *pRenderContext, mstudiomesh_t* pmesh, studiomeshdata_t* pMeshData,
StudioModelLighting_t lighting, IMaterial *pMaterial, int lod );
int R_StudioDrawPoints( IMatRenderContext *pRenderContext, int skin, void /*IClientEntity*/ *pClientEntity,
IMaterial **ppMaterials, int *pMaterialFlags, int boneMask, int lod, ColorMeshInfo_t *pColorMeshes );
int R_StudioDrawMesh( IMatRenderContext *pRenderContext, mstudiomesh_t* pmesh, studiomeshdata_t* pMeshData,
StudioModelLighting_t lighting, IMaterial *pMaterial, ColorMeshInfo_t *pColorMeshes, int lod );
int R_StudioRenderFinal( IMatRenderContext *pRenderContext,
int skin, int nBodyPartCount, BodyPartInfo_t *pBodyPartInfo, void /*IClientEntity*/ *pClientEntity,
IMaterial **ppMaterials, int *pMaterialFlags, int boneMask, int lod, ColorMeshInfo_t *pColorMeshes = NULL );
int R_StudioDrawStaticMesh( IMatRenderContext *pRenderContext, mstudiomesh_t* pmesh,
studiomeshgroup_t* pGroup, StudioModelLighting_t lighting, float r_blend, IMaterial* pMaterial,
int lod, ColorMeshInfo_t *pColorMeshes );
int R_StudioDrawDynamicMesh( IMatRenderContext *pRenderContext, mstudiomesh_t* pmesh,
studiomeshgroup_t* pGroup, StudioModelLighting_t lighting,
float r_blend, IMaterial* pMaterial, int lod );
int R_StudioDrawGroupHWSkin( IMatRenderContext *pRenderContext, studiomeshgroup_t* pGroup, IMesh* pMesh, ColorMeshInfo_t *pColorMeshInfo = NULL );
int R_StudioDrawGroupSWSkin( studiomeshgroup_t* pGroup, IMesh* pMesh );
void R_StudioDrawHulls( int hitboxset, bool translucent );
void R_StudioDrawBones (void);
void R_StudioVertBuffer( void );
void DrawNormal( const Vector& pos, float scale, const Vector& normal, const Vector& color );
void BoneMatToMaterialMat( matrix3x4_t& boneMat, float materialMat[4][4] );
// Various inner-loop methods
void R_StudioSoftwareProcessMesh( mstudiomesh_t* pmesh, CMeshBuilder& meshBuilder,
int numVertices, unsigned short* pGroupToMesh, StudioModelLighting_t lighting, bool doFlex, float r_blend,
bool bNeedsTangentSpace, bool bDX8Vertex, IMaterial *pMaterial );
void R_StudioSoftwareProcessMesh_Normals( mstudiomesh_t* pmesh, CMeshBuilder& meshBuilder,
int numVertices, unsigned short* pGroupToMesh, StudioModelLighting_t lighting, bool doFlex, float r_blend,
bool bShowNormals, bool bShowTangentFrame );
template< class T >
void ComputeFlexedVertex_StreamOffset( mstudioflex_t *pflex, T *pvanim, int vertCount, float w1, float w2, float w3, float w4 );
void R_StudioProcessFlexedMesh_StreamOffset( mstudiomesh_t* pmesh, int lod );
template <VertexCompressionType_t T> void FillFlexMeshGroupVB( CMeshBuilder & meshBuilder, studiomeshgroup_t *pGroup );
void R_StudioFlexMeshGroup( studiomeshgroup_t *pGroup );
template<VertexCompressionType_t T> void R_StudioRestoreMesh( mstudiomesh_t* pmesh, studiomeshgroup_t* pMeshData );
void R_StudioProcessFlexedMesh( mstudiomesh_t* pmesh, CMeshBuilder& meshBuilder,
int numVertices, unsigned short* pGroupToMesh );
// Eye rendering using vertex shaders
void SetEyeMaterialVars( IMaterial* pMaterial, mstudioeyeball_t* peyeball,
const Vector& eyeOrigin, const matrix3x4_t& irisTransform, const matrix3x4_t& glintTransform );
void ComputeEyelidStateFACS( mstudiomodel_t *pSubModel );
void R_StudioEyelidFACS( const mstudioeyeball_t *peyeball, const eyeballstate_t *pstate );
void R_StudioEyeballPosition( const mstudioeyeball_t *peyeball, eyeballstate_t *pstate );
// Computes the texture projection matrix for the glint texture
void ComputeGlintTextureProjection( eyeballstate_t const* pState,
const Vector& vright, const Vector& vup, matrix3x4_t& mat );
void R_StudioEyeballGlint( const eyeballstate_t *pstate, IMaterialVar *pGlintTextureVar,
const Vector& vright, const Vector& vup, const Vector& r_origin );
ITexture* RenderGlintTexture( const eyeballstate_t *pstate,
const Vector& vright, const Vector& vup, const Vector& r_origin );
int BuildGlintRenderData( GlintRenderData_t *pData, int nMaxGlints,
const eyeballstate_t *pstate, const Vector& vright, const Vector& vup, const Vector& r_origin );
void R_MouthSetupVertexShader( IMaterial* pMaterial );
// Computes a vertex format to use
VertexFormat_t ComputeSWSkinVertexFormat( IMaterial *pMaterial ) const;
inline bool R_TeethAreVisible( void )
{
return true;
/*
// FIXME: commented out until Gary can change them to just draw black
mstudiomouth_t *pMouth = m_pStudioHdr->pMouth( 0 );
float fIllum = m_FlexWeights[pMouth->flexdesc];
return fIllum > 0.0f;
*/
}
inline StudioModelLighting_t R_StudioComputeLighting( IMaterial *pMaterial, int materialFlags, ColorMeshInfo_t *pColorMeshes );
inline void R_StudioTransform( Vector& in1, mstudioboneweight_t *pboneweight, Vector& out1 );
inline void R_StudioRotate( Vector& in1, mstudioboneweight_t *pboneweight, Vector& out1 );
inline void R_StudioRotate( Vector4D& in1, mstudioboneweight_t *pboneweight, Vector4D& out1 );
inline void R_StudioEyeballNormal( mstudioeyeball_t const* peyeball, Vector& org,
Vector& pos, Vector& normal );
void MaterialPlanerProjection( const matrix3x4_t& mat, int count, const Vector *psrcverts, Vector2D *pdesttexcoords );
void AddGlint( CPixelWriter &pixelWriter, float x, float y, const Vector& color );
// Methods associated with lighting
int R_LightGlintPosition( int index, const Vector& org, Vector& delta, Vector& intensity );
void R_LightEffectsWorld( const lightpos_t *light, const Vector& normal, const Vector &src, Vector &dest );
void R_GatherStats( studiomeshgroup_t *pGroup, CMeshBuilder &MeshBuilder, IMesh *pMesh, IMaterial *pMaterial );
public:
// NJS: Messy, but needed for an externally optimized routine to set up the lighting.
void R_InitLightEffectsWorld3();
void (FASTCALL *R_LightEffectsWorld3)( const LightDesc_t *pLightDesc, const lightpos_t *light, const Vector& normal, Vector &dest );
private:
inline float R_WorldLightAngle( const LightDesc_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta );
void InitDebugMaterials( void );
void ShutdownDebugMaterials( void );
int SortMeshes( int* pIndices, IMaterial **ppMaterials, short* pskinref, const Vector& vforward, const Vector& r_origin );
// Computes pose to decal space transforms for decal creation
// returns false if it can't for some reason.
bool ComputePoseToDecal( Ray_t const& ray, const Vector& up );
bool AddDecalToModel( DecalBuildInfo_t& buildInfo );
// Helper methods for decal projection, projects pose space vertex data
bool TransformToDecalSpace( DecalBuildInfo_t& build, const Vector& pos, mstudioboneweight_t *pboneweight, Vector2D& uv );
bool ProjectDecalOntoMesh( DecalBuildInfo_t& build, DecalBuildVertexInfo_t* pVertexInfo, mstudiomesh_t *pMesh );
bool IsFrontFacing( const Vector * norm, const mstudioboneweight_t *pboneweight );
int ComputeClipFlags( DecalBuildVertexInfo_t* pVertexInfo, int i );
void ConvertMeshVertexToDecalVertex( DecalBuildInfo_t& build, int meshIndex, DecalVertex_t& decalVertex, int nGroupIndex = 0xFFFF );
unsigned short AddVertexToDecal( DecalBuildInfo_t& build, int meshIndex, int nGroupIndex = 0xFFFF );
unsigned short AddVertexToDecal( DecalBuildInfo_t& build, DecalVertex_t& vert );
void AddClippedDecalToTriangle( DecalBuildInfo_t& build, DecalClipState_t& clipState );
bool ClipDecal( DecalBuildInfo_t& build, int i1, int i2, int i3, int *pClipFlags );
void AddTriangleToDecal( DecalBuildInfo_t& build, int i1, int i2, int i3, int gi1, int gi2, int gi3 );
void AddDecalToMesh( DecalBuildInfo_t& build );
int GetDecalMaterial( DecalLod_t& decalLod, IMaterial* pDecalMaterial );
int AddDecalToMaterialList( DecalMaterial_t* pMaterial );
// Total number of meshes we have to deal with
int ComputeTotalMeshCount( int iRootLOD, int iMaxLOD, int body ) const;
// Project decals onto all meshes
void ProjectDecalsOntoMeshes( DecalBuildInfo_t& build, int nMeshCount );
// Set up the locations for vertices to use
int ComputeVertexAllocation( int iMaxLOD, int body, studiohwdata_t *pHardwareData, MeshVertexInfo_t *pVertexInfo );
// Removes a decal and associated vertices + indices from the history list
void RetireDecal( DecalModelList_t &list, DecalId_t nDecalID, int iLOD, int iMaxLOD );
// Helper methods related to drawing decals
void DrawSingleBoneDecals( CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial );
bool DrawMultiBoneDecals( CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr );
void DrawSingleBoneFlexedDecals( IMatRenderContext *pRenderContext, CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial );
bool DrawMultiBoneFlexedDecals( IMatRenderContext *pRenderContext, CMeshBuilder& meshBuilder, DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, studioloddata_t *pStudioLOD );
void DrawDecalMaterial( IMatRenderContext *pRenderContext, DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, studioloddata_t *pStudioLOD );
void DrawDecal( const DrawModelInfo_t &drawInfo, int lod, int body );
bool PreDrawDecal( IMatRenderContext *pRenderContext, const DrawModelInfo_t &drawInfo );
// Draw shadows
void DrawShadows( const DrawModelInfo_t& info, int flags, int boneMask );
// Draw flashlight lighting on decals.
void DrawFlashlightDecals( const DrawModelInfo_t& info, int lod );
// Helper methods related to extracting and balancing
float RampFlexWeight( mstudioflex_t &flex, float w );
// Remove decal from LRU
void RemoveDecalListFromLRU( StudioDecalHandle_t h );
// Helper methods related to flexing vertices
void R_StudioFlexVerts( mstudiomesh_t *pmesh, int lod );
// Flex stats
void GetFlexStats( );
// Sets up the hw flex mesh
void ComputeFlexWeights( int nFlexCount, mstudioflex_t *pFlex, MorphWeight_t *pWeights );
// Generate morph accumulator
void GenerateMorphAccumulator( mstudiomodel_t *pSubModel );
// Computes eyeball state
void ComputeEyeballState( mstudiomodel_t *pSubModel );
// Avoid some warnings...
CStudioRender( CStudioRender const& );
public:
// Render context (comes from queue)
StudioRenderContext_t *m_pRC;
private:
// Stores all decals for a particular material and lod
CUtlLinkedList< DecalMaterial_t, unsigned short, true > m_DecalMaterial;
// Stores all decal lists that have been made
CUtlFixedLinkedList< DecalModelList_t > m_DecalList;
CThreadFastMutex m_DecalMutex;
// Stores all shadows to be cast on the current object
CUtlVector<ShadowState_t> m_ShadowState;
matrix3x4_t m_StaticPropRootToWorld;
matrix3x4_t *m_pBoneToWorld; // bone transformation matrix( comes from queue )
matrix3x4_t *m_PoseToWorld; // bone transformation matrix
matrix3x4_t *m_PoseToDecal; // bone transformation matrix
// Flex state, comes from queue
float *m_pFlexWeights;
float *m_pFlexDelayedWeights;
studiohdr_t *m_pStudioHdr;
mstudiomodel_t *m_pSubModel;
studiomeshdata_t *m_pStudioMeshes;
eyeballstate_t m_pEyeballState[16]; // MAXSTUDIOEYEBALLS
// debug materials
IMaterial *m_pMaterialMRMWireframe;
IMaterial *m_pMaterialMRMWireframeZBuffer;
IMaterial *m_pMaterialMRMNormals;
IMaterial *m_pMaterialTangentFrame;
IMaterial *m_pMaterialTranslucentModelHulls;
IMaterial *m_pMaterialSolidModelHulls;
IMaterial *m_pMaterialAdditiveVertexColorVertexAlpha;
IMaterial *m_pMaterialModelBones;
IMaterial *m_pMaterialWorldWireframe;
IMaterial *m_pMaterialModelEnvCubemap;
// Depth override material
IMaterial *m_pDepthWrite[2][2];
IMaterial *m_pSSAODepthWrite[2][2];
// GLINT data
ITexture* m_pGlintTexture;
ITexture* m_pGlintLODTexture;
IMaterial *m_pGlintBuildMaterial;
short m_GlintWidth;
short m_GlintHeight;
// Flex data
CCachedRenderData m_VertexCache;
// Cached variables:
bool m_bSkippedMeshes : 1;
bool m_bDrawTranslucentSubModels : 1;
DecalId_t m_nDecalId;
CUtlFixedLinkedList< DecalLRU_t > m_DecalLRU;
friend class CGlintTextureRegenerator;
friend struct mstudiomodel_t;
friend class CStudioRenderContext;
};
//-----------------------------------------------------------------------------
// Converts matrices to a format material system wants
//-----------------------------------------------------------------------------
/*
================
R_StudioTransform
================
*/
inline void CStudioRender::R_StudioTransform( Vector& in1, mstudioboneweight_t *pboneweight, Vector& out1 )
{
// MEASURECODE( "R_StudioTransform" );
Vector out2;
switch( pboneweight->numbones )
{
case 1:
VectorTransform( in1, m_PoseToWorld[(unsigned)pboneweight->bone[0]], out1 );
break;
/*
case 2:
VectorTransform( in1, m_PoseToWorld[pboneweight->bone[0]], out1 );
out1 *= pboneweight->weight[0];
VectorTransform( in1, m_PoseToWorld[pboneweight->bone[1]], out2 );
VectorMA( out1, pboneweight->weight[1], out2, out1 );
break;
case 3:
VectorTransform( in1, m_PoseToWorld[pboneweight->bone[0]], out1 );
out1 *= pboneweight->weight[0];
VectorTransform( in1, m_PoseToWorld[pboneweight->bone[1]], out2 );
VectorMA( out1, pboneweight->weight[1], out2, out1 );
VectorTransform( in1, m_PoseToWorld[pboneweight->bone[2]], out2 );
VectorMA( out1, pboneweight->weight[2], out2, out1 );
break;
*/
default:
VectorFill( out1, 0 );
for (int i = 0; i < pboneweight->numbones; i++)
{
VectorTransform( in1, m_PoseToWorld[(unsigned)pboneweight->bone[i]], out2 );
VectorMA( out1, pboneweight->weight[i], out2, out1 );
}
break;
}
}
/*
================
R_StudioRotate
================
*/
inline void CStudioRender::R_StudioRotate( Vector& in1, mstudioboneweight_t *pboneweight, Vector& out1 )
{
// NOTE: This only works to rotate normals if there's no scale in the
// pose to world transforms. If we ever add scale, we'll need to
// multiply by the inverse transpose of the pose to world
if (pboneweight->numbones == 1)
{
VectorRotate( in1, m_PoseToWorld[(unsigned)pboneweight->bone[0]], out1 );
}
else
{
Vector out2;
VectorFill( out1, 0 );
for (int i = 0; i < pboneweight->numbones; i++)
{
VectorRotate( in1, m_PoseToWorld[(unsigned)pboneweight->bone[i]], out2 );
VectorMA( out1, pboneweight->weight[i], out2, out1 );
}
VectorNormalize( out1 );
}
}
inline void CStudioRender::R_StudioRotate( Vector4D& realIn1, mstudioboneweight_t *pboneweight, Vector4D& realOut1 )
{
// garymcthack - god this sucks.
Vector in1( realIn1[0], realIn1[1], realIn1[2] );
Vector out1;
if (pboneweight->numbones == 1)
{
VectorRotate( in1, m_PoseToWorld[(unsigned)pboneweight->bone[0]], out1 );
}
else
{
Vector out2;
VectorFill( out1, 0 );
for (int i = 0; i < pboneweight->numbones; i++)
{
VectorRotate( in1, m_PoseToWorld[(unsigned)pboneweight->bone[i]], out2 );
VectorMA( out1, pboneweight->weight[i], out2, out1 );
}
VectorNormalize( out1 );
}
realOut1.Init( out1[0], out1[1], out1[2], realIn1[3] );
}
//-----------------------------------------------------------------------------
// Compute the contribution of a light depending on it's angle
//-----------------------------------------------------------------------------
/*
light_normal (lights normal translated to same space as other normals)
surface_normal
light_direction_normal | (light_pos - vertex_pos) |
*/
template< int nLightType >
class CWorldLightAngleWrapper
{
public:
FORCEINLINE static float WorldLightAngle( const LightDesc_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
{
float dot, dot2, ratio;
switch (nLightType)
{
case MATERIAL_LIGHT_POINT:
#if 1
// half-lambert
dot = DotProduct( snormal, delta );
if (dot < 0.f)
return 0.f;
#else
dot = DotProduct( snormal, delta ) * 0.5 + 0.5;
dot = dot * dot;
#endif
return dot;
case MATERIAL_LIGHT_SPOT:
#if 1
// half-lambert
dot = DotProduct( snormal, delta );
if (dot < 0.)
return 0.f;
#else
dot = DotProduct( snormal, delta ) * 0.5 + 0.5;
dot = dot * dot;
#endif
dot2 = -DotProduct (delta, lnormal);
if (dot2 <= wl->m_PhiDot)
return 0.f; // outside light cone
ratio = dot;
if (dot2 >= wl->m_ThetaDot)
return ratio; // inside inner cone
if ((wl->m_Falloff == 1.f) || (wl->m_Falloff == 0.f))
{
ratio *= (dot2 - wl->m_PhiDot) / (wl->m_ThetaDot - wl->m_PhiDot);
}
else
{
ratio *= pow((dot2 - wl->m_PhiDot) / (wl->m_ThetaDot - wl->m_PhiDot), wl->m_Falloff );
}
return ratio;
case MATERIAL_LIGHT_DIRECTIONAL:
#if 1
// half-lambert
dot2 = -DotProduct( snormal, lnormal );
if (dot2 < 0.f)
return 0.f;
#else
dot2 = -DotProduct( snormal, lnormal ) * 0.5 + 0.5;
dot2 = dot2 * dot2;
#endif
return dot2;
case MATERIAL_LIGHT_DISABLE:
return 0.f;
NO_DEFAULT;
}
}
};
template< int nLightType >
class CWorldLightAngleWrapperConstDirectional
{
public:
FORCEINLINE static float WorldLightAngle( const LightDesc_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta, float directionalamount )
{
float dot, dot2, ratio;
// directional amount is constant
dot = directionalamount;
if (dot < 0.f)
return 0.f;
switch (nLightType)
{
case MATERIAL_LIGHT_POINT:
case MATERIAL_LIGHT_DIRECTIONAL:
return dot;
case MATERIAL_LIGHT_SPOT:
dot2 = -DotProduct (delta, lnormal);
if (dot2 <= wl->m_PhiDot)
return 0.f; // outside light cone
ratio = dot;
if (dot2 >= wl->m_ThetaDot)
return ratio; // inside inner cone
if ((wl->m_Falloff == 1.f) || (wl->m_Falloff == 0.f))
{
ratio *= (dot2 - wl->m_PhiDot) / (wl->m_ThetaDot - wl->m_PhiDot);
}
else
{
ratio *= pow((dot2 - wl->m_PhiDot) / (wl->m_ThetaDot - wl->m_PhiDot), wl->m_Falloff );
}
return ratio;
case MATERIAL_LIGHT_DISABLE:
return 0.f;
NO_DEFAULT;
}
}
};
inline float CStudioRender::R_WorldLightAngle( const LightDesc_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
{
switch (wl->m_Type)
{
case MATERIAL_LIGHT_DISABLE: return CWorldLightAngleWrapper<MATERIAL_LIGHT_DISABLE>::WorldLightAngle( wl, lnormal, snormal, delta );
case MATERIAL_LIGHT_POINT: return CWorldLightAngleWrapper<MATERIAL_LIGHT_POINT>::WorldLightAngle( wl, lnormal, snormal, delta );
case MATERIAL_LIGHT_DIRECTIONAL: return CWorldLightAngleWrapper<MATERIAL_LIGHT_DIRECTIONAL>::WorldLightAngle( wl, lnormal, snormal, delta );
case MATERIAL_LIGHT_SPOT: return CWorldLightAngleWrapper<MATERIAL_LIGHT_SPOT>::WorldLightAngle( wl, lnormal, snormal, delta );
NO_DEFAULT;
}
}
//-----------------------------------------------------------------------------
// Draws eyeballs
//-----------------------------------------------------------------------------
inline void CStudioRender::R_StudioEyeballNormal( mstudioeyeball_t const* peyeball, Vector& org,
Vector& pos, Vector& normal )
{
// inside of a flattened torus
VectorSubtract( pos, org, normal );
float flUpAmount = DotProduct( normal, peyeball->up );
VectorMA( normal, -0.5 * flUpAmount, peyeball->up, normal );
VectorNormalize( normal );
}
//-----------------------------------------------------------------------------
//
// Stateless utility methods
//
//-----------------------------------------------------------------------------
// Computes the submodel for a specified body + bodypart
int R_StudioSetupModel( int nBodyPart, int nBody, mstudiomodel_t **pSubModel, const studiohdr_t *pStudioHdr );
// Computes PoseToWorld from BoneToWorld
void ComputePoseToWorld( matrix3x4_t *pPoseToWorld, studiohdr_t *pStudioHdr, int boneMask, const Vector& vecViewOrigin, const matrix3x4_t *pBoneToWorld );
// Computes the model LOD
inline int ComputeModelLODAndMetric( studiohwdata_t *pHardwareData, float flUnitSphereSize, float *pMetric )
{
// NOTE: This function was split off since CStudioRender needs it also.
float flMetric = pHardwareData->LODMetric( flUnitSphereSize );
if ( pMetric )
{
*pMetric = flMetric;
}
return pHardwareData->GetLODForMetric( flMetric );
}
#endif // CSTUDIORENDER_H
+114
View File
@@ -0,0 +1,114 @@
//-----------------------------------------------------------------------------
// STUDIORENDER.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$macro SRCDIR ".."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$Configuration
{
$Linker
{
$SystemLibraries "iconv" [$OSXALL]
}
$Compiler
{
$PreprocessorDefinitions "$BASE;STUDIORENDER_EXPORTS;PROTECTED_THINGS_ENABLE"
$PreprocessorDefinitions "$BASE;fopen=dont_use_fopen" [$WIN32]
}
}
$Project "StudioRender"
{
$Folder "Source Files"
{
$File "studiorender.cpp"
$File "studiorendercontext.cpp"
$File "flexrenderdata.cpp"
$File "r_studio.cpp"
$File "r_studiodecal.cpp"
$File "r_studiodraw.cpp"
$File "r_studiodraw_computeflexedvertex.cpp"
$File "r_studioflex.cpp"
$File "r_studiogettriangles.cpp"
$File "r_studiolight.cpp"
$File "r_studiostats.cpp"
}
$Folder "Header Files"
{
$File "r_studiolight.h"
$File "studiorender.h"
$File "studiorendercontext.h"
$File "flexrenderdata.h"
}
$Folder "Public Header Files"
{
$File "$SRCDIR\public\mathlib\amd3dx.h"
$File "$SRCDIR\public\basehandle.h"
$File "$SRCDIR\public\tier0\basetypes.h"
$File "$SRCDIR\public\bspflags.h"
$File "$SRCDIR\public\clientstats.h"
$File "$SRCDIR\public\cmodel.h"
$File "$SRCDIR\public\tier0\commonmacros.h"
$File "$SRCDIR\public\mathlib\compressed_vector.h"
$File "$SRCDIR\public\const.h"
$File "$SRCDIR\public\tier1\convar.h"
$File "$SRCDIR\public\tier0\dbg.h"
$File "$SRCDIR\public\tier0\fasttimer.h"
$File "$SRCDIR\public\gametrace.h"
$File "$SRCDIR\public\appframework\IAppSystem.h"
$File "$SRCDIR\public\tier0\icommandline.h"
$File "$SRCDIR\public\ihandleentity.h"
$File "$SRCDIR\public\materialsystem\imaterial.h"
$File "$SRCDIR\public\materialsystem\imaterialsystem.h"
$File "$SRCDIR\public\materialsystem\imaterialsystemhardwareconfig.h"
$File "$SRCDIR\public\materialsystem\imaterialvar.h"
$File "$SRCDIR\public\materialsystem\imesh.h"
$File "$SRCDIR\public\tier1\interface.h"
$File "$SRCDIR\public\istudiorender.h"
$File "$SRCDIR\public\materialsystem\itexture.h"
$File "$SRCDIR\public\mathlib\mathlib.h"
$File "$SRCDIR\public\measure_section.h"
$File "$SRCDIR\public\tier0\mem.h"
$File "$SRCDIR\public\tier0\memalloc.h"
$File "$SRCDIR\public\tier0\memdbgoff.h"
$File "$SRCDIR\public\tier0\memdbgon.h"
$File "$SRCDIR\public\model_types.h"
$File "$SRCDIR\public\optimize.h"
$File "$SRCDIR\public\pixelwriter.h"
$File "$SRCDIR\public\tier0\platform.h"
$File "$SRCDIR\public\tier0\protected_things.h"
$File "$SRCDIR\public\string_t.h"
$File "$SRCDIR\public\tier1\strtools.h"
$File "$SRCDIR\public\studio.h"
$File "$SRCDIR\public\tier1\utlbuffer.h"
$File "$SRCDIR\public\tier1\utllinkedlist.h"
$File "$SRCDIR\public\tier1\utlmemory.h"
$File "$SRCDIR\public\tier1\utlvector.h"
$File "$SRCDIR\public\vcollide.h"
$File "$SRCDIR\public\mathlib\vector.h"
$File "$SRCDIR\public\mathlib\vector2d.h"
$File "$SRCDIR\public\mathlib\vector4d.h"
$File "$SRCDIR\public\mathlib\vmatrix.h"
$File "$SRCDIR\public\mathlib\vplane.h"
$File "$SRCDIR\public\tier0\vprof.h"
$File "$SRCDIR\public\vstdlib\vstdlib.h"
$File "$SRCDIR\public\vtf\vtf.h"
$File "$SRCDIR\public\tier1\UtlStringMap.h"
}
$folder "Link Libraries"
{
$Lib bitmap
$Lib mathlib
$Lib tier2
$Lib tier3
}
}
File diff suppressed because it is too large Load Diff
+246
View File
@@ -0,0 +1,246 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#ifndef STUDIORENDERCONTEXT_H
#define STUDIORENDERCONTEXT_H
#ifdef _WIN32
#pragma once
#endif
#include "istudiorender.h"
#include "tier3/tier3.h"
#include "studio.h"
#include "tier1/delegates.h"
#include "tier1/memstack.h"
#include "studiorender.h"
//-----------------------------------------------------------------------------
// Foward declarations
//-----------------------------------------------------------------------------
class IStudioDataCache;
class CStudioRender;
//-----------------------------------------------------------------------------
// Global interfaces
//-----------------------------------------------------------------------------
extern IStudioDataCache *g_pStudioDataCache;
extern CStudioRender *g_pStudioRenderImp;
IMaterial* GetModelSpecificDecalMaterial( IMaterial* pDecalMaterial );
//-----------------------------------------------------------------------------
// Internal config structure
//-----------------------------------------------------------------------------
struct StudioRenderConfigInternal_t : public StudioRenderConfig_t
{
bool m_bSupportsVertexAndPixelShaders : 1;
bool m_bSupportsOverbright : 1;
bool m_bEnableHWMorph : 1;
bool m_bStatsMode : 1;
};
//-----------------------------------------------------------------------------
// All the data needed to render a studiomodel
//-----------------------------------------------------------------------------
struct FlexWeights_t
{
float *m_pFlexWeights;
float *m_pFlexDelayedWeights;
};
struct StudioRenderContext_t
{
StudioRenderConfigInternal_t m_Config;
Vector m_ViewTarget;
Vector m_ViewOrigin;
Vector m_ViewRight;
Vector m_ViewUp;
Vector m_ViewPlaneNormal;
Vector4D m_LightBoxColors[6];
LightDesc_t m_LocalLights[MAXLOCALLIGHTS];
int m_NumLocalLights;
float m_ColorMod[3];
float m_AlphaMod;
IMaterial* m_pForcedMaterial;
OverrideType_t m_nForcedMaterialType;
};
//-----------------------------------------------------------------------------
// Helper to queue up calls if necessary
//-----------------------------------------------------------------------------
#define QUEUE_STUDIORENDER_CALL( FuncName, ClassName, pObject, ... ) \
CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); \
ICallQueue *pCallQueue = pRenderContext->GetCallQueue(); \
if ( !pCallQueue || studio_queue_mode.GetInt() == 0 ) \
{ \
pObject->FuncName( __VA_ARGS__ ); \
} \
else \
{ \
pCallQueue->QueueCall( pObject, &ClassName::FuncName, ##__VA_ARGS__ ); \
}
#define QUEUE_STUDIORENDER_CALL_RC( FuncName, ClassName, pObject, pRenderContext, ... ) \
ICallQueue *pCallQueue = pRenderContext->GetCallQueue(); \
if ( !pCallQueue || studio_queue_mode.GetInt() == 0 ) \
{ \
pObject->FuncName( __VA_ARGS__ ); \
} \
else \
{ \
pCallQueue->QueueCall( pObject, &ClassName::FuncName, ##__VA_ARGS__ ); \
}
//-----------------------------------------------------------------------------
// Implementation of IStudioRender
//-----------------------------------------------------------------------------
class CStudioRenderContext : public CTier3AppSystem< IStudioRender >
{
typedef CTier3AppSystem< IStudioRender > BaseClass;
// Methods of IAppSystem
public:
virtual bool Connect( CreateInterfaceFn factory );
virtual void Disconnect();
virtual void *QueryInterface( const char *pInterfaceName );
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Methods of IStudioRender
public:
virtual void BeginFrame( void );
virtual void EndFrame( void );
virtual void Mat_Stub( IMaterialSystem *pMatSys );
virtual void UpdateConfig( const StudioRenderConfig_t& config );
virtual void GetCurrentConfig( StudioRenderConfig_t& config );
virtual bool LoadModel(studiohdr_t *pStudioHdr, void *pVtxData, studiohwdata_t *pHardwareData);
virtual void UnloadModel( studiohwdata_t *pHardwareData );
virtual void RefreshStudioHdr( studiohdr_t* pStudioHdr, studiohwdata_t* pHardwareData );
virtual void SetEyeViewTarget( const studiohdr_t *pStudioHdr, int nBodyIndex, const Vector& worldPosition );
virtual void SetAmbientLightColors( const Vector *pAmbientOnlyColors );
virtual void SetAmbientLightColors( const Vector4D *pAmbientOnlyColors );
virtual void SetLocalLights( int numLights, const LightDesc_t *pLights );
virtual int GetNumAmbientLightSamples();
virtual const Vector *GetAmbientLightDirections();
virtual void SetViewState( const Vector& viewOrigin, const Vector& viewRight, const Vector& viewUp, const Vector& viewPlaneNormal );
virtual int GetNumLODs( const studiohwdata_t &hardwareData ) const;
virtual float GetLODSwitchValue( const studiohwdata_t &hardwareData, int lod ) const;
virtual void SetLODSwitchValue( studiohwdata_t &hardwareData, int lod, float switchValue );
virtual void SetColorModulation( const float* pColor );
virtual void SetAlphaModulation( float alpha );
virtual void DrawModel( DrawModelResults_t *pResults, const DrawModelInfo_t& info, matrix3x4_t *pCustomBoneToWorld, float *pFlexWeights, float *pFlexDelayedWeights, const Vector& origin, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
virtual void DrawModelArray( const DrawModelInfo_t &drawInfo, int arrayCount, model_array_instance_t *pInstanceData, int instanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
virtual void DrawModelStaticProp( const DrawModelInfo_t& info, const matrix3x4_t &modelToWorld, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
virtual void DrawStaticPropDecals( const DrawModelInfo_t &drawInfo, const matrix3x4_t &modelToWorld );
virtual void DrawStaticPropShadows( const DrawModelInfo_t &drawInfo, const matrix3x4_t &modelToWorld, int flags );
virtual void ForcedMaterialOverride( IMaterial *newMaterial, OverrideType_t nOverrideType = OVERRIDE_NORMAL );
DELEGATE_TO_OBJECT_1( StudioDecalHandle_t, CreateDecalList, studiohwdata_t *, g_pStudioRenderImp );
virtual void DestroyDecalList( StudioDecalHandle_t handle );
virtual void AddDecal( StudioDecalHandle_t handle, studiohdr_t *pStudioHdr, matrix3x4_t *pBoneToWorld, const Ray_t & ray, const Vector& decalUp, IMaterial* pDecalMaterial, float radius, int body, bool noPokethru, int maxLODToDecal = ADDDECAL_TO_ALL_LODS );
virtual void ComputeLighting( const Vector* pAmbient, int lightCount, LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting );
virtual void ComputeLightingConstDirectional( const Vector* pAmbient, int lightCount, LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting, float flDirectionalAmount );
virtual void AddShadow( IMaterial* pMaterial, void* pProxyData, FlashlightState_t *pFlashlightState, VMatrix *pWorldToTexture, ITexture *pFlashlightDepthTexture );
virtual void ClearAllShadows();
virtual int ComputeModelLod( studiohwdata_t* pHardwareData, float flUnitSphereSize, float *pMetric = NULL );
virtual void GetPerfStats( DrawModelResults_t *pResults, const DrawModelInfo_t &info, CUtlBuffer *pSpewBuf = NULL ) const;
virtual void GetTriangles( const DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, GetTriangles_Output_t &out );
virtual int GetMaterialList( studiohdr_t *pStudioHdr, int count, IMaterial** ppMaterials );
virtual int GetMaterialListFromBodyAndSkin( MDLHandle_t studio, int nSkin, int nBody, int nCountOutputMaterials, IMaterial** ppOutputMaterials );
virtual matrix3x4_t* LockBoneMatrices( int nCount );
virtual void UnlockBoneMatrices();
virtual void LockFlexWeights( int nWeightCount, float **ppFlexWeights, float **ppFlexDelayedWeights = NULL );
virtual void UnlockFlexWeights();
virtual void GetMaterialOverride( IMaterial** ppOutForcedMaterial, OverrideType_t* pOutOverrideType );
// Other public methods
public:
CStudioRenderContext();
virtual ~CStudioRenderContext();
private:
// Load, unload materials
void LoadMaterials( studiohdr_t *phdr, OptimizedModel::FileHeader_t *, studioloddata_t &lodData, int lodID );
// Determines material flags
void ComputeMaterialFlags( studiohdr_t *phdr, studioloddata_t &lodData, IMaterial *pMaterial );
// Creates, destroys static meshes
void R_StudioCreateStaticMeshes( studiohdr_t *pStudioHdr, OptimizedModel::FileHeader_t* pVtxHdr,
studiohwdata_t *pStudioHWData, int lodID, int *pColorMeshID );
void R_StudioCreateSingleMesh( studiohdr_t *pStudioHdr, studioloddata_t *pStudioLodData,
mstudiomesh_t* pMesh, OptimizedModel::MeshHeader_t* pVtxMesh, int numBones,
studiomeshdata_t* pMeshData, int *pColorMeshID );
void R_StudioDestroyStaticMeshes( int numStudioMeshes, studiomeshdata_t **ppStudioMeshes );
// Determine if any strip groups shouldn't be morphed
void DetermineHWMorphing( mstudiomodel_t *pModel, OptimizedModel::ModelLODHeader_t *pVtxLOD );
// Count deltas affecting a particular stripgroup
int CountDeltaFlexedStripGroups( mstudiomodel_t *pModel, OptimizedModel::ModelLODHeader_t *pVtxLOD );
// Count vertices affected by deltas in a particular strip group
int CountFlexedVertices( mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup );
// Builds morph data
void R_StudioBuildMorph( studiohdr_t *pStudioHdr, studiomeshgroup_t* pMeshGroup, mstudiomesh_t* pMesh,
OptimizedModel::StripGroupHeader_t *pStripGroup );
// Builds the decal bone remap for a particular mesh
void ComputeHWMorphDecalBoneRemap( studiohdr_t *pStudioHdr, OptimizedModel::FileHeader_t *pVtxHdr, studiohwdata_t *pStudioHWData, int nLOD );
void BuildDecalBoneMap( studiohdr_t *pStudioHdr, int *pUsedBones, int *pBoneRemap, int *pMaxBoneCount, mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup );
// Helper methods used to construct static meshes
int GetNumBoneWeights( const OptimizedModel::StripGroupHeader_t *pGroup );
VertexFormat_t CalculateVertexFormat( const studiohdr_t *pStudioHdr, const studioloddata_t *pStudioLodData,
const mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t *pGroup, bool bIsHwSkinned );
bool MeshNeedsTangentSpace( studiohdr_t *pStudioHdr, studioloddata_t *pStudioLodData, mstudiomesh_t* pMesh );
void R_StudioBuildMeshGroup( const char *pModelName, bool bNeedsTangentSpace, studiomeshgroup_t* pMeshGroup,
OptimizedModel::StripGroupHeader_t *pStripGroup, mstudiomesh_t* pMesh,
studiohdr_t *pStudioHdr, VertexFormat_t vertexFormat );
void R_StudioBuildMeshStrips( studiomeshgroup_t* pMeshGroup,
OptimizedModel::StripGroupHeader_t *pStripGroup );
template <VertexCompressionType_t T> bool R_AddVertexToMesh( const char *pModelName, bool bNeedsTangentSpace, CMeshBuilder& meshBuilder,
OptimizedModel::Vertex_t* pVertex, mstudiomesh_t* pMesh, const mstudio_meshvertexdata_t *vertData, bool hwSkin );
// This will generate random flex data that has a specified # of non-zero values
void GenerateRandomFlexWeights( int nWeightCount, float* pWeights, float *pDelayedWeights );
// Computes LOD
int ComputeRenderLOD( IMatRenderContext *pRenderContext, const DrawModelInfo_t& info, const Vector &origin, float *pMetric );
// This invokes proxies of all materials that are queued to be rendered
void InvokeBindProxies( const DrawModelInfo_t &info );
// Did this matrix come from our allocator?
bool IsInternallyAllocated( const matrix3x4_t *pBoneToWorld );
// Did this flex weights come from our allocator?
bool IsInternallyAllocated( const float *pFlexWeights );
private:
StudioRenderContext_t m_RC;
// Used by the lighting computation methods,
// this is only here to prevent constructors in lightpos_t from being repeatedly run
lightpos_t m_pLightPos[MAXLIGHTCOMPUTE];
};
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline int CStudioRenderContext::ComputeModelLod( studiohwdata_t *pHardwareData, float flUnitSphereSize, float *pMetric )
{
return ComputeModelLODAndMetric( pHardwareData, flUnitSphereSize, pMetric );
}
#endif // STUDIORENDERCONTEXT_H
+3
View File
@@ -0,0 +1,3 @@
LIBRARY StudioRender_360.dll
EXPORTS
CreateInterface @1