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

#include <stdlib.h>
#include "studiorender.h"
#include "studiorendercontext.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialvar.h"
#include "materialsystem/imesh.h"
#include "optimize.h"
#include "mathlib/vmatrix.h"
#include "tier0/vprof.h"
#include "tier1/strtools.h"
#include "tier1/KeyValues.h"
#include "tier0/memalloc.h"
#include "convar.h"
#include "materialsystem/itexture.h"
#include "tier2/tier2.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
CStudioRender g_StudioRender;
CStudioRender *g_pStudioRenderImp = &g_StudioRender;


//-----------------------------------------------------------------------------
// Activate to get stats
//-----------------------------------------------------------------------------
//#define REPORT_FLEX_STATS 1

#ifdef REPORT_FLEX_STATS
static int s_nModelsDrawn = 0;
static int s_nActiveFlexCount = 0;
static ConVar r_flexstats( "r_flexstats", "0", FCVAR_CHEAT );
#endif


//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CStudioRender::CStudioRender()
{
	m_pRC = NULL;
	m_pBoneToWorld = NULL;
	m_pFlexWeights = NULL;
	m_pFlexDelayedWeights = NULL;
	m_pStudioHdr = NULL;
	m_pStudioMeshes = NULL;
	m_pSubModel = NULL;
	m_pGlintTexture = NULL;
	m_GlintWidth = 0;
	m_GlintHeight = 0;

	// Cache-align our important matrices
	MemAlloc_PushAllocDbgInfo( __FILE__, __LINE__ );

	m_PoseToWorld = (matrix3x4_t*)MemAlloc_AllocAligned( MAXSTUDIOBONES * sizeof(matrix3x4_t), 32 );
	m_PoseToDecal = (matrix3x4_t*)MemAlloc_AllocAligned( MAXSTUDIOBONES * sizeof(matrix3x4_t), 32 );

	MemAlloc_PopAllocDbgInfo();
	m_nDecalId = 1;
}

CStudioRender::~CStudioRender()
{
	MemAlloc_FreeAligned(m_PoseToWorld);
	MemAlloc_FreeAligned(m_PoseToDecal);
}

void CStudioRender::InitDebugMaterials( void )
{
	m_pMaterialMRMWireframe = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true );
	m_pMaterialMRMWireframe->IncrementReferenceCount();

	m_pMaterialMRMWireframeZBuffer = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmwireframezbuffer", TEXTURE_GROUP_OTHER, true );
	m_pMaterialMRMWireframeZBuffer->IncrementReferenceCount();

	m_pMaterialMRMNormals = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmrmnormals", TEXTURE_GROUP_OTHER, true );
	m_pMaterialMRMNormals->IncrementReferenceCount();

	m_pMaterialTangentFrame = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugvertexcolor", TEXTURE_GROUP_OTHER, true );
	m_pMaterialTangentFrame->IncrementReferenceCount();

	m_pMaterialTranslucentModelHulls = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugtranslucentmodelhulls", TEXTURE_GROUP_OTHER, true );
	m_pMaterialTranslucentModelHulls->IncrementReferenceCount();

	m_pMaterialSolidModelHulls = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugsolidmodelhulls", TEXTURE_GROUP_OTHER, true );
	m_pMaterialSolidModelHulls->IncrementReferenceCount();

	m_pMaterialAdditiveVertexColorVertexAlpha = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/additivevertexcolorvertexalpha", TEXTURE_GROUP_OTHER, true );
	m_pMaterialAdditiveVertexColorVertexAlpha->IncrementReferenceCount();

	m_pMaterialModelBones = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugmodelbones", TEXTURE_GROUP_OTHER, true );
	m_pMaterialModelBones->IncrementReferenceCount();

	m_pMaterialModelEnvCubemap =
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/env_cubemap_model", TEXTURE_GROUP_OTHER, true );
	m_pMaterialModelEnvCubemap->IncrementReferenceCount();
	
	m_pMaterialWorldWireframe = 
		g_pMaterialSystem->FindMaterial( "//platform/materials/debug/debugworldwireframe", TEXTURE_GROUP_OTHER, true );
	m_pMaterialWorldWireframe->IncrementReferenceCount();

	if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 )
	{
		KeyValues *pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 0 );
		pVMTKeyValues->SetInt( "$nocull", 0 );
		m_pDepthWrite[0][0] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite00", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pDepthWrite[0][0]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 0 );
		pVMTKeyValues->SetInt( "$nocull", 1 );
		m_pDepthWrite[0][1] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite01", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pDepthWrite[0][1]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 1 );
		pVMTKeyValues->SetInt( "$nocull", 0 );
		m_pDepthWrite[1][0] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite10", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pDepthWrite[1][0]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 1 );
		pVMTKeyValues->SetInt( "$nocull", 1 );
		m_pDepthWrite[1][1] = g_pMaterialSystem->FindProceduralMaterial( "__DepthWrite11", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pDepthWrite[1][1]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 0 );
		pVMTKeyValues->SetInt( "$nocull", 0 );
		pVMTKeyValues->SetInt( "$color_depth", 1 );
		m_pSSAODepthWrite[0][0] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite00", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pSSAODepthWrite[0][0]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 0 );
		pVMTKeyValues->SetInt( "$nocull", 1 );
		pVMTKeyValues->SetInt( "$color_depth", 1 );
		m_pSSAODepthWrite[0][1] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite01", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pSSAODepthWrite[0][1]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 1 );
		pVMTKeyValues->SetInt( "$nocull", 0 );
		pVMTKeyValues->SetInt( "$color_depth", 1 );
		m_pSSAODepthWrite[1][0] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite10", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pSSAODepthWrite[1][0]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "DepthWrite" );
		pVMTKeyValues->SetInt( "$no_fullbright", 1 );
		pVMTKeyValues->SetInt( "$alphatest", 1 );
		pVMTKeyValues->SetInt( "$nocull", 1 );
		pVMTKeyValues->SetInt( "$color_depth", 1 );
		m_pSSAODepthWrite[1][1] = g_pMaterialSystem->FindProceduralMaterial( "__ColorDepthWrite11", TEXTURE_GROUP_OTHER, pVMTKeyValues );
		m_pSSAODepthWrite[1][1]->IncrementReferenceCount();

		pVMTKeyValues = new KeyValues( "EyeGlint" );
		m_pGlintBuildMaterial = g_pMaterialSystem->CreateMaterial( "___glintbuildmaterial", pVMTKeyValues );
	}
}

void CStudioRender::ShutdownDebugMaterials( void )
{
#ifdef _WIN32
	if ( m_pMaterialMRMWireframe )
	{
		m_pMaterialMRMWireframe->DecrementReferenceCount();
		m_pMaterialMRMWireframe = NULL;
	}

	if ( m_pMaterialMRMWireframeZBuffer )
	{
		m_pMaterialMRMWireframeZBuffer->DecrementReferenceCount();
		m_pMaterialMRMWireframeZBuffer = NULL;
	}

	if ( m_pMaterialMRMNormals )
	{
		m_pMaterialMRMNormals->DecrementReferenceCount();
		m_pMaterialMRMNormals = NULL;
	}

	if ( m_pMaterialTangentFrame )
	{
		m_pMaterialTangentFrame->DecrementReferenceCount();
		m_pMaterialTangentFrame = NULL;
	}

	if ( m_pMaterialTranslucentModelHulls )
	{
		m_pMaterialTranslucentModelHulls->DecrementReferenceCount();
		m_pMaterialTranslucentModelHulls = NULL;
	}
	
	if ( m_pMaterialSolidModelHulls )
	{
		m_pMaterialSolidModelHulls->DecrementReferenceCount();
		m_pMaterialSolidModelHulls = NULL;
	}
	
	if ( m_pMaterialAdditiveVertexColorVertexAlpha )
	{
		m_pMaterialAdditiveVertexColorVertexAlpha->DecrementReferenceCount();
		m_pMaterialAdditiveVertexColorVertexAlpha = NULL;
	}
	
	if ( m_pMaterialModelBones )
	{
		m_pMaterialModelBones->DecrementReferenceCount();
		m_pMaterialModelBones = NULL;
	}
	
	if ( m_pMaterialModelEnvCubemap )
	{
		m_pMaterialModelEnvCubemap->DecrementReferenceCount();
		m_pMaterialModelEnvCubemap = NULL;
	}

	if ( m_pMaterialWorldWireframe )
	{
		m_pMaterialWorldWireframe->DecrementReferenceCount();
		m_pMaterialWorldWireframe = NULL;
	}

	// DepthWrite materials
	for ( int32 i = 0; i < 4; i++ )
	{
		if ( m_pDepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] )
		{
			m_pDepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ]->DecrementReferenceCount();
			m_pDepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] = NULL;
		}

		if ( m_pSSAODepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] )
		{
			m_pSSAODepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ]->DecrementReferenceCount();
			m_pSSAODepthWrite[ ( i & 0x2 ) >> 1 ][ i & 0x1 ] = NULL;
		}
	}

	if ( m_pGlintBuildMaterial )
	{
		m_pGlintBuildMaterial->DecrementReferenceCount();
		m_pGlintBuildMaterial = NULL;
	}
#endif
}

static void ReleaseMaterialSystemObjects()
{
//	g_StudioRender.UncacheGlint();
}

static void RestoreMaterialSystemObjects( int nChangeFlags )
{
//	g_StudioRender.PrecacheGlint();
}



//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CStudioRender::Init()
{
	if ( g_pMaterialSystem && g_pMaterialSystemHardwareConfig )
	{
		g_pMaterialSystem->AddReleaseFunc( ReleaseMaterialSystemObjects );
		g_pMaterialSystem->AddRestoreFunc( RestoreMaterialSystemObjects );

		InitDebugMaterials();

		return INIT_OK;
	}

	return INIT_FAILED;
}

void CStudioRender::Shutdown( void )
{
	UncacheGlint();
	ShutdownDebugMaterials();

	if ( g_pMaterialSystem )
	{
		g_pMaterialSystem->RemoveReleaseFunc( ReleaseMaterialSystemObjects );
		g_pMaterialSystem->RemoveRestoreFunc( RestoreMaterialSystemObjects );
	}
}


//-----------------------------------------------------------------------------
// Sets the lighting render state
//-----------------------------------------------------------------------------
void CStudioRender::SetLightingRenderState()
{
	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );

	// FIXME: What happens when we use the fixed function pipeline but vertex shaders 
	// are active? For the time being this only works because everything that does
	// vertex lighting does, in fact, have a vertex shader which is used to render it.
	pRenderContext->SetAmbientLightCube( m_pRC->m_LightBoxColors );

	if ( m_pRC->m_Config.bSoftwareLighting || m_pRC->m_NumLocalLights == 0 )
	{
		pRenderContext->DisableAllLocalLights();
	}
	else
	{
		int nMaxLightCount = g_pMaterialSystemHardwareConfig->MaxNumLights();
		LightDesc_t desc;
		desc.m_Type = MATERIAL_LIGHT_DISABLE;

		int i;
		int nLightCount = min( m_pRC->m_NumLocalLights, nMaxLightCount );
		for( i = 0; i < nLightCount; ++i )
		{
			pRenderContext->SetLight( i, m_pRC->m_LocalLights[i] );
		}
		for( ; i < nMaxLightCount; ++i )
		{
			pRenderContext->SetLight( i, desc );
		}
	}
}


//-----------------------------------------------------------------------------
// Shadow state (affects the models as they are rendered)
//-----------------------------------------------------------------------------
void CStudioRender::AddShadow( IMaterial* pMaterial, void* pProxyData, FlashlightState_t *pFlashlightState, VMatrix *pWorldToTexture, ITexture *pFlashlightDepthTexture )
{
	int i = m_ShadowState.AddToTail();
	ShadowState_t& state = m_ShadowState[i];
	state.m_pMaterial = pMaterial;
	state.m_pProxyData = pProxyData;
	state.m_pFlashlightState = pFlashlightState;
	state.m_pWorldToTexture = pWorldToTexture;
	state.m_pFlashlightDepthTexture = pFlashlightDepthTexture;
}

void CStudioRender::ClearAllShadows()
{
	m_ShadowState.RemoveAll();
}

void CStudioRender::GetFlexStats( )
{
#ifdef REPORT_FLEX_STATS
	static bool s_bLastFlexStats = false;
	bool bDoStats = r_flexstats.GetInt() != 0;
	if ( bDoStats )
	{
		if ( !s_bLastFlexStats )
		{
			s_nModelsDrawn = 0;
			s_nActiveFlexCount = 0;
		}

		// Count number of active weights
		int nActiveFlexCount = 0;
		for ( int i = 0; i < MAXSTUDIOFLEXDESC; ++i )
		{
			if ( fabs( m_FlexWeights[i] ) >= 0.001f || fabs( m_FlexDelayedWeights[i] ) >= 0.001f )
			{
				++nActiveFlexCount;
			}
		}

		++s_nModelsDrawn;
		s_nActiveFlexCount += nActiveFlexCount;
	}
	else
	{
		if ( s_bLastFlexStats )
		{
			if ( s_nModelsDrawn )
			{
				Msg( "Average number of flexes/model: %d\n", s_nActiveFlexCount / s_nModelsDrawn );
			}
			else
			{
				Msg( "No models rendered to take stats of\n" );
			}

			s_nModelsDrawn = 0;
			s_nActiveFlexCount = 0;
		}
	}

	s_bLastFlexStats = bDoStats;
#endif
}


//-----------------------------------------------------------------------------
// Main model rendering entry point
//-----------------------------------------------------------------------------
void CStudioRender::DrawModel( const DrawModelInfo_t& info, const StudioRenderContext_t &rc, 
	matrix3x4_t *pBoneToWorld, const FlexWeights_t &flex, int flags )
{
	if ( ( flags & STUDIORENDER_GENERATE_STATS ) != 0 )
	{
		ModelStats( info, rc, pBoneToWorld, flex, flags );

		return;
	}

	VPROF( "CStudioRender::DrawModel");

	m_pRC = const_cast< StudioRenderContext_t* >( &rc );
	m_pFlexWeights = flex.m_pFlexWeights;
	m_pFlexDelayedWeights = flex.m_pFlexDelayedWeights;
	m_pBoneToWorld = pBoneToWorld;

	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );

	// Disable flex if we're told to...
	bool flexConfig = m_pRC->m_Config.bFlex;
	if (flags & STUDIORENDER_DRAW_NO_FLEXES)
	{
		m_pRC->m_Config.bFlex = false;
	}

	// Enable wireframe if we're told to...
	bool bWireframe = m_pRC->m_Config.bWireframe;
	if ( flags & STUDIORENDER_DRAW_WIREFRAME )
	{
		m_pRC->m_Config.bWireframe = true;
	}

	int boneMask = BONE_USED_BY_VERTEX_AT_LOD( info.m_Lod );

	// Preserve the matrices if we're skinning
	pRenderContext->MatrixMode( MATERIAL_MODEL );
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();

	m_VertexCache.StartModel();

	m_pStudioHdr = info.m_pStudioHdr;
	if ( !info.m_pHardwareData->m_pLODs )
	{
		// If we are missing LODs then print the model name before returning
		// so we can perhaps correct the underlying problem.
		Msg( "Missing LODs for %s, lod index is %d.\n", m_pStudioHdr->pszName(), info.m_Lod );
		return;
	}
	m_pStudioMeshes = info.m_pHardwareData->m_pLODs[info.m_Lod].m_pMeshData;

	// Bone to world must be set before calling drawmodel; it uses that here
	ComputePoseToWorld( m_PoseToWorld, m_pStudioHdr, boneMask, m_pRC->m_ViewOrigin, pBoneToWorld );

	R_StudioRenderModel( pRenderContext, info.m_Skin, info.m_Body, info.m_HitboxSet, info.m_pClientEntity,
		info.m_pHardwareData->m_pLODs[info.m_Lod].ppMaterials, 
		info.m_pHardwareData->m_pLODs[info.m_Lod].pMaterialFlags, flags, boneMask, info.m_Lod, info.m_pColorMeshes);

	// Draw all the decals on this model
	// If the model is not in memory, this code may not function correctly
	// This code assumes the model has been rendered!
	// So skip if the model hasn't been rendered
	// Also, skip if we're rendering to the shadow depth map
	if ( ( m_pStudioMeshes != 0 ) && !( flags & ( STUDIORENDER_SHADOWDEPTHTEXTURE | STUDIORENDER_SSAODEPTHTEXTURE )) )
	{
		if ((flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY)
		{
			DrawDecal( info, info.m_Lod, info.m_Body );
		}

		// Draw shadows
		if ( !( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
		{
			DrawShadows( info, flags, boneMask );
		}

		if( (flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY &&
			!( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
		{
			DrawFlashlightDecals( info, info.m_Lod );
		}
	}

	// Restore the matrices if we're skinning
	pRenderContext->MatrixMode( MATERIAL_MODEL );
	pRenderContext->PopMatrix();

	// Restore the configs
	m_pRC->m_Config.bFlex = flexConfig;
	m_pRC->m_Config.bWireframe = bWireframe;

#ifdef REPORT_FLEX_STATS
	GetFlexStats();
#endif

	pRenderContext->SetNumBoneWeights( 0 );
	m_pRC = NULL;
	m_pBoneToWorld = NULL;
	m_pFlexWeights = NULL;
	m_pFlexDelayedWeights = NULL;
}

void CStudioRender::DrawModelStaticProp( const DrawModelInfo_t& info, 
	const StudioRenderContext_t &rc, const matrix3x4_t& rootToWorld, int flags )
{
	VPROF( "CStudioRender::DrawModelStaticProp");

	m_pRC = const_cast<StudioRenderContext_t*>( &rc );

	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );

	memcpy( &m_StaticPropRootToWorld, &rootToWorld, sizeof(matrix3x4_t) );
	memcpy( &m_PoseToWorld[0], &rootToWorld, sizeof(matrix3x4_t) );
	m_pBoneToWorld = &m_StaticPropRootToWorld;

	bool flexConfig = m_pRC->m_Config.bFlex;
	m_pRC->m_Config.bFlex = false;
	bool bWireframe = m_pRC->m_Config.bWireframe;
	if ( flags & STUDIORENDER_DRAW_WIREFRAME )
	{
		m_pRC->m_Config.bWireframe = true;
	}

	int lod = info.m_Lod;
	m_pStudioHdr = info.m_pStudioHdr;
	m_pStudioMeshes = info.m_pHardwareData->m_pLODs[lod].m_pMeshData;

	if ( ( flags & STUDIORENDER_GENERATE_STATS ) != 0 )
	{
		FlexWeights_t	flex;

		ModelStats( info, rc, m_pBoneToWorld, flex, flags | STUDIORENDER_DRAW_NO_FLEXES );

		return;
	}

	R_StudioRenderModel( pRenderContext, info.m_Skin, info.m_Body, info.m_HitboxSet, info.m_pClientEntity,
		info.m_pHardwareData->m_pLODs[lod].ppMaterials, 
		info.m_pHardwareData->m_pLODs[lod].pMaterialFlags, flags, BONE_USED_BY_ANYTHING, lod, info.m_pColorMeshes);

	// If we're not shadow depth mapping
	if ( ( flags & ( STUDIORENDER_SHADOWDEPTHTEXTURE | STUDIORENDER_SSAODEPTHTEXTURE ) ) == 0 )
	{
		// FIXME: Should this occur in a separate call?
		// Draw all the decals on this model
		if ((flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY)
		{
			DrawDecal( info, lod, info.m_Body );
		}

		// Draw shadows
		if ( !( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
		{
			DrawShadows( info, flags, BONE_USED_BY_ANYTHING );
		}

		if( (flags & STUDIORENDER_DRAW_GROUP_MASK) != STUDIORENDER_DRAW_TRANSLUCENT_ONLY &&
			!( flags & STUDIORENDER_DRAW_NO_SHADOWS ) )
		{
			DrawFlashlightDecals( info, lod );
		}
	}

	// Restore the configs
	m_pRC->m_Config.bFlex = flexConfig;
	m_pRC->m_Config.bWireframe = bWireframe;

	pRenderContext->SetNumBoneWeights( 0 );
	m_pBoneToWorld = NULL;
	m_pRC = NULL;
}




// UNDONE: Currently no flex supported, no per instance cubemap or other lighting state supported, no eyeballs supported
// NOTE: This is a fast path for simple models with skeletons but not many other features
void CStudioRender::DrawModelArray( const DrawModelInfo_t &drawInfo, const StudioRenderContext_t &rc, int arrayCount, model_array_instance_t *pInstanceData, int instanceStride, int flags )
{
	tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %d", __FUNCTION__, arrayCount );

#ifndef SWDS												// no drawing on dedicated server
#if 0
	FlexWeights_t flex;
	memset(&flex, 0, sizeof(flex));
	for ( int i = 0; i < arrayCount; i++ )
	{
		DrawModel( drawInfo, rc, &pInstanceData[i].modelToWorld, flex, flags );
	}
	return;
#endif

	m_pRC = const_cast< StudioRenderContext_t* >( &rc );
	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );

	// Preserve the matrices if we're skinning
	pRenderContext->MatrixMode( MATERIAL_MODEL );
	pRenderContext->PushMatrix();
	pRenderContext->LoadIdentity();
	pRenderContext->SetNumBoneWeights( 0 );

	// get the studio mesh data for this lod
	studiomeshdata_t *pMeshDataBase = drawInfo.m_pHardwareData->m_pLODs[drawInfo.m_Lod].m_pMeshData;
	IMaterial **ppMaterials = drawInfo.m_pHardwareData->m_pLODs[drawInfo.m_Lod].ppMaterials;
	int *pMaterialFlags = drawInfo.m_pHardwareData->m_pLODs[drawInfo.m_Lod].pMaterialFlags;
	studiohdr_t *pStudioHdr = drawInfo.m_pStudioHdr;
	m_bDrawTranslucentSubModels = false;

	int skin = drawInfo.m_Skin;
	short *pskinref	= pStudioHdr->pSkinref( 0 );
	if ( skin > 0 && skin < pStudioHdr->numskinfamilies )
	{
		pskinref += ( skin * pStudioHdr->numskinref );
	}

	for ( int body = 0; body < pStudioHdr->numbodyparts; ++body ) 
	{
		mstudiobodyparts_t  *pbodypart = pStudioHdr->pBodypart( body );

		int index = drawInfo.m_Body / pbodypart->base;
		index = index % pbodypart->nummodels;
		mstudiomodel_t *pSubmodel = pbodypart->pModel( index );


		for ( int meshIndex = 0; meshIndex < pSubmodel->nummeshes; ++meshIndex )
		{
			mstudiomesh_t *pmesh = pSubmodel->pMesh(meshIndex);
			studiomeshdata_t *pMeshData = &pMeshDataBase[pmesh->meshid];
			Assert( pMeshData );

			if ( !pMeshData->m_NumGroup )
				continue;

			if ( !pMaterialFlags )
				continue;

			StudioModelLighting_t lighting = LIGHTING_HARDWARE;
			int materialFlags = pMaterialFlags[pskinref[pmesh->material]];

			IMaterial* pMaterial = R_StudioSetupSkinAndLighting( pRenderContext, pskinref[ pmesh->material ], ppMaterials, materialFlags, drawInfo.m_pClientEntity, NULL, lighting );
			if ( !pMaterial )
				continue;

			// eyeball! can't do those in array mode yet
			Assert( pmesh->materialtype != 1 );
			//R_StudioDrawMesh( pRenderContext, pmesh, pMeshData, lighting, pMaterial, NULL, drawInfo.m_Lod );
			// Draw all the various mesh groups...
			for ( int meshGroupIndex = 0; meshGroupIndex < pMeshData->m_NumGroup; ++meshGroupIndex )
			{
				studiomeshgroup_t* pGroup = &pMeshData->m_pMeshGroup[meshGroupIndex];

				// Older models are merely flexed while new ones are also delta flexed
				Assert(!(pGroup->m_Flags & MESHGROUP_IS_FLEXED));
				Assert(!(pGroup->m_Flags & MESHGROUP_IS_DELTA_FLEXED));
				IMesh *pMesh = pGroup->m_pMesh;

				// Needed when we switch back and forth between hardware + software lighting
				if ( IsPC() && pGroup->m_MeshNeedsRestore )
				{
					VertexCompressionType_t compressionType = CompressionType( pMesh->GetVertexFormat() );
					switch ( compressionType )
					{
					case VERTEX_COMPRESSION_ON:
						R_StudioRestoreMesh<VERTEX_COMPRESSION_ON>( pmesh, pGroup );
					case VERTEX_COMPRESSION_NONE:
					default:
						R_StudioRestoreMesh<VERTEX_COMPRESSION_NONE>( pmesh, pGroup );
						break;
					}
					pGroup->m_MeshNeedsRestore = false;
				}
				pMesh->SetColorMesh( NULL, 0 );

				MaterialPrimitiveType_t stripType = MATERIAL_TRIANGLES;
				pMesh->SetPrimitiveType(stripType);
				if ( pStudioHdr->numbones > 1 )
				{
					byte *pData = (byte *)pInstanceData;
					for ( int i = 0;i < arrayCount; i++, pData += instanceStride )
					{
						matrix3x4_t *pBones = &( ((model_array_instance_t *)pData)->modelToWorld );
						pRenderContext->LoadMatrix( pBones[0] );
						for (int j = 0; j < pGroup->m_NumStrips; ++j)
						{
							OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[j];
							// Reset bone state if we're hardware skinning
							pRenderContext->SetNumBoneWeights( pStrip->numBones );
							for (int k = 0; k < pStrip->numBoneStateChanges; ++k)
							{
								OptimizedModel::BoneStateChangeHeader_t* pStateChange = pStrip->pBoneStateChange(k);
								if ( pStateChange->newBoneID < 0 )
									break;

								pRenderContext->LoadBoneMatrix( pStateChange->hardwareID, pBones[pStateChange->newBoneID] );
							}
							MaterialPrimitiveType_t localStripType = pStrip->flags & OptimizedModel::STRIP_IS_TRISTRIP ? MATERIAL_TRIANGLE_STRIP : MATERIAL_TRIANGLES;

							if ( localStripType != stripType )
							{
								pMesh->SetPrimitiveType( localStripType );
								stripType = localStripType;
							}
							pMesh->Draw( pStrip->indexOffset, pStrip->numIndices );
						}
					}
					pRenderContext->SetNumBoneWeights( 0 );
				}
				else
				{
					byte *pData = (byte *)pInstanceData;
					for ( int i = 0;i < arrayCount; i++, pData += instanceStride )
					{
						matrix3x4_t *pBones = &( ((model_array_instance_t *)pData)->modelToWorld );
						pRenderContext->LoadMatrix( pBones[0] );
						for (int j = 0; j < pGroup->m_NumStrips; ++j)
						{
							OptimizedModel::StripHeader_t* pStrip = &pGroup->m_pStripData[j];
							MaterialPrimitiveType_t localStripType = pStrip->flags & OptimizedModel::STRIP_IS_TRISTRIP ? MATERIAL_TRIANGLE_STRIP : MATERIAL_TRIANGLES;

							if ( localStripType != stripType )
							{
								pMesh->SetPrimitiveType( localStripType );
								stripType = localStripType;
							}
							pMesh->Draw( pStrip->indexOffset, pStrip->numIndices );
						}
					}
				}
			}
		}
	}

	pRenderContext->MatrixMode( MATERIAL_MODEL );
	pRenderContext->PopMatrix();
#endif
}