//===== Copyright 1996-2005, 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 #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; struct MeshRenderData_t; struct MeshRenderData2_t; struct BaseMeshRenderData_t; struct ShadowMeshRenderData_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 #define MAX_MAT_OVERRIDES 4 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 ) const { if ((m_Body == 0xFFFF) || (m_Model == 0xFFFF) || (m_Mesh == 0xFFFF)) return NULL; mstudiobodyparts_t * RESTRICT pBody = pHdr->pBodypart( m_Body ); mstudiomodel_t * RESTRICT 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 BeginFrame( void ); void EndFrame( void ); void PushScissor( FlashlightState_t *state ); void PopScissor( FlashlightState_t *state ); 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 StudioModelArrayInfo_t &drawInfo, const StudioRenderContext_t &rc, int arrayCount, StudioArrayInstanceData_t *pInstanceData, int instanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL ); void DrawModelArray2( const StudioModelArrayInfo2_t &info, const StudioRenderContext_t &rc, int nCount, StudioArrayData_t *pArrayData, int nInstanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL ); void DrawModelShadowArray( const StudioRenderContext_t &rc, int nCount, StudioArrayData_t *pShadowData, int nInstanceStride, int flags ); // 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 DrawModelArrayStaticProp( const DrawModelInfo_t& info, const StudioRenderContext_t &rc, int nInstanceCount, const MeshInstanceData_t *pInstanceData, ColorMeshInfo_t **pColorMeshes ); 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 ); // 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, void *pvProxyUserData = NULL, int nAdditionalDecalFlags = 0 ); // 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 #ifndef _CERT void GatherRenderedFaceInfo( IStudioRender::FaceInfoCallbackFunc_t pFunc ); // Spew per-model-per-frame 'faces rendered' counters: void UpdateModelFaceCounts( int nNumToSpew = 0, bool bClearHistory = 0 ); #endif // _CERT int GetForcedMaterialOverrideIndex( int nMaterialIndex ); 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; }; struct DecalHistory_t { unsigned short m_Material; unsigned short m_Decal; DecalId_t m_nId; unsigned short m_nPad; }; typedef CUtlLinkedList DecalVertexList_t; typedef CUtlVector DecalIndexList_t; typedef CUtlLinkedList DecalList_t; typedef CUtlLinkedList DecalHistoryList_t; struct DecalMaterial_t { IMaterial* m_pMaterial; DecalIndexList_t m_Indices; DecalVertexList_t m_Vertices; DecalList_t m_Decals; void *m_pvProxyUserData; }; 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 }; // Render data for decals struct DecalRenderData_t { DecalMaterial_t *m_pDecalMaterial; StudioArrayInstanceData_t *m_pInstance; IMaterial *m_pRenderMaterial; bool m_bIsVertexLit; }; // 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; float m_flMaxDepth; float m_flFrontFacingCosineCheck; 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; bool m_AllowBehindPointOfImpact; bool m_bEnforceProjectionRadiusZ; }; 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; int m_nFlags; }; 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, IMaterial *pMaterial ); void R_StudioSoftwareProcessMesh_NormalsBatched( IMatRenderContext *pRenderContext, mstudiomesh_t* pmesh, studiomeshgroup_t* pGroup, StudioModelLighting_t lighting, bool doFlex, float r_blend, bool bShowNormals, bool bShowTangent ); void R_StudioSoftwareProcessMesh_Normals( mstudiomesh_t* pmesh, CMeshBuilder& meshBuilder, int firstVertex, int numVertices, unsigned short* pGroupToMesh, StudioModelLighting_t lighting, bool doFlex, float r_blend, bool bShowNormals, bool bShowTangentS, bool bShowTangentT ); void R_StudioProcessFlexedMesh_StreamOffset( mstudiomesh_t* pmesh, int lod ); template void FillFlexMeshGroupVB( CMeshBuilder & meshBuilder, studiomeshgroup_t *pGroup ); void R_StudioFlexMeshGroup( studiomeshgroup_t *pGroup ); template 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( ) 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, matrix3x4_t *pPoseToWorld, Vector& out1 ); inline void R_StudioRotate( Vector& in1, mstudioboneweight_t *pboneweight, matrix3x4_t *pPoseToWorld, Vector& out1 ); inline void R_StudioRotate( Vector4D& in1, mstudioboneweight_t *pboneweight, matrix3x4_t *pPoseToWorld, 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 ); bool BTryToRetireDecal( StudioDecalHandle_t hDecal, DecalHistoryList_t *pHistoryList, bool bCanRetirePlayerSpray ); 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( DecalBuildInfo_t& build, const Vector * norm, const mstudioboneweight_t *pboneweight ); bool IsDecalStartPointWithinZLimit( DecalBuildInfo_t& build, const Vector * ppos, 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, void *pvProxyUserData ); 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 ); // Same as above, but doesn't repeat a walk through the LRU list. void RetireDecalAtAddress( DecalModelList_t &list, DecalLRUListIndex_t lruAddress, int iLOD, int iMaxLOD ); // Cleans up immediate mode decals. void CleanupDecals(); // Helper methods related to drawing decals void DrawSingleBoneDecals( CMeshBuilder& meshBuilder, const 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, bool bQuadList ); // 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 ); // Implementational methods of model array rendering void DrawModelSubArray( IMatRenderContext* pRenderContext, const StudioModelArrayInfo_t &drawInfo, const StudioRenderContext_t &rc, int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride, int nFlags ); // Implementation details for fast model rendering int CountMeshesToDraw( const StudioModelArrayInfo_t &drawInfo, int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride, int nTimesRendered ); int CountMeshesToDraw( const StudioModelArrayInfo2_t &drawInfo, int nCount, StudioArrayData_t *pArrayData, int nInstanceStride, int nTimesRendered ); static bool SortLessFunc( const MeshRenderData_t &left, const MeshRenderData_t &right ); static bool SortLessFunc2( const MeshRenderData2_t &left, const MeshRenderData2_t &right ); int BuildSortedRenderList( MeshRenderData_t *pRenderData, int *pTotalStripCount, const StudioModelArrayInfo_t &drawInfo, int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride, int nFlags ); int BuildSortedRenderList( MeshRenderData2_t *pRenderData, int *pTotalStripCount, const StudioModelArrayInfo2_t &drawInfo, int nCount, StudioArrayData_t *pArrayData, int nInstanceStride, int nFlags ); void BuildForcedMaterialRenderList( MeshRenderData_t *pRenderData, int *pTotalStripCount, const StudioModelArrayInfo_t &drawInfo, const StudioRenderContext_t &rc, int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride ); void BuildForcedMaterialRenderList( MeshRenderData2_t *pRenderData, int *pTotalStripCount, const StudioModelArrayInfo2_t &drawInfo, const StudioRenderContext_t &rc, int nCount, StudioArrayData_t *pArrayData, int nInstanceStride ); void RestoreMeshes( int nCount, BaseMeshRenderData_t *pRenderData, int nStride ); void DrawMeshRenderData( IMatRenderContext *pRenderContext, const StudioModelArrayInfo_t &drawInfo, int nCount, MeshRenderData_t *pRenderData, int nTotalStripCount, int nFlashlightMask ); void DrawMeshRenderData( IMatRenderContext *pRenderContext, const StudioModelArrayInfo2_t &drawInfo, int nCount, MeshRenderData2_t *pRenderData, int nTotalStripCount, int nFlashlightMask ); void DrawModelArrayFlashlight( IMatRenderContext *pRenderContext, const StudioModelArrayInfo_t &drawInfo, int nCount, MeshRenderData_t *pRenderData, int nTotalStripCount ); void DrawModelArrayFlashlight( IMatRenderContext *pRenderContext, const StudioModelArrayInfo2_t &drawInfo, int nCount, MeshRenderData2_t *pRenderData, int nTotalStripCount ); void BuildDecalIndices( CMeshBuilder &meshBuilder, const DecalMaterial_t& decalMaterial ); int CountDecalMeshesToDraw( int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride ); static bool SortDecalsLessFunc( const DecalRenderData_t &left, const DecalRenderData_t &right ); int BuildSortedDecalRenderList( DecalRenderData_t *pDecalRenderData, int nCount, StudioArrayInstanceData_t *pInstanceData, int nInstanceStride ); void DrawInstancedMultiBoneDecals( CMeshBuilder& meshBuilder, const DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, matrix3x4_t *pPoseToWorld ); void DrawInstancedSingleBoneDecals( CMeshBuilder& meshBuilder, const DecalMaterial_t& decalMaterial, studiohdr_t *pStudioHdr, matrix3x4_t *pPoseToWorld ); void DrawModelArrayDecals( IMatRenderContext *pRenderContext, studiohdr_t *pStudioHdr, int nCount, DecalRenderData_t *pRenderData, int nFlashlightMask ); void DrawModelArrayFlashlightDecals( IMatRenderContext *pRenderContext, studiohdr_t *pStudioHdr, int nFlashlightCount, FlashlightInstance_t *pFlashlights, int nCount, DecalRenderData_t *pRenderData ); // Implementation details w/ respect to shadow depth rendering int CountMeshesToDraw( int nCount, StudioArrayData_t *pShadowData, int nInstanceStride ); int BuildShadowRenderList( ShadowMeshRenderData_t *pRenderData, int *pTotalStripCount, int nCount, StudioArrayData_t *pShadowData, int nInstanceStride, int flags ); void DrawShadowMeshRenderData( IMatRenderContext *pRenderContext, int nCount, ShadowMeshRenderData_t *pRenderData, int nTotalStripCount ); void GetDepthWriteMaterial( IMaterial** ppDepthMaterial, bool *pIsAlphaTested, bool *pUsesTreeSway, IMaterial *pSrcMaterial, bool IsTranslucentUnderModulation, bool bIsSSAODepthWrite = false ); void SetupAlphaTestedDepthWrite( IMaterial* pDepthMaterial, IMaterial *pSrcMaterial ); void SetupTreeSwayDepthWrite( IMaterial* pDepthMaterial, IMaterial *pSrcMaterial ); static bool ShadowSortLessFunc( const ShadowMeshRenderData_t &left, const ShadowMeshRenderData_t &right ); void ComputeDiffuseModulation( Vector4D *pDiffuseModulation ); // Subdivision surface rendering methods void SkinSubDCage( mstudiovertex_t *pVertices, int nNumVertices, matrix3x4_t *pPoseToWorld, CCachedRenderData &vertexCache, unsigned short* pGroupToMesh, fltx4 *vOutput, bool bDoFlex ); void GenerateBicubicPatches( mstudiomesh_t* pmesh, studiomeshgroup_t* pGroup, bool bDoFlex ); void SoftwareProcessQuadMesh( mstudiomesh_t* pmesh, CMeshBuilder& meshBuilder, int numFaces, unsigned short* pGroupToMesh, unsigned short *pIndices, bool bTangentSpace, bool bDoFlex ); // 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 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; studiohwdata_t *m_pStudioHWData; eyeballstate_t m_pEyeballState[16]; // MAXSTUDIOEYEBALLS // debug materials IMaterial *m_pMaterialWireframe[2][2]; // Four flavors: (ZBuffer, DisplacementMapped) IMaterial *m_pMaterialMRMNormals; IMaterial *m_pMaterialTangentFrame; IMaterial *m_pMaterialTranslucentModelHulls; IMaterial *m_pMaterialSolidModelHulls; IMaterial *m_pMaterialAdditiveVertexColorVertexAlpha; IMaterial *m_pMaterialModelBones; IMaterial *m_pMaterialWorldWireframe; IMaterial *m_pMaterialModelEnvCubemap; IMaterial *m_pMaterialSolidBackfacePrepass; // Depth override material IMaterial *m_pDepthWrite[ 2 ][ 2 ][ 2 ]; IMaterial *m_pSSAODepthWrite[ 2 ][ 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; FlashlightState_t *m_pCurrentFlashlight; #if !defined( LINUX ) // Vectors of Vector4D's for CPU-side subdivision surface processing CUtlVector< fltx4, CUtlMemoryAligned > m_vSkinnedSubDVertices; #endif #ifndef _CERT // Each model counts how many rendered faces it accounts for each frame: CUtlHash< studiohwdata_t * > m_ModelFaceCountHash; #endif // !_CERT 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, matrix3x4_t *pPoseToWorld, Vector& out1 ) { // MEASURECODE( "R_StudioTransform" ); Vector out2; switch( pboneweight->numbones ) { case 1: VectorTransform( in1, pPoseToWorld[pboneweight->bone[0]], out1 ); break; /* case 2: VectorTransform( in1, pPoseToWorld[pboneweight->bone[0]], out1 ); out1 *= pboneweight->weight[0]; VectorTransform( in1, pPoseToWorld[pboneweight->bone[1]], out2 ); VectorMA( out1, pboneweight->weight[1], out2, out1 ); break; case 3: VectorTransform( in1, pPoseToWorld[pboneweight->bone[0]], out1 ); out1 *= pboneweight->weight[0]; VectorTransform( in1, pPoseToWorld[pboneweight->bone[1]], out2 ); VectorMA( out1, pboneweight->weight[1], out2, out1 ); VectorTransform( in1, pPoseToWorld[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, pPoseToWorld[pboneweight->bone[i]], out2 ); VectorMA( out1, pboneweight->weight[i], out2, out1 ); } break; } } /* ================ R_StudioRotate ================ */ inline void CStudioRender::R_StudioRotate( Vector& in1, mstudioboneweight_t *pboneweight, matrix3x4_t *pPoseToWorld, 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, pPoseToWorld[pboneweight->bone[0]], out1 ); } else { Vector out2; VectorFill( out1, 0 ); for (int i = 0; i < pboneweight->numbones; i++) { VectorRotate( in1, pPoseToWorld[pboneweight->bone[i]], out2 ); VectorMA( out1, pboneweight->weight[i], out2, out1 ); } VectorNormalize( out1 ); } } inline void CStudioRender::R_StudioRotate( Vector4D& realIn1, mstudioboneweight_t *pboneweight, matrix3x4_t *pPoseToWorld, Vector4D& realOut1 ) { // garymcthack - god this sucks. Vector in1( realIn1[0], realIn1[1], realIn1[2] ); Vector out1; if (pboneweight->numbones == 1) { VectorRotate( in1, pPoseToWorld[pboneweight->bone[0]], out1 ); } else { Vector out2; VectorFill( out1, 0 ); for (int i = 0; i < pboneweight->numbones; i++) { VectorRotate( in1, pPoseToWorld[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; } #ifdef _PS3 Assert( false ); // PS3 doesn't have true __assume (used in NO_DEFAULT), so a return value is expected return 0.0f; #endif } }; 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; } #ifdef _PS3 Assert( false ); // PS3 doesn't have true __assume (used in NO_DEFAULT), so a return value is expected return 0.0f; #endif } }; 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::WorldLightAngle( wl, lnormal, snormal, delta ); case MATERIAL_LIGHT_POINT: return CWorldLightAngleWrapper::WorldLightAngle( wl, lnormal, snormal, delta ); case MATERIAL_LIGHT_DIRECTIONAL: return CWorldLightAngleWrapper::WorldLightAngle( wl, lnormal, snormal, delta ); case MATERIAL_LIGHT_SPOT: return CWorldLightAngleWrapper::WorldLightAngle( wl, lnormal, snormal, delta ); NO_DEFAULT; } #ifdef _PS3 Assert( false ); // PS3 doesn't have true __assume (used in NO_DEFAULT), so a return value is expected return 0.0f; #endif } //----------------------------------------------------------------------------- // 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 ); } // Helper to determine which material type to use depending on what strip header flags are set. MaterialPrimitiveType_t GetPrimitiveTypeForStripHeaderFlags( unsigned char Flags ); #endif // CSTUDIORENDER_H