//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Implements visual effects entities: sprites, beams, bubbles, etc.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "Sprite.h"
#include "model_types.h"
#include "engine/ivmodelinfo.h"
#include "tier0/vprof.h"
#include "engine/ivdebugoverlay.h"

#if defined( CLIENT_DLL )
	#include "enginesprite.h"
	#include "iclientmode.h"
	#include "c_baseviewmodel.h"
#	ifdef PORTAL
		#include "c_prop_portal.h"
#	endif //ifdef PORTAL
#else
	#include "baseviewmodel.h"
#endif

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

const float MAX_SPRITE_SCALE = 64.0f;
const float MAX_GLOW_PROXY_SIZE = 64.0f;

LINK_ENTITY_TO_CLASS( env_sprite, CSprite );
LINK_ENTITY_TO_CLASS( env_sprite_oriented, CSpriteOriented );
#if !defined( CLIENT_DLL )
LINK_ENTITY_TO_CLASS( env_glow, CSprite ); // For backwards compatibility, remove when no longer needed.
#endif

#if !defined( CLIENT_DLL )
BEGIN_DATADESC( CSprite )

	DEFINE_FIELD( m_flLastTime, FIELD_TIME ),
	DEFINE_FIELD( m_flMaxFrame, FIELD_FLOAT ),
	DEFINE_FIELD( m_hAttachedToEntity, FIELD_EHANDLE ),
	DEFINE_FIELD( m_nAttachment, FIELD_INTEGER ),
	DEFINE_FIELD( m_flDieTime, FIELD_TIME ),

	DEFINE_FIELD( m_nBrightness,		FIELD_INTEGER ),
	DEFINE_FIELD( m_flBrightnessTime,	FIELD_FLOAT ),

	DEFINE_KEYFIELD( m_flSpriteScale, FIELD_FLOAT, "scale" ),
	DEFINE_KEYFIELD( m_flSpriteFramerate, FIELD_FLOAT, "framerate" ),
	DEFINE_KEYFIELD( m_flFrame, FIELD_FLOAT, "frame" ),
#ifdef PORTAL
	DEFINE_FIELD( m_bDrawInMainRender, FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bDrawInPortalRender, FIELD_BOOLEAN ),
#endif
	DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ),

	DEFINE_KEYFIELD( m_flGlowProxySize,	FIELD_FLOAT, "GlowProxySize" ),
	
	DEFINE_FIELD( m_flScaleTime,		FIELD_FLOAT ),
	DEFINE_FIELD( m_flStartScale,		FIELD_FLOAT ),
	DEFINE_FIELD( m_flDestScale,		FIELD_FLOAT ),
	DEFINE_FIELD( m_flScaleTimeStart,	FIELD_TIME ),
	DEFINE_FIELD( m_nStartBrightness,	FIELD_INTEGER ),
	DEFINE_FIELD( m_nDestBrightness,	FIELD_INTEGER ),
	DEFINE_FIELD( m_flBrightnessTimeStart, FIELD_TIME ),
	DEFINE_FIELD( m_bWorldSpaceScale,	FIELD_BOOLEAN ),

	// Function Pointers
	DEFINE_FUNCTION( AnimateThink ),
	DEFINE_FUNCTION( ExpandThink ),
	DEFINE_FUNCTION( AnimateUntilDead ),
	DEFINE_FUNCTION( BeginFadeOutThink ),

	// Inputs
	DEFINE_INPUT( m_flSpriteScale, FIELD_FLOAT, "SetScale" ),
	DEFINE_INPUTFUNC( FIELD_VOID, "HideSprite", InputHideSprite ),
	DEFINE_INPUTFUNC( FIELD_VOID, "ShowSprite", InputShowSprite ),
	DEFINE_INPUTFUNC( FIELD_VOID, "ToggleSprite", InputToggleSprite ),
	DEFINE_INPUTFUNC( FIELD_FLOAT, "ColorRedValue", InputColorRedValue ),
	DEFINE_INPUTFUNC( FIELD_FLOAT, "ColorGreenValue", InputColorGreenValue ),
	DEFINE_INPUTFUNC( FIELD_FLOAT, "ColorBlueValue", InputColorBlueValue ),

END_DATADESC()

#else

BEGIN_PREDICTION_DATA( CSprite )

	// Networked
	DEFINE_PRED_FIELD( m_hAttachedToEntity, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_nAttachment, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_flScaleTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_flSpriteScale, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_flSpriteFramerate, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_flFrame, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
#ifdef PORTAL
	DEFINE_PRED_FIELD( m_bDrawInMainRender, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_bDrawInPortalRender, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
#endif
	DEFINE_PRED_FIELD( m_flBrightnessTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_nBrightness, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),

	DEFINE_FIELD( m_flLastTime, FIELD_FLOAT ),
	DEFINE_FIELD( m_flMaxFrame, FIELD_FLOAT ),
	DEFINE_FIELD( m_flDieTime, FIELD_FLOAT ),

//	DEFINE_FIELD( m_flHDRColorScale, FIELD_FLOAT ),
//	DEFINE_FIELD( m_flStartScale, FIELD_FLOAT ),			//Starting scale
//	DEFINE_FIELD( m_flDestScale, FIELD_FLOAT ),			//Destination scale
//	DEFINE_FIELD( m_flScaleTimeStart, FIELD_FLOAT ),		//Real time for start of scale
//	DEFINE_FIELD( m_nStartBrightness, FIELD_INTEGER ),		//Starting brightness
//	DEFINE_FIELD( m_nDestBrightness, FIELD_INTEGER ),		//Destination brightness
//	DEFINE_FIELD( m_flBrightnessTimeStart, FIELD_FLOAT ),	//Real time for brightness

END_PREDICTION_DATA()

#endif

IMPLEMENT_NETWORKCLASS_ALIASED( Sprite, DT_Sprite );

#if defined( CLIENT_DLL )

static void RecvProxy_SpriteScale( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	((CSprite*)pStruct)->SetSpriteScale( pData->m_Value.m_Float );
}

#endif

BEGIN_NETWORK_TABLE( CSprite, DT_Sprite )
#if !defined( CLIENT_DLL )
	SendPropEHandle( SENDINFO(m_hAttachedToEntity )),
	SendPropInt( SENDINFO(m_nAttachment ), 8 ),
	SendPropFloat( SENDINFO(m_flScaleTime ), 0,	SPROP_NOSCALE ),

#ifdef HL2_DLL
	SendPropFloat( SENDINFO(m_flSpriteScale ), 0,	SPROP_NOSCALE),
#else
	SendPropFloat( SENDINFO(m_flSpriteScale ), 8,	SPROP_ROUNDUP,	0.0f,	MAX_SPRITE_SCALE),
#endif
	SendPropFloat( SENDINFO(m_flGlowProxySize ), 6,	SPROP_ROUNDUP,	0.0f,	MAX_GLOW_PROXY_SIZE),

	SendPropFloat( SENDINFO(m_flHDRColorScale ), 0,	SPROP_NOSCALE,	0.0f,	100.0f),

	SendPropFloat( SENDINFO(m_flSpriteFramerate ), 8,	SPROP_ROUNDUP,	0,	60.0f),
	SendPropFloat( SENDINFO(m_flFrame),		20, SPROP_ROUNDDOWN,	0.0f,   256.0f),
#ifdef PORTAL
	SendPropBool( SENDINFO(m_bDrawInMainRender) ),
	SendPropBool( SENDINFO(m_bDrawInPortalRender) ),
#endif //#ifdef PORTAL
	SendPropFloat( SENDINFO(m_flBrightnessTime ), 0,	SPROP_NOSCALE ),
	SendPropInt( SENDINFO(m_nBrightness), 8, SPROP_UNSIGNED ),
	SendPropBool( SENDINFO(m_bWorldSpaceScale) ),
#else
	RecvPropEHandle(RECVINFO(m_hAttachedToEntity)),
	RecvPropInt(RECVINFO(m_nAttachment)),
	RecvPropFloat(RECVINFO(m_flScaleTime)),
	RecvPropFloat(RECVINFO(m_flSpriteScale), 0, RecvProxy_SpriteScale),
	RecvPropFloat(RECVINFO(m_flSpriteFramerate)),
	RecvPropFloat(RECVINFO(m_flGlowProxySize)),

	RecvPropFloat( RECVINFO(m_flHDRColorScale )),

	RecvPropFloat(RECVINFO(m_flFrame)),
#ifdef PORTAL
	RecvPropBool( RECVINFO(m_bDrawInMainRender) ),
	RecvPropBool( RECVINFO(m_bDrawInPortalRender) ),
#endif //#ifdef PORTAL
	RecvPropFloat(RECVINFO(m_flBrightnessTime)),
	RecvPropInt(RECVINFO(m_nBrightness)),
	RecvPropBool( RECVINFO(m_bWorldSpaceScale) ),
#endif
END_NETWORK_TABLE()


CSprite::CSprite() : BaseClass()
{
	m_flGlowProxySize = 2.0f;
	m_flHDRColorScale = 1.0f;

#ifdef PORTAL
	m_bDrawInMainRender = true;
	m_bDrawInPortalRender = true;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::Spawn( void )
{
	SetSolid( SOLID_NONE );
	SetMoveType( MOVETYPE_NONE );
	m_flFrame = 0;

	Precache();
	SetModel( STRING( GetModelName() ) );
	CollisionProp()->SetSurroundingBoundsType( USE_GAME_CODE );

	m_flMaxFrame = (float)modelinfo->GetModelFrameCount( GetModel() ) - 1;
	AddEffects( EF_NOSHADOW | EF_NORECEIVESHADOW );

#if defined( CLIENT_DLL )
	SetNextClientThink( CLIENT_THINK_ALWAYS );
#endif

#if !defined( CLIENT_DLL )
	if ( GetEntityName() != NULL_STRING && !(m_spawnflags & SF_SPRITE_STARTON) )
	{
		TurnOff();
	}
	else
#endif
	{
		TurnOn();
	}
	
	// Worldcraft only sets y rotation, copy to Z
	if ( GetLocalAngles().y != 0 && GetLocalAngles().z == 0 )
	{
		QAngle angles = GetLocalAngles();

		angles.z = angles.y;
		angles.y = 0;

		SetLocalAngles( angles );
	}

	// Clamp our scale if necessary
	float scale = m_flSpriteScale;
	
	if ( scale < 0 || scale > MAX_SPRITE_SCALE )
	{
#if !defined( CLIENT_DLL ) 
		DevMsg( "LEVEL DESIGN ERROR: Sprite %s with bad scale %f [0..%f]\n", GetDebugName(), m_flSpriteScale.Get(), MAX_SPRITE_SCALE );
#endif
		scale = clamp( (float) m_flSpriteScale, 0.f, MAX_SPRITE_SCALE );
	}

	//Set our state
	SetBrightness( m_clrRender->a );
	SetScale( scale );

#if defined( CLIENT_DLL )
	m_flStartScale = m_flDestScale = m_flSpriteScale;
	m_nStartBrightness = m_nDestBrightness = m_nBrightness;
#endif

}


//-----------------------------------------------------------------------------
// Purpose: Initialize absmin & absmax to the appropriate box
//-----------------------------------------------------------------------------
void CSprite::EnableWorldSpaceScale( bool bEnable )
{
	m_bWorldSpaceScale = bEnable;
}

//-----------------------------------------------------------------------------
// Purpose: Initialize absmin & absmax to the appropriate box
//-----------------------------------------------------------------------------
void CSprite::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
{
	float flScale = m_flSpriteScale * 0.5f;

	if ( m_bWorldSpaceScale == false )
	{
		// Find the height and width of the source of the sprite
		float width = modelinfo->GetModelSpriteWidth( GetModel() );
		float height = modelinfo->GetModelSpriteHeight( GetModel() );
		flScale *= MAX( width, height );
	}

	pVecWorldMins->Init( -flScale, -flScale, -flScale );
	pVecWorldMaxs->Init( flScale, flScale, flScale );
	*pVecWorldMins += GetAbsOrigin();
	*pVecWorldMaxs += GetAbsOrigin();
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *szModelName - 
//-----------------------------------------------------------------------------
void CSprite::SetModel( const char *szModelName )
{
	int index = modelinfo->GetModelIndex( szModelName );
	const model_t *model = modelinfo->GetModel( index );
	if ( model && modelinfo->GetModelType( model ) != mod_sprite )
	{
		Msg( "Setting CSprite to non-sprite model %s\n", szModelName?szModelName:"NULL" );
	}

#if !defined( CLIENT_DLL )
	UTIL_SetModel( this, szModelName );
#else
	BaseClass::SetModel( szModelName );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::Precache( void )
{
	if ( GetModelName() != NULL_STRING )
	{
		PrecacheModel( STRING( GetModelName() ) );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pSpriteName - 
//			&origin - 
//-----------------------------------------------------------------------------
void CSprite::SpriteInit( const char *pSpriteName, const Vector &origin )
{
	SetModelName( MAKE_STRING(pSpriteName) );
	SetLocalOrigin( origin );
	Spawn();
}

#if !defined( CLIENT_DLL )

int CSprite::UpdateTransmitState( void )
{
	if ( GetMoveParent() )
	{
		// we must call ShouldTransmit() if we have a move parent
		return SetTransmitState( FL_EDICT_FULLCHECK );
	}
	else
	{
		return SetTransmitState( FL_EDICT_ALWAYS );
	}
}

int CSprite::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
	// Certain entities like sprites and ropes are strewn throughout the level and they rarely change.
	// For these entities, it's more efficient to transmit them once and then always leave them on
	// the client. Otherwise, the server will have to send big bursts of data with the entity states
	// as they come in and out of the PVS.
	
	if ( GetMoveParent() )
	{
		CBaseViewModel *pViewModel = dynamic_cast<CBaseViewModel *>( GetMoveParent() );

		if ( pViewModel )
		{
			return pViewModel->ShouldTransmit( pInfo );
		}
	}
	
	return FL_EDICT_ALWAYS;
}
 
//-----------------------------------------------------------------------------
// Purpose: Fixup parent after restore
//-----------------------------------------------------------------------------
void CSprite::OnRestore()
{
	BaseClass::OnRestore();

	// Reset attachment after save/restore
	if ( GetFollowedEntity() )
	{
		SetAttachment( GetFollowedEntity(), m_nAttachment );
	}
	else
	{
		// Clear attachment
		m_hAttachedToEntity = NULL;
		m_nAttachment = 0;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pSpriteName - 
//			&origin - 
//			animate - 
// Output : CSprite
//-----------------------------------------------------------------------------
CSprite *CSprite::SpriteCreate( const char *pSpriteName, const Vector &origin, bool animate )
{
	CSprite *pSprite = CREATE_ENTITY( CSprite, "env_sprite" );
	pSprite->SpriteInit( pSpriteName, origin );
	pSprite->SetSolid( SOLID_NONE );
	UTIL_SetSize( pSprite, vec3_origin, vec3_origin );
	pSprite->SetMoveType( MOVETYPE_NONE );
	if ( animate )
		pSprite->TurnOn();

	return pSprite;
}
#endif

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pSpriteName - 
//			&origin - 
//			animate - 
// Output : CSprite
//-----------------------------------------------------------------------------
CSprite *CSprite::SpriteCreatePredictable( const char *module, int line, const char *pSpriteName, const Vector &origin, bool animate )
{
	CSprite *pSprite = ( CSprite * )CBaseEntity::CreatePredictedEntityByName( "env_sprite", module, line );
	if ( pSprite )
	{
		pSprite->SpriteInit( pSpriteName, origin );
		pSprite->SetSolid( SOLID_NONE );
		pSprite->SetSize( vec3_origin, vec3_origin );
		pSprite->SetMoveType( MOVETYPE_NONE );
		if ( animate )
			pSprite->TurnOn();
	}

	return pSprite;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::AnimateThink( void )
{
	Animate( m_flSpriteFramerate * (gpGlobals->curtime - m_flLastTime) );

	SetNextThink( gpGlobals->curtime );
	m_flLastTime			= gpGlobals->curtime;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::AnimateUntilDead( void )
{
	if ( gpGlobals->curtime > m_flDieTime )
	{
		Remove( );
	}
	else
	{
		AnimateThink();
		SetNextThink( gpGlobals->curtime );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : scaleSpeed - 
//			fadeSpeed - 
//-----------------------------------------------------------------------------
void CSprite::Expand( float scaleSpeed, float fadeSpeed )
{
	m_flSpeed = scaleSpeed;
	m_iHealth = fadeSpeed;
	SetThink( &CSprite::ExpandThink );

	SetNextThink( gpGlobals->curtime );
	m_flLastTime	= gpGlobals->curtime;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::ExpandThink( void )
{
	float frametime = gpGlobals->curtime - m_flLastTime;
	SetSpriteScale( m_flSpriteScale + m_flSpeed * frametime );

	int sub = (int)(m_iHealth * frametime);
	if ( sub > m_clrRender->a )
	{
		SetRenderColorA( 0 );
		Remove( );
	}
	else
	{
		SetRenderColorA( m_clrRender->a - sub );
		SetNextThink( gpGlobals->curtime );
		m_flLastTime		= gpGlobals->curtime;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : frames - 
//-----------------------------------------------------------------------------
void CSprite::Animate( float frames )
{ 
	m_flFrame += frames;
	if ( m_flFrame > m_flMaxFrame )
	{
#if !defined( CLIENT_DLL )
		if ( m_spawnflags & SF_SPRITE_ONCE )
		{
			TurnOff();
		}
		else
#endif
		{
			if ( m_flMaxFrame > 0 )
				m_flFrame = fmod( m_flFrame, m_flMaxFrame );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSprite::SetBrightness( int brightness, float time )
{
	m_nBrightness			= brightness;	//Take our current position as our starting position
	m_flBrightnessTime		= time;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::SetSpriteScale( float scale )
{
	if ( scale != m_flSpriteScale )
	{
		m_flSpriteScale		= scale;	//Take our current position as our new starting position
		// The surrounding box is based on sprite scale... it changes, box is dirty
		CollisionProp()->MarkSurroundingBoundsDirty();
	}
}

void CSprite::SetScale( float scale, float time )
{
	m_flScaleTime		= time;
	SetSpriteScale( scale );
	// The surrounding box is based on sprite scale... it changes, box is dirty
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::TurnOff( void )
{
	AddEffects( EF_NODRAW );
	SetNextThink( TICK_NEVER_THINK );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::TurnOn( void )
{
	RemoveEffects( EF_NODRAW );
	if ( (m_flSpriteFramerate && m_flMaxFrame > 1.0)
#if !defined( CLIENT_DLL )
		|| (m_spawnflags & SF_SPRITE_ONCE) 
#endif
		)
	{
		SetThink( &CSprite::AnimateThink );
		SetNextThink( gpGlobals->curtime );
		m_flLastTime = gpGlobals->curtime;
	}
	m_flFrame = 0;
}

#if !defined( CLIENT_DLL )
// DVS TODO: Obsolete Use handler
void CSprite::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
	int on = !IsEffectActive( EF_NODRAW );
	if ( ShouldToggle( useType, on ) )
	{
		if ( on )
		{
			TurnOff();
		}
		else
		{
			TurnOn();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Input handler that hides the sprite.
//-----------------------------------------------------------------------------
void CSprite::InputHideSprite( inputdata_t &inputdata )
{
	TurnOff();
}


//-----------------------------------------------------------------------------
// Purpose: Input handler that hides the sprite.
//-----------------------------------------------------------------------------
void CSprite::InputShowSprite( inputdata_t &inputdata )
{
	TurnOn();
}

void CSprite::InputColorRedValue( inputdata_t &inputdata )
{
	int nNewColor = clamp( FastFloatToSmallInt( inputdata.value.Float() ), 0, 255 );
	SetColor( nNewColor, m_clrRender->g, m_clrRender->b );
}

void CSprite::InputColorGreenValue( inputdata_t &inputdata )
{
	int nNewColor = clamp( FastFloatToSmallInt( inputdata.value.Float() ), 0, 255 );
	SetColor( m_clrRender->r, nNewColor, m_clrRender->b );
}

void CSprite::InputColorBlueValue( inputdata_t &inputdata )
{
	int nNewColor = clamp( FastFloatToSmallInt( inputdata.value.Float() ), 0, 255 );
	SetColor( m_clrRender->r, m_clrRender->g, nNewColor );
}

//-----------------------------------------------------------------------------
// Purpose: Input handler that toggles the sprite between hidden and shown.
//-----------------------------------------------------------------------------
void CSprite::InputToggleSprite( inputdata_t &inputdata )
{
	if ( !IsEffectActive( EF_NODRAW ) )
	{
		TurnOff();
	}
	else
	{
		TurnOn();
	}
}
#endif

#if defined( CLIENT_DLL )

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float CSprite::GetRenderScale( void )
{
	//See if we're done scaling
	if ( ( m_flScaleTime == 0 ) || ( (m_flScaleTimeStart+m_flScaleTime) < gpGlobals->curtime ) )
		return m_flSpriteScale;

	//Get our percentage
	float timeDelta = ( gpGlobals->curtime - m_flScaleTimeStart ) / m_flScaleTime;

	//Return the result
	return ( m_flStartScale + ( ( m_flDestScale - m_flStartScale  ) * timeDelta ) );
}

//-----------------------------------------------------------------------------
// Purpose: Get the rendered extents of the sprite
//-----------------------------------------------------------------------------
void CSprite::GetRenderBounds( Vector &vecMins, Vector &vecMaxs )
{
	float flScale = GetRenderScale() * 0.5f;

	// If our scale is normalized we need to convert that to actual world units
	if ( m_bWorldSpaceScale == false )
	{
		CEngineSprite *psprite = (CEngineSprite *) modelinfo->GetModelExtraData( GetModel() );
		if ( psprite )
		{
			float flSize = MAX( psprite->GetWidth(), psprite->GetHeight() );
			flScale *= flSize;
		}
	}

	vecMins.Init( -flScale, -flScale, -flScale );
	vecMaxs.Init(  flScale,  flScale,  flScale );

#if 0
	// Visualize the bounds
	debugoverlay->AddBoxOverlay( GetRenderOrigin(), vecMins, vecMaxs, GetRenderAngles(), 255, 255, 255, 0, 0.01f );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int	CSprite::GetRenderBrightness( void )
{
	//See if we're done scaling
	if ( ( m_flBrightnessTime == 0 ) || ( (m_flBrightnessTimeStart+m_flBrightnessTime) < gpGlobals->curtime ) )
	{
		return m_nBrightness;
	}

	//Get our percentage
	float timeDelta = ( gpGlobals->curtime - m_flBrightnessTimeStart ) / m_flBrightnessTime;

	float brightness = ( (float) m_nStartBrightness + ( (float) ( m_nDestBrightness - m_nStartBrightness  ) * timeDelta ) );

	//Return the result
	return (int) brightness;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSprite::OnDataChanged( DataUpdateType_t updateType )
{
	BaseClass::OnDataChanged( updateType );

	// Only think when sapping
	SetNextClientThink( CLIENT_THINK_ALWAYS );
	if ( updateType == DATA_UPDATE_CREATED )
	{
		m_flStartScale = m_flDestScale = m_flSpriteScale;
		m_nStartBrightness = m_nDestBrightness = m_nBrightness;
	}

	UpdateVisibility();
}

void CSprite::ClientThink( void )
{
	BaseClass::ClientThink();

	// Module render colors over time
	if ( m_flSpriteScale != m_flDestScale )
	{
		m_flStartScale		= m_flDestScale;
		m_flDestScale		= m_flSpriteScale;
		m_flScaleTimeStart	= gpGlobals->curtime;
	}

	if ( m_nBrightness != m_nDestBrightness )
	{
		m_nStartBrightness		= m_nDestBrightness;
		m_nDestBrightness		= m_nBrightness;
		m_flBrightnessTimeStart = gpGlobals->curtime;
	}
}

extern bool g_bRenderingScreenshot;
extern ConVar r_drawviewmodel;

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : flags - 
// Output : int
//-----------------------------------------------------------------------------
int CSprite::DrawModel( int flags )
{
	VPROF_BUDGET( "CSprite::DrawModel", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
	//See if we should draw
	if ( !IsVisible() || ( m_bReadyToDraw == false ) )
		return 0;

#ifdef PORTAL
	if ( ( !g_pPortalRender->IsRenderingPortal() && !m_bDrawInMainRender ) || 
		( g_pPortalRender->IsRenderingPortal() && !m_bDrawInPortalRender ) )
	{
		return 0;
	}
#endif //#ifdef PORTAL

	// Tracker 16432:  If rendering a savegame screenshot then don't draw sprites 
	//   who have viewmodels as their moveparent
	if ( g_bRenderingScreenshot || !r_drawviewmodel.GetBool() )
	{
		C_BaseViewModel *vm = dynamic_cast< C_BaseViewModel * >( GetMoveParent() );
		if ( vm )
		{
			return 0;
		}
	}

	//Must be a sprite
	if ( modelinfo->GetModelType( GetModel() ) != mod_sprite )
	{
		Assert( 0 );
		return 0;
	}

	float renderscale = GetRenderScale();
	if ( m_bWorldSpaceScale )
	{
		CEngineSprite *psprite = ( CEngineSprite * )modelinfo->GetModelExtraData( GetModel() );
		float flMinSize = MIN( psprite->GetWidth(), psprite->GetHeight() );
		renderscale /= flMinSize;
	}

	//Draw it
	int drawn = DrawSprite( 
		this,
		GetModel(), 
		GetAbsOrigin(), 
		GetAbsAngles(), 
		m_flFrame,				// sprite frame to render
		m_hAttachedToEntity,	// attach to
		m_nAttachment,			// attachment point
		GetRenderMode(),		// rendermode
		m_nRenderFX,
		GetRenderBrightness(),	// alpha
		m_clrRender->r,
		m_clrRender->g,
		m_clrRender->b,
		renderscale,			// sprite scale
		GetHDRColorScale()		// HDR Color Scale
		);

	return drawn;
}


const Vector& CSprite::GetRenderOrigin()
{
	static Vector vOrigin;
	vOrigin = GetAbsOrigin();

	if ( m_hAttachedToEntity )
	{
		C_BaseEntity *ent = m_hAttachedToEntity->GetBaseEntity();
		if ( ent )
		{
			QAngle dummyAngles;
			ent->GetAttachment( m_nAttachment, vOrigin, dummyAngles );
		}
	}

	return vOrigin;
}

#endif

//-----------------------------------------------------------------------------
// Purpose: oriented sprites
//			CSprites swap the roll and yaw angle inputs, and rotate the yaw 180 degrees
//-----------------------------------------------------------------------------

#if !defined( CLIENT_DLL )
IMPLEMENT_SERVERCLASS_ST( CSpriteOriented, DT_SpriteOriented )
END_SEND_TABLE()
#else
#undef CSpriteOriented
IMPLEMENT_CLIENTCLASS_DT(C_SpriteOriented, DT_SpriteOriented, CSpriteOriented)
#define CSpriteOriented C_SpriteOriented
END_RECV_TABLE()
#endif

#if !defined( CLIENT_DLL )

void CSpriteOriented::Spawn( void )
{
	// save a copy of the angles, CSprite swaps the yaw and roll
	QAngle angles = GetAbsAngles();
	BaseClass::Spawn();
	// ORIENTED sprites "forward" vector points in the players "view" direction, not the direction "out" from the sprite (gah)
	angles.y = anglemod( angles.y + 180 );
	SetAbsAngles( angles );
}

#else

bool CSpriteOriented::IsTransparent( void )
{
	return true;
}

#endif
