
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_CalculateSurfaceReorientationNormal_EnvCache.cpp
// Author : Andy Tate
// Date   : 5-Feb-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Physics/AVP_CalculateSurfaceReorientationNormal_EnvCache.h"

// Asura independent header-files...
#include "Debugging/Asura_DebugLineRender.h"
#include "Dynamics/Asura_Collision_Object.h"
#include "Physics/Asura_Physics_CCD.h"
#include "Physics/Asura_Physics_CollisionMesh.h"
#include "Physics/Asura_Physics_Object.h"
#include "Physics/Asura_Physics_RigidBody.h"
#include "Physics/Asura_Physics_RigidBodyDefinition.h"

// Game independent header-files...
#include "Entity/PhysicalObject/AVP_ClientEntity_PhysicalObject.h"

#if defined(ASURA_TOOLS)
// Tools header-files...
#include "Misc/Tools_MessageLog.h"
#endif	//defined(ASURA_TOOLS)


////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

// Limit on the number of normals the system can cope with...
static const u_int	g_uMaxNormals	= 8;

#if !defined(ASURA_NO_DEBUG_OUTPUT)
DEBUGVAR bool	dbg_bDebugRenderOBB	= false;	//true;	//
#endif	//!defined(ASURA_NO_DEBUG_OUTPUT)


////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalAndVelocityForTriangle
// Author       : Andy Tate
// Date         : 6-Feb-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalAndVelocityForTriangle(const Asura_Physics_Tri& xTriangle,
																						  Asura_AVector3_InParam xInputVelocity)
{
	Asura_AVector3	xTriangleNormal;

	// Update the normal using this triangle...
	if(CalculateTriangleNormal(xTriangle, m_xPhysicsOBB, m_xQueryOBB, xInputVelocity, xTriangleNormal))
	{
		const u_int		uNumNormalsUsed	= GetNumNormalsUsed();

		Asura_AVector3	xVelocity;

		// See if we've already got this surface-normal...
		for(u_int u = 0; u < uNumNormalsUsed; u++)
		{
			Asura_AVector3	xNormal(Asura_Maths::ZeroAVector3);

			GetNormal(u, xNormal);

			if(xTriangleNormal.ApproximatelyEquals(xNormal))
			{
				return;
			}
		}

		// Update the velocity now that we know that the normal is unique...
		if(CalculateVelocityAlongTriangle(xTriangle, xInputVelocity, xVelocity))
		{
			m_xDesiredVelocity	+= xVelocity;
		}

		// We've found a new surface-normal - attempt to store it...
		if(!AddNormal(xTriangleNormal))
		{
#if defined(ASURA_ASSERT)	// || defined(ASURA_TOOLS)
			const Asura_AVector3	xCentre(xTriangle.GetCentre());
#endif	//defined(ASURA_ASSERT) || defined(ASURA_TOOLS)

			Asura_Assertf(false, ("Run out of space to store normals for physics-object handler.\nTriangle at (%.3f, %.3f, %.3f) will be rejected.", xCentre.GetX(), xCentre.GetY(), xCentre.GetZ()));

#if 0	//defined(ASURA_TOOLS)
			{
				const char*	szCat		= "Physics";
				const char*	szSubCat	= "Reorientation";

				Tools_MessageLog::ShowOverlay(szCat, szSubCat);
				Tools_MessageLog::AddMessage(szCat, szSubCat, "Run out of space to store normals at (%.3f, %.3f, %.3f)", xCentre.GetX(), xCentre.GetY(), xCentre.GetZ());
			}
#endif	//defined(ASURA_TOOLS)
		}
	}
}

/// End of function 'AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalAndVelocityForTriangle'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalForEnvironment
// Author       : Andy Tate
// Date         : 9-Aug-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalForEnvironment(const Asura_Physics_Object& xPhysicsObject,
																				  Asura_AVector3_InParam xInputVelocity)
{
	const Asura_Bounding_Box&					xPhysicsBB	= xPhysicsObject.GetBoundingBox();

	const Asura_Physics_EnvironmentCache_Face*	pxFace;

	// Update the environment-cache...
	m_xEnvironmentCache.Update(xPhysicsBB, 1.000f);
	pxFace	= m_xEnvironmentCache.GetFaceList();

	// Sum the normals of all possible "floor" polys...
	while(pxFace != NULL)
	{
		const Asura_Physics_Tri	xTriangle(pxFace->m_uFaceID);

		// Update our data...
		UpdateNormalAndVelocityForTriangle(xTriangle, xInputVelocity);

		// Try the next face...
		pxFace	= pxFace->m_pxNext;
	}
}

/// End of function 'AVP_PhysicsObjectHandler::UpdateSurfaceNormalForEnvironment'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalForObject
// Author       : Andy Tate
// Date         : 9-Aug-2007
////////////////////////////////////////////////////////////////////////////////

void AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalForObject(const Asura_Physics_Object& xPhysicsObject,
																			 Asura_AVector3_InParam xInputVelocity)
{
	const Asura_Physics_ObjectCollisionReport*	pxObjectCollisionReport	= xPhysicsObject.GetObjectCollisionReportList();

	while(pxObjectCollisionReport != NULL)
	{
		const Asura_Handle						iHandle					= pxObjectCollisionReport->GetPhysicsHandle();
		const Asura_Physics_RigidBodyInstance*	pxRigidBodyInstance		= Asura_Physics_RigidBodyInstance::GetInstancePointer(iHandle);

		if(pxRigidBodyInstance != NULL)
		{
			const Asura_Guid uGuid = pxRigidBodyInstance->GetAssociatedEntityGuid();
			
			// Check physical objects and containers.
			const bool bCanUseForSRO = AVP_ClientEntity_PhysicalObject::CanUseForSurfaceReorientation(uGuid);
			if (bCanUseForSRO)
			{
				float			fPenetration;
				Asura_AVector3	xAxis;

				// Do our 2 objects overlap?
				if(Asura_Physics_CCD::FindPenetration_OBBVsOBB(m_xQueryOBB, m_xPhysicsOBB, fPenetration, xAxis))
				{
					const Asura_Physics_RigidBodyDefinition*	pxRigidBodyDefinition	= pxRigidBodyInstance->GetDefinition();

					if(pxRigidBodyDefinition != NULL)
					{
						const Asura_CollisionMesh*	pxCollisionMesh	= pxRigidBodyDefinition->GetCollisionMesh();

						Asura_Physics_Tri			xTriangle;

						// Do we have a collision-mesh?
						if(pxCollisionMesh == NULL)
						{
							Asura_Physics_CollisionOBB_Iterator	xIterator(m_xPhysicsOBB);

							// Create the triangles which make up this OBB...
							for(xIterator.AccessFirstTri(); xIterator.GetCurrentTri(xTriangle); xIterator.AccessNextTri())
							{
								UpdateNormalAndVelocityForTriangle(xTriangle, xInputVelocity);
							}
						}
						else
						{
							Asura_Physics_CollisionMesh_Iterator	xIterator(pxCollisionMesh);

							// Create the triangles which make up this mesh...
							for(xIterator.AccessFirstTri(); xIterator.GetCurrentTri(xTriangle); xIterator.AccessNextTri())
							{
								UpdateNormalAndVelocityForTriangle(xTriangle, xInputVelocity);
							}
						}
					}
				}
			}
		}

		// Try the next object...
		pxObjectCollisionReport	= pxObjectCollisionReport->GetNextReport();
	}
}

/// End of function 'AVP_CalculateSurfaceReorientationNormal_EnvCache::UpdateNormalForObject'. ///



////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_CalculateSurfaceReorientationNormal_EnvCache::AVP_CalculateSurfaceReorientationNormal_EnvCache
// Author       : Andy Tate
// Date         : 6-Feb-2008
////////////////////////////////////////////////////////////////////////////////

AVP_CalculateSurfaceReorientationNormal_EnvCache::AVP_CalculateSurfaceReorientationNormal_EnvCache(const float fCosLimit,
																								   const u_short usPolyFlagsToIgnore)
: PARENT(fCosLimit, g_uMaxNormals)
{
	// Ensure that we ignore the polys we're supposed to...
	m_xEnvironmentCache.SetFlagsOfPolysToIgnore(usPolyFlagsToIgnore);
}

/// End of function 'AVP_CalculateSurfaceReorientationNormal_EnvCache::AVP_CalculateSurfaceReorientationNormal_EnvCache'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_CalculateSurfaceReorientationNormal_EnvCache::Update
// Author       : Andy Tate
// Date         : 5-Feb-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_CalculateSurfaceReorientationNormal_EnvCache::Update(Asura_Physics_Object& xPhysicsObject,
															  Asura_AVector3_InParam xInputVelocity,
															  Asura_AVector3_InParam xInputNormal)
{
	const Asura_Collision_Object*	pxCollisionObject	= xPhysicsObject.GetCollisionObject();
	const Asura_Bounding_Box		xCollisionObjectBB	= pxCollisionObject->GetBoundingBox();

	Asura_Bounding_Box				xTestBB;

	// Call the parent version first...
	PARENT::Update(xPhysicsObject, xInputVelocity, xInputNormal);

	// Ensure our OBB is slightly bigger than the actual BB to ensure that we pick-up
	// any faces we should be touching both in-front and below the actor...
	xTestBB.MinX	= xCollisionObjectBB.MinX - 0.025f;
	xTestBB.MaxX	= xCollisionObjectBB.MaxX + 0.025f;
	xTestBB.MinY	= xCollisionObjectBB.MaxY - 0.100f;
	xTestBB.MaxY	= xCollisionObjectBB.MaxY + 0.250f;
	xTestBB.MinZ	= xCollisionObjectBB.MinZ - 0.025f;
	xTestBB.MaxZ	= xCollisionObjectBB.MaxZ + 0.025f;

	m_xPhysicsOBB	= Asura_OBB(xCollisionObjectBB, m_xCurrentPosition, m_xCurrentOrientation);	//pxRigidBodyInstance->GetHalfExtents(), pxRigidBodyInstance->GetCentreOfMass(), pxRigidBodyInstance->GetOrientation());
	m_xQueryOBB		= Asura_OBB(xTestBB, m_xCurrentPosition, m_xCurrentOrientation);

#if !defined(ASURA_NO_DEBUG_OUTPUT)
	if(dbg_bDebugRenderOBB)
	{
		// Render the OBBs used...
		Asura_DebugLineRender::AddOBB(m_xPhysicsOBB, 0x80FFFF00);
		Asura_DebugLineRender::AddOBB(m_xQueryOBB, 0x80FFFFFF);
	}
#endif	// !defined(ASURA_NO_DEBUG_OUTPUT)

	// Test for both environment surfaces AND objects...
	UpdateNormalForEnvironment(xPhysicsObject, m_xCurrentVelocityPerpendicularToNormal);
	UpdateNormalForObject(xPhysicsObject, m_xCurrentVelocityPerpendicularToNormal);

	// Update the velocity...
	if(m_uNumNormalsUsed == 0 || m_xDesiredNormal.ApproximatelyEquals( Asura_Maths::ZeroAVector3 ))
	{
		m_xDesiredVelocity	= xInputVelocity;
	}
	else
	{
		m_xDesiredNormal.Normalise();

		if(m_uNumNormalsUsed == 1)
		{
			// Only re-apply gravity if we're hitting a single plane...
			m_xDesiredVelocity	+= m_xCurrentVelocityAlongNormal;
		}
		else
		{
			const float	fSpeed	= m_xDesiredVelocity.Magnitude();

			// Re-scale the velocity...
			if(fSpeed > 0.0f)
			{
				const float	fScale	= m_xCurrentVelocityPerpendicularToNormal.Magnitude() / fSpeed;

				m_xDesiredVelocity	*= fScale;
			}
		}
	}
}

/// End of function 'AVP_CalculateSurfaceReorientationNormal_EnvCache::Update'. ///


/// End of file. ///
