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

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 );
}

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

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
studiorender/r_studio.cpp Normal file
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

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 );
}
}

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 ) );
}
}
}
}
}
}
}

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 );
}

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

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;
}

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
studiorender/studiorender.h Normal file
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

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

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

View File

@ -0,0 +1,3 @@
LIBRARY StudioRender_360.dll
EXPORTS
CreateInterface @1