//========= 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