//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================
#include "movieobjects/dmemdl.h"
#include "movieobjects/dmetransform.h"
#include "movieobjects/dmedag.h"
#include "movieobjects_interfaces.h"
#include "datamodel/dmelementfactoryhelper.h"
#include "datacache/imdlcache.h"
#include "istudiorender.h"
#include "bone_setup.h"
#include "tier3/tier3.h"
#include "tier3/mdlutils.h"

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


//-----------------------------------------------------------------------------
// Expose this class to the scene database 
//-----------------------------------------------------------------------------
IMPLEMENT_ELEMENT_FACTORY( DmeMDL, CDmeMDL );


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CDmeMDL::OnConstruction()
{
	m_bDrawInEngine = false;
//	SetAttributeValueElement( "transform", CreateElement< CDmeTransform >() );
//	SetAttributeValue( "mdlfilename", "models/alyx.mdl" );
	m_Color.InitAndSet( this, "color", Color( 255, 255, 255, 255 ) );
	m_nSkin.InitAndSet( this, "skin", 0 );
	m_nBody.InitAndSet( this, "body", 0 );
	m_nSequence.InitAndSet( this, "sequence", 0 );
	m_nLOD.InitAndSet( this, "lod", 0 );
	m_flPlaybackRate.InitAndSet( this, "playbackrate", 30.0f );
	m_flTime.InitAndSet( this, "time", 0.0f );
	m_vecViewTarget.Init( this, "viewTarget" );
	m_bWorldSpaceViewTarget.Init( this, "worldSpaceViewTarget" );
}

void CDmeMDL::OnDestruction()
{
	m_MDL.SetMDL( MDLHANDLE_INVALID );
}


void CDmeMDL::SetMDL( MDLHandle_t handle )
{
	m_MDL.SetMDL( handle );

	Vector vecMins, vecMaxs;
	GetMDLBoundingBox( &vecMins, &vecMaxs, m_MDL.GetMDL(), m_nSequence );
	Vector vecLookAt( 100.0f, 0.0f, vecMaxs.z );

	m_vecViewTarget.Set( vecLookAt );
	m_bWorldSpaceViewTarget = false;
}

MDLHandle_t CDmeMDL::GetMDL( ) const
{
	return m_MDL.GetMDL();
}

	
//-----------------------------------------------------------------------------
// Loads the model matrix based on the transform
//-----------------------------------------------------------------------------
void CDmeMDL::DrawInEngine( bool bDrawInEngine )
{
	m_bDrawInEngine = bDrawInEngine;
}

bool CDmeMDL::IsDrawingInEngine() const
{
	return m_bDrawInEngine;
}
	

//-----------------------------------------------------------------------------
// Returns the bounding box for the model
//-----------------------------------------------------------------------------
void CDmeMDL::GetBoundingBox( Vector *pMins, Vector *pMaxs ) const
{
	GetMDLBoundingBox( pMins, pMaxs, m_MDL.GetMDL(), m_nSequence );

	// Rotate the root transform to make it align with DMEs
	// DMEs up vector is the y axis
	if ( !m_bDrawInEngine )
	{
		Vector vecMins, vecMaxs;
		matrix3x4_t engineToDme;
		CDmeDag::EngineToDmeMatrix( engineToDme );
		TransformAABB( engineToDme, *pMins, *pMaxs, vecMins, vecMaxs );
		*pMins = vecMins;
		*pMaxs = vecMaxs;
	}
}


//-----------------------------------------------------------------------------
// Returns the radius of the model as measured from the origin
//-----------------------------------------------------------------------------
float CDmeMDL::GetRadius() const
{
	return GetMDLRadius( m_MDL.GetMDL(), m_nSequence );
}

	
//-----------------------------------------------------------------------------
// Returns a more accurate bounding sphere
//-----------------------------------------------------------------------------
void CDmeMDL::GetBoundingSphere( Vector &vecCenter, float &flRadius )
{
	Vector vecEngineCenter;
	GetMDLBoundingSphere( &vecEngineCenter, &flRadius, m_MDL.GetMDL(), m_nSequence );

	// Rotate the root transform to make it align with DMEs
	// DMEs up vector is the y axis
	if ( !m_bDrawInEngine )
	{
		matrix3x4_t engineToDme;
		CDmeDag::EngineToDmeMatrix( engineToDme );
		VectorTransform( vecEngineCenter, engineToDme, vecCenter );
	}
	else
	{
		vecCenter = vecEngineCenter;
	}
}

	
//-----------------------------------------------------------------------------
// Updates the MDL rendering helper
//-----------------------------------------------------------------------------
void CDmeMDL::UpdateMDL()
{
	m_MDL.m_Color = m_Color;
	m_MDL.m_nSkin = m_nSkin;
	m_MDL.m_nBody = m_nBody;
	m_MDL.m_nSequence = m_nSequence;
	m_MDL.m_nLOD = m_nLOD;
	m_MDL.m_flPlaybackRate = m_flPlaybackRate;
	m_MDL.m_flTime = m_flTime;
	m_MDL.m_vecViewTarget = m_vecViewTarget;
	m_MDL.m_Color = m_Color;
	m_MDL.m_bWorldSpaceViewTarget = m_bWorldSpaceViewTarget;
}


//-----------------------------------------------------------------------------
// Draws the mesh
//-----------------------------------------------------------------------------
void CDmeMDL::Draw( const matrix3x4_t &shapeToWorld, CDmeDrawSettings *pDrawSettings /* = NULL */ )
{
	UpdateMDL();
	studiohdr_t *pStudioHdr = m_MDL.GetStudioHdr();
	if ( !pStudioHdr )
		return;

	// FIXME: Why is this necessary!?!?!?
	CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
	if ( !m_bDrawInEngine )
	{
		pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
	}

	matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( pStudioHdr->numbones );
	SetUpBones( shapeToWorld, pStudioHdr->numbones, pBoneToWorld );
	g_pStudioRender->UnlockBoneMatrices();

	m_MDL.Draw( shapeToWorld, pBoneToWorld );

	// FIXME: Why is this necessary!?!?!?
	if ( !m_bDrawInEngine )
	{
		pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
	}
}


void CDmeMDL::SetUpBones( const matrix3x4_t& shapeToWorld, int nMaxBoneCount, matrix3x4_t *pOutputMatrices )
{
	UpdateMDL();

	// Root transform
	matrix3x4_t rootToWorld;

	// Rotate the root transform to make it align with DMEs
	// DMEs up vector is the y axis
	if ( !m_bDrawInEngine )
	{
		matrix3x4_t engineToDme;
		CDmeDag::EngineToDmeMatrix( engineToDme );
		ConcatTransforms( engineToDme, shapeToWorld, rootToWorld );
	}
	else
	{
		MatrixCopy( shapeToWorld, rootToWorld );
	}

	m_MDL.SetUpBones( rootToWorld, nMaxBoneCount, pOutputMatrices );
}