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