//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//===========================================================================//

#ifndef STUDIORENDERCONTEXT_H
#define STUDIORENDERCONTEXT_H
#ifdef _WIN32
#pragma once
#endif

#include "istudiorender.h"
#include "tier3/tier3.h"
#include "studio.h"
#include "tier1/delegates.h"
#include "tier1/memstack.h"
#include "studiorender.h"


//-----------------------------------------------------------------------------
// Foward declarations
//-----------------------------------------------------------------------------
class IStudioDataCache;
class CStudioRender;


//-----------------------------------------------------------------------------
// Global interfaces
//-----------------------------------------------------------------------------
extern IStudioDataCache *g_pStudioDataCache;
extern CStudioRender *g_pStudioRenderImp;

IMaterial* GetModelSpecificDecalMaterial( IMaterial* pDecalMaterial );

//-----------------------------------------------------------------------------
// Internal config structure
//-----------------------------------------------------------------------------
struct StudioRenderConfigInternal_t : public StudioRenderConfig_t
{
	bool m_bSupportsVertexAndPixelShaders : 1;
	bool m_bSupportsOverbright : 1;
	bool m_bEnableHWMorph : 1;
	bool m_bStatsMode : 1;
};


//-----------------------------------------------------------------------------
// All the data needed to render a studiomodel
//-----------------------------------------------------------------------------
struct FlexWeights_t
{
	float *m_pFlexWeights;
	float *m_pFlexDelayedWeights;
};

struct StudioRenderContext_t
{
	StudioRenderConfigInternal_t	m_Config;
	Vector					m_ViewTarget;
	Vector					m_ViewOrigin;
	Vector					m_ViewRight;
	Vector					m_ViewUp;
	Vector					m_ViewPlaneNormal;
	Vector4D				m_LightBoxColors[6];
	LightDesc_t				m_LocalLights[MAXLOCALLIGHTS];
	int						m_NumLocalLights;
	float					m_ColorMod[3];
	float					m_AlphaMod;
	IMaterial*				m_pForcedMaterial;
	OverrideType_t			m_nForcedMaterialType;
};


//-----------------------------------------------------------------------------
// Helper to queue up calls if necessary
//-----------------------------------------------------------------------------
#define QUEUE_STUDIORENDER_CALL( FuncName, ClassName, pObject, ... )	\
	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );			\
	ICallQueue *pCallQueue = pRenderContext->GetCallQueue();			\
	if ( !pCallQueue || studio_queue_mode.GetInt() == 0 )				\
	{																	\
		pObject->FuncName( __VA_ARGS__ );								\
	}																	\
	else																\
	{																	\
		pCallQueue->QueueCall( pObject, &ClassName::FuncName, ##__VA_ARGS__ );	\
	}

#define QUEUE_STUDIORENDER_CALL_RC( FuncName, ClassName, pObject, pRenderContext, ... )	\
	ICallQueue *pCallQueue = pRenderContext->GetCallQueue();			\
	if ( !pCallQueue || studio_queue_mode.GetInt() == 0 )				\
	{																	\
		pObject->FuncName( __VA_ARGS__ );								\
	}																	\
	else																\
	{																	\
		pCallQueue->QueueCall( pObject, &ClassName::FuncName, ##__VA_ARGS__ );	\
	}


//-----------------------------------------------------------------------------
// Implementation of IStudioRender
//-----------------------------------------------------------------------------
class CStudioRenderContext : public CTier3AppSystem< IStudioRender >
{
	typedef CTier3AppSystem< IStudioRender > BaseClass;

	// Methods of IAppSystem 
public:
	virtual bool Connect( CreateInterfaceFn factory );
	virtual void Disconnect();
	virtual void *QueryInterface( const char *pInterfaceName );
	virtual InitReturnVal_t Init();
	virtual void Shutdown();

	// Methods of IStudioRender
public:
	virtual void BeginFrame( void );
	virtual void EndFrame( void );
	virtual void Mat_Stub( IMaterialSystem *pMatSys );
	virtual void UpdateConfig( const StudioRenderConfig_t& config );
	virtual void GetCurrentConfig( StudioRenderConfig_t& config );
	virtual bool LoadModel(studiohdr_t *pStudioHdr, void *pVtxData, studiohwdata_t *pHardwareData);
	virtual void UnloadModel( studiohwdata_t *pHardwareData );
	virtual void RefreshStudioHdr( studiohdr_t* pStudioHdr, studiohwdata_t* pHardwareData );
	virtual void SetEyeViewTarget( const studiohdr_t *pStudioHdr, int nBodyIndex, const Vector& worldPosition );
	virtual void SetAmbientLightColors( const Vector *pAmbientOnlyColors );
	virtual void SetAmbientLightColors( const Vector4D *pAmbientOnlyColors );
	virtual void SetLocalLights( int numLights, const LightDesc_t *pLights );
	virtual int GetNumAmbientLightSamples();
	virtual const Vector *GetAmbientLightDirections();
	virtual void SetViewState( const Vector& viewOrigin, const Vector& viewRight, const Vector& viewUp, const Vector& viewPlaneNormal );
	virtual int GetNumLODs( const studiohwdata_t &hardwareData ) const;
	virtual float GetLODSwitchValue( const studiohwdata_t &hardwareData, int lod ) const;
	virtual void SetLODSwitchValue( studiohwdata_t &hardwareData, int lod, float switchValue );
	virtual void SetColorModulation( const float* pColor );
	virtual void SetAlphaModulation( float alpha );
	virtual void DrawModel( DrawModelResults_t *pResults, const DrawModelInfo_t& info, matrix3x4_t *pCustomBoneToWorld, float *pFlexWeights, float *pFlexDelayedWeights, const Vector& origin, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
	virtual void DrawModelArray( const DrawModelInfo_t &drawInfo, int arrayCount, model_array_instance_t *pInstanceData, int instanceStride, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
	virtual void DrawModelStaticProp( const DrawModelInfo_t& info, const matrix3x4_t &modelToWorld, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
	virtual void DrawStaticPropDecals( const DrawModelInfo_t &drawInfo, const matrix3x4_t &modelToWorld );
	virtual void DrawStaticPropShadows( const DrawModelInfo_t &drawInfo, const matrix3x4_t &modelToWorld, int flags );
	virtual void ForcedMaterialOverride( IMaterial *newMaterial, OverrideType_t nOverrideType = OVERRIDE_NORMAL );
	DELEGATE_TO_OBJECT_1( StudioDecalHandle_t, CreateDecalList, studiohwdata_t *, g_pStudioRenderImp );
	virtual void DestroyDecalList( StudioDecalHandle_t handle );
	virtual void AddDecal( StudioDecalHandle_t handle, studiohdr_t *pStudioHdr, matrix3x4_t *pBoneToWorld, const Ray_t & ray, const Vector& decalUp, IMaterial* pDecalMaterial, float radius, int body, bool noPokethru, int maxLODToDecal = ADDDECAL_TO_ALL_LODS );
	virtual void ComputeLighting( const Vector* pAmbient, int lightCount, LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting );
	virtual void ComputeLightingConstDirectional( const Vector* pAmbient, int lightCount, LightDesc_t* pLights, const Vector& pt, const Vector& normal, Vector& lighting, float flDirectionalAmount );
	virtual void AddShadow( IMaterial* pMaterial, void* pProxyData, FlashlightState_t *pFlashlightState, VMatrix *pWorldToTexture, ITexture *pFlashlightDepthTexture );
	virtual void ClearAllShadows();
	virtual int ComputeModelLod( studiohwdata_t* pHardwareData, float flUnitSphereSize, float *pMetric = NULL );
	virtual void GetPerfStats( DrawModelResults_t *pResults, const DrawModelInfo_t &info, CUtlBuffer *pSpewBuf = NULL ) const;
	virtual void GetTriangles( const DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, GetTriangles_Output_t &out );
	virtual int GetMaterialList( studiohdr_t *pStudioHdr, int count, IMaterial** ppMaterials );
	virtual int GetMaterialListFromBodyAndSkin( MDLHandle_t studio, int nSkin, int nBody, int nCountOutputMaterials, IMaterial** ppOutputMaterials );
	virtual matrix3x4_t* LockBoneMatrices( int nCount );
	virtual void UnlockBoneMatrices();
	virtual void LockFlexWeights( int nWeightCount, float **ppFlexWeights, float **ppFlexDelayedWeights = NULL );
	virtual void UnlockFlexWeights();
	virtual void GetMaterialOverride( IMaterial** ppOutForcedMaterial, OverrideType_t* pOutOverrideType );

	// Other public methods
public:
	CStudioRenderContext();
	virtual ~CStudioRenderContext();

private:
	// Load, unload materials
	void LoadMaterials( studiohdr_t *phdr, OptimizedModel::FileHeader_t *, studioloddata_t &lodData, int lodID );

	// Determines material flags
	void ComputeMaterialFlags( studiohdr_t *phdr, studioloddata_t &lodData, IMaterial *pMaterial );

	// Creates, destroys static meshes
	void R_StudioCreateStaticMeshes( studiohdr_t *pStudioHdr, OptimizedModel::FileHeader_t* pVtxHdr,
		studiohwdata_t *pStudioHWData, int lodID, int *pColorMeshID );
	void R_StudioCreateSingleMesh( studiohdr_t *pStudioHdr, studioloddata_t *pStudioLodData, 
		mstudiomesh_t* pMesh, OptimizedModel::MeshHeader_t* pVtxMesh, int numBones, 
		studiomeshdata_t* pMeshData, int *pColorMeshID );
	void R_StudioDestroyStaticMeshes( int numStudioMeshes, studiomeshdata_t **ppStudioMeshes );

	// Determine if any strip groups shouldn't be morphed
	void DetermineHWMorphing( mstudiomodel_t *pModel, OptimizedModel::ModelLODHeader_t *pVtxLOD );

	// Count deltas affecting a particular stripgroup
	int CountDeltaFlexedStripGroups( mstudiomodel_t *pModel, OptimizedModel::ModelLODHeader_t *pVtxLOD );

	// Count vertices affected by deltas in a particular strip group
	int CountFlexedVertices( mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup );

	// Builds morph data
	void R_StudioBuildMorph( studiohdr_t *pStudioHdr, studiomeshgroup_t* pMeshGroup,	mstudiomesh_t* pMesh,
		OptimizedModel::StripGroupHeader_t *pStripGroup );

	// Builds the decal bone remap for a particular mesh
	void ComputeHWMorphDecalBoneRemap( studiohdr_t *pStudioHdr, OptimizedModel::FileHeader_t *pVtxHdr, studiohwdata_t *pStudioHWData, int nLOD );
	void BuildDecalBoneMap( studiohdr_t *pStudioHdr, int *pUsedBones, int *pBoneRemap, int *pMaxBoneCount, mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup );

	// Helper methods used to construct static meshes
	int GetNumBoneWeights( const OptimizedModel::StripGroupHeader_t *pGroup );
	VertexFormat_t CalculateVertexFormat( const studiohdr_t *pStudioHdr, const studioloddata_t *pStudioLodData,
																const mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t *pGroup, bool bIsHwSkinned );
	bool MeshNeedsTangentSpace( studiohdr_t *pStudioHdr, studioloddata_t *pStudioLodData, mstudiomesh_t* pMesh );
	void R_StudioBuildMeshGroup( const char *pModelName, bool bNeedsTangentSpace, studiomeshgroup_t* pMeshGroup,
		OptimizedModel::StripGroupHeader_t *pStripGroup, mstudiomesh_t* pMesh,
		studiohdr_t *pStudioHdr, VertexFormat_t vertexFormat );
	void R_StudioBuildMeshStrips( studiomeshgroup_t* pMeshGroup,
		OptimizedModel::StripGroupHeader_t *pStripGroup );
	template <VertexCompressionType_t T> bool R_AddVertexToMesh( const char *pModelName, bool bNeedsTangentSpace, CMeshBuilder& meshBuilder, 
		OptimizedModel::Vertex_t* pVertex, mstudiomesh_t* pMesh, const mstudio_meshvertexdata_t *vertData, bool hwSkin );

	// This will generate random flex data that has a specified # of non-zero values
	void GenerateRandomFlexWeights( int nWeightCount, float* pWeights, float *pDelayedWeights );

	// Computes LOD
	int ComputeRenderLOD( IMatRenderContext *pRenderContext, const DrawModelInfo_t& info, const Vector &origin, float *pMetric );

	// This invokes proxies of all materials that are queued to be rendered
	void InvokeBindProxies( const DrawModelInfo_t &info );

	// Did this matrix come from our allocator?
	bool IsInternallyAllocated( const matrix3x4_t *pBoneToWorld );

	// Did this flex weights come from our allocator?
	bool IsInternallyAllocated( const float *pFlexWeights );

private:
	StudioRenderContext_t m_RC;

	// Used by the lighting computation methods,
	// this is only here to prevent constructors in lightpos_t from being repeatedly run
	lightpos_t m_pLightPos[MAXLIGHTCOMPUTE];
};


//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline int CStudioRenderContext::ComputeModelLod( studiohwdata_t *pHardwareData, float flUnitSphereSize, float *pMetric )
{
	return ComputeModelLODAndMetric( pHardwareData, flUnitSphereSize, pMetric );
}


#endif // STUDIORENDERCONTEXT_H