You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
931 lines
31 KiB
931 lines
31 KiB
//========= 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
|
|
|