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.
932 lines
31 KiB
932 lines
31 KiB
5 years ago
|
//========= 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
|