//-----------------------------------------------------------------------------
// Purpose: Generates a view matrix based on our current yaw, pitch, and roll.
//			The view matrix does not consider FOV or clip plane distances.
//-----------------------------------------------------------------------------
void CCamera::BuildViewMatrix()
{
	// The camera transformation is produced by multiplying roll * yaw * pitch.
	// This will transform a point from world space into quake camera space, 
	// which is exactly what we want for our view matrix. However, quake
	// camera space isn't the same as material system camera space, so
	// we're going to have to apply a transformation that goes from quake
	// camera space to material system camera space.
	
	CameraIdentityMatrix( m_ViewMatrix );

	RotateAroundAxis(m_ViewMatrix, m_fPitch, 0 );
	RotateAroundAxis(m_ViewMatrix, m_fRoll, 1);
	RotateAroundAxis(m_ViewMatrix, m_fYaw, 2);

	// Translate the viewpoint to the world origin.
	VMatrix TempMatrix;
	TempMatrix.Identity();
	TempMatrix.SetTranslation( -m_ViewPoint );

	m_ViewMatrix = m_ViewMatrix * TempMatrix;

	m_ViewProjMatrix = m_ProjMatrix * m_ViewMatrix;
	m_ViewProjMatrix.InverseGeneral( m_InvViewProjMatrix );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFArrowPanel::Paint()
{
	if ( !m_hEntity.Get() )
		return;

	C_BaseEntity *pEnt = m_hEntity.Get();
	IMaterial *pMaterial = m_NeutralMaterial;

	C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();

	// figure out what material we need to use
	if ( pEnt->GetTeamNumber() == TF_TEAM_RED )
	{
		pMaterial = m_RedMaterial;

		if ( pLocalPlayer && ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) )
		{
			// is our target a player?
			C_BaseEntity *pTargetEnt = pLocalPlayer->GetObserverTarget();
			if ( pTargetEnt && pTargetEnt->IsPlayer() )
			{
				// does our target have the flag and are they carrying the flag we're currently drawing?
				C_TFPlayer *pTarget = static_cast< C_TFPlayer* >( pTargetEnt );
				if ( pTarget->HasTheFlag() && ( pTarget->GetItem() == pEnt ) )
				{
					pMaterial = m_RedMaterialNoArrow;
				}
			}
		}
	}
	else if ( pEnt->GetTeamNumber() == TF_TEAM_BLUE )
	{
		pMaterial = m_BlueMaterial;

		if ( pLocalPlayer && ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) )
		{
			// is our target a player?
			C_BaseEntity *pTargetEnt = pLocalPlayer->GetObserverTarget();
			if ( pTargetEnt && pTargetEnt->IsPlayer() )
			{
				// does our target have the flag and are they carrying the flag we're currently drawing?
				C_TFPlayer *pTarget = static_cast< C_TFPlayer* >( pTargetEnt );
				if ( pTarget->HasTheFlag() && ( pTarget->GetItem() == pEnt ) )
				{
					pMaterial = m_BlueMaterialNoArrow;
				}
			}
		}
	}
	else if (pEnt->GetTeamNumber() == TF_TEAM_GREEN)
	{
		pMaterial = m_GreenMaterial;

		if (pLocalPlayer && (pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE))
		{
			// is our target a player?
			C_BaseEntity *pTargetEnt = pLocalPlayer->GetObserverTarget();
			if (pTargetEnt && pTargetEnt->IsPlayer())
			{
				// does our target have the flag and are they carrying the flag we're currently drawing?
				C_TFPlayer *pTarget = static_cast< C_TFPlayer* >(pTargetEnt);
				if (pTarget->HasTheFlag() && (pTarget->GetItem() == pEnt))
				{
					pMaterial = m_GreenMaterialNoArrow;
				}
			}
		}
	}
	else if (pEnt->GetTeamNumber() == TF_TEAM_YELLOW)
	{
		pMaterial = m_YellowMaterial;

		if (pLocalPlayer && (pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE))
		{
			// is our target a player?
			C_BaseEntity *pTargetEnt = pLocalPlayer->GetObserverTarget();
			if (pTargetEnt && pTargetEnt->IsPlayer())
			{
				// does our target have the flag and are they carrying the flag we're currently drawing?
				C_TFPlayer *pTarget = static_cast< C_TFPlayer* >(pTargetEnt);
				if (pTarget->HasTheFlag() && (pTarget->GetItem() == pEnt))
				{
					pMaterial = m_YellowMaterialNoArrow;
				}
			}
		}
	}

	int x = 0;
	int y = 0;
	ipanel()->GetAbsPos( GetVPanel(), x, y );
	int nWidth = GetWide();
	int nHeight = GetTall();

	CMatRenderContextPtr pRenderContext( materials );
	pRenderContext->MatrixMode( MATERIAL_MODEL );
	pRenderContext->PushMatrix(); 

	VMatrix panelRotation;
	panelRotation.Identity();
	MatrixBuildRotationAboutAxis( panelRotation, Vector( 0, 0, 1 ), GetAngleRotation() );
//	MatrixRotate( panelRotation, Vector( 1, 0, 0 ), 5 );
	panelRotation.SetTranslation( Vector( x + nWidth/2, y + nHeight/2, 0 ) );
	pRenderContext->LoadMatrix( panelRotation );

	IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );

	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );

	meshBuilder.TexCoord2f( 0, 0, 0 );
	meshBuilder.Position3f( -nWidth/2, -nHeight/2, 0 );
	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.TexCoord2f( 0, 1, 0 );
	meshBuilder.Position3f( nWidth/2, -nHeight/2, 0 );
	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.TexCoord2f( 0, 1, 1 );
	meshBuilder.Position3f( nWidth/2, nHeight/2, 0 );
	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.TexCoord2f( 0, 0, 1 );
	meshBuilder.Position3f( -nWidth/2, nHeight/2, 0 );
	meshBuilder.Color4ub( 255, 255, 255, 255 );
	meshBuilder.AdvanceVertex();

	meshBuilder.End();

	pMesh->Draw();
	pRenderContext->PopMatrix();
}
//-----------------------------------------------------------------------------
// Got a detail! Place it on the surface...
//-----------------------------------------------------------------------------
// BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
// (only when not in the debugger?)
// Printing the values of normal at the bottom of the function fixes it as does
// disabling global optimizations.
static void PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
{
	// But only place it on the surface if it meets the angle constraints...
	float cosAngle = normal.z;

	// Never emit if the angle's too steep
	if (cosAngle < model.m_MaxCosAngle)
		return;

	// If it's between min + max, flip a coin...
	if (cosAngle < model.m_MinCosAngle)
	{
		float probability = (cosAngle - model.m_MaxCosAngle) / 
			(model.m_MinCosAngle - model.m_MaxCosAngle);

		float t = rand() / (float)RAND_MAX;
		if (t > probability)
			return;
	}

	// Compute the orientation of the detail
	QAngle angles;
	if (model.m_Flags & MODELFLAG_UPRIGHT)
	{
		// If it's upright, we just select a random yaw
		angles.Init( 0, 360.0f * rand() / (float)RAND_MAX, 0.0f );
	}
	else
	{
		// It's not upright, so it must conform to the ground. Choose
		// a random orientation based on the surface normal

		Vector zaxis;
		VectorCopy( normal, zaxis );
		VectorNormalize( zaxis );

		// Choose any two arbitrary axes which are perpendicular to the normal
		Vector xaxis( 1, 0, 0 );
		if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
			xaxis.Init( 0, 1, 0 );
		Vector yaxis;
		CrossProduct( zaxis, xaxis, yaxis );
		VectorNormalize( yaxis );
		CrossProduct( yaxis, zaxis, xaxis );
		VectorNormalize( xaxis );
		VMatrix matrix;
		matrix.SetBasisVectors( xaxis, yaxis, zaxis );
		matrix.SetTranslation( vec3_origin );

		float rotAngle = 360.0f * rand() / (float)RAND_MAX;
		VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
		matrix = matrix * rot;

		MatrixToAngles( matrix, angles );
	}

	// FIXME: We may also want a purely random rotation too

	// Insert an element into the object dictionary if it aint there...
	switch ( model.m_Type )
	{
	case DETAIL_PROP_TYPE_MODEL:
		AddDetailToLump( model.m_ModelName.String(), pt, angles, model.m_Orientation );
		break;

	// Sprites and procedural models made from sprites
	case DETAIL_PROP_TYPE_SPRITE:
	default:
		{
			float flScale = 1.0f;
			if ( model.m_flRandomScaleStdDev != 0.0f ) 
			{
				flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
			}

			AddDetailSpriteToLump( pt, angles, model, flScale );
		}
		break;
	}
}