
///////////////////////////////////////////////////////////////////////////////
//
// File		: AVP_Actor_CameraHandler.cpp
// Author	: Andy Tate
// Modified	: Paul Humphries
// Date		: 23-Feb-2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif   // ifdef ASURA_USE_PRAGMA_ONCE

#ifndef __AVP_ACTOR_CAMERAHANDLER_H__
#define __AVP_ACTOR_CAMERAHANDLER_H__

// Asura files
#include "Core/Asura.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_List.h"
#include "Network/Asura_Guid.h"

// Project platform-independent
#include "Actor/AVP_Actor_VelocityInterceptor.h"

const u_int AVP_CAMERA_NUM_ZOOM_LEVELS = 4;		// number of zoom levels for the first person camera

// Forward declarations
class AVP_Actor_CameraHandler;
class AVP_ClientEntity_Actor;

struct AVP_Actor_CameraMove
{
public:
	float			m_fThreshold;
	Asura_Vector_3	m_xOffset;
	Asura_Euler_3	m_xEuler;
	float			m_fOffsetDuration;
	float			m_fEulerDuration;
};

class AVP_Actor_CameraMovement
{
public:
	AVP_Actor_VelocityInterceptor <Asura_Vector_3>	m_xRelativeOffset;
	AVP_Actor_VelocityInterceptor <Asura_Euler_3>	m_xRelativeEuler;

	AVP_Actor_CameraMovement();
	AVP_Actor_CameraMovement(const AVP_Actor_CameraMove& xMove);

	void Reset(const AVP_Actor_CameraMove& xMove)
	{
		m_xRelativeOffset.Reset(xMove.m_xOffset);
		m_xRelativeEuler.Reset(xMove.m_xEuler);
	}

	void AddMove(const AVP_Actor_CameraMove& xMove)
	{
		if (xMove.m_fThreshold == 0.0f)
		{
			m_xRelativeOffset.SetTarget(xMove.m_xOffset, xMove.m_fOffsetDuration);
			m_xRelativeEuler.SetTarget(xMove.m_xEuler, xMove.m_fEulerDuration);
		}
		else
		{
			m_xRelativeOffset.AddTarget(xMove.m_fThreshold, xMove.m_xOffset, xMove.m_fOffsetDuration);
			m_xRelativeEuler.AddTarget(xMove.m_fThreshold, xMove.m_xEuler, xMove.m_fEulerDuration);
		}
	}

	void AddMoveMirror(const AVP_Actor_CameraMove& xMove)
	{
		Asura_Vector_3 xOffset;
		Asura_Euler_3 xEuler;
		AVP_Maths::MirrorYZ(xMove.m_xOffset, xOffset);
		AVP_Maths::MirrorYZ(xMove.m_xEuler, xEuler);

		if (xMove.m_fThreshold == 0.0f)
		{
			m_xRelativeOffset.SetTarget(xOffset, xMove.m_fOffsetDuration);
			m_xRelativeEuler.SetTarget(xEuler, xMove.m_fEulerDuration);
		}
		else
		{
			m_xRelativeOffset.AddTarget(xMove.m_fThreshold, xOffset, xMove.m_fOffsetDuration);
			m_xRelativeEuler.AddTarget(xMove.m_fThreshold, xEuler, xMove.m_fEulerDuration);
		}
	}

	void GetValues(Asura_Vector_3& xVector, Asura_Euler_3& xEuler)
	{
		m_xRelativeOffset.CalculatePosition(xVector);
		m_xRelativeEuler.CalculatePosition(xEuler);
	}

	void AddTime(float fTimeSlice)
	{
		m_xRelativeOffset.AddTime(fTimeSlice);
		m_xRelativeEuler.AddTime(fTimeSlice);
	}
};


// Camera properties structure

struct AVP_Actor_CameraProperties
{
	float			m_fRadius;						// The camera is treated as a sphere (for collisions)
	float			m_fSpeed;						// The speed at which the camera moves out (after being pulled in behind walls etc)

	float			m_fDistanceOffset;				// The offset distance for the third person camera
	float			m_fDistanceReduceOffset;		// The distance below which the offset is reduced
	float			m_fFovThirdPerson;				// The field of view for the third person camera
	float			m_fDurationToFirst;				// The time taken to change between first and third person
	float			m_fDurationToThird;				// The time taken to change between first and third person
	float			m_fExpTurnToFirst;				// The character turn speed when changing to first person

	float			m_fRotationMaxAngularVelocity;	// The maximum angular velocity of the camera when rotating around the player
	float			m_fRotationHalfLife;			// The time taken for the angular velocity to half

	float			m_fDistanceHalfPlayerWidth;		// Half the player width used for the LOS tests which detect corners to move around
	float			m_fFactorClearCorner;			// The factor for ensuring we clear corners (should be around 1.01f)

	Asura_Vector_3	m_xOffsetHead;					// Vector from the actor position to the back of the head
	float			m_fDistanceFadeOutActor;		// The distance at which the actor starts to fade out

	float			m_fDistanceFollowInside;		// distance for camera to follow player inside
	float			m_fDistanceFollowOutside;		// distance for camera to follow player outside
	float			m_fDistanceMoveBack;			// distance for camera to move back from player in open space
	float			m_fDistanceLook;				// distance above which the camera looks at the player
	float			m_fDistanceGrappleCheck;		// distance for the grapple LOS tests to determine the camera move direction

	float			m_fExpFollowInside;				// factor for following the player inside
	float			m_fExpFollowOutside;			// factor for moving the camera backwards
	float			m_fExpCharacterHeight;			// factor for smoothing character height
	float			m_fExpCameraHeight;				// factor for smoothing camera height
	float			m_fExpWallEvade;				// factor for moving the camera around walls
	float			m_fExpLock;						// factor for moving the camera behind the player when lock is held
	float			m_fExpInactive;					// factor for moving the camera behind the player automatically after inactive period

	float			m_fDurationOutsideToInside;		// duration to change from outside to inside camera (and vice versa)
	float			m_fDurationInsideToOutside;		// duration after which the camera moves slowly behind the player automatically
	float			m_fDurationWallEvade;			// duration after which the camera gives up moving behind walls and is placed in front
/*
	AVP_Actor_CameraMove		m_xCameraOutside;				// Outside camera
	AVP_Actor_CameraMove		m_xCameraInside;				// Inside camera
	AVP_Actor_CameraMove		m_xCameraGrapple;				// Grapple camera
	AVP_Actor_CameraMove		m_xCameraGrapple1;				// Two-step grapple camera - step 1
	AVP_Actor_CameraMove		m_xCameraGrapple2;				// Two-step grapple camera - step 2
	AVP_Actor_CameraMove		m_xCameraLockOutside;			// Locked camera for outside
	AVP_Actor_CameraMove		m_xCameraLockInside;			// Locked camera for inside
*/
};

// Camera handler class

class AVP_Actor_CameraHandler
{
public:
	enum FovModifier
	{
		FOVMOD_ADS = 0,
		FOVMOD_SPRINT,
		MAX_FOVMOD
	};

	enum CameraType
	{
		CT_FIRST_PERSON,
		CT_THIRD_PERSON,
		CT_EXTERNAL,

		MAX_CAMERA_TYPES
	};

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
	AVP_Actor_CameraHandler						(const Asura_Guid uOwnerGuid, AVP_Actor_CameraProperties& xCameraProperties);
#else
	AVP_Actor_CameraHandler						(const Asura_Guid uOwnerGuid, const AVP_Actor_CameraProperties& xCameraProperties);
#endif

	virtual ~AVP_Actor_CameraHandler			();

	virtual void			Reset				(void);

	virtual void			SetAsuraCamera		(AVP_ClientEntity_Actor& xOwner);
	virtual	void			Update				(AVP_ClientEntity_Actor& xOwner);

	virtual void			SetCameraType		(const CameraType eType);

// 	void					SetInsideCamera		(bool bInside, bool bInstant = false);
// 	void					SetGrappleCamera	(bool bGrapple);
// 	void					SetLockedCamera		(bool bLocked);

	void					SetPosition			(const Asura_Vector_3& xPosition)		{ m_xCameraPosition = xPosition; }
	void					SetOrientation		(const Asura_Matrix_3x3& xOrientation)	{ m_xCameraOrientation = xOrientation; }
	void					SetCameraRotation	(float fAmount)							{ m_fCameraRotation = fAmount; }

	void					SetFirstPersonCameraPosition(const Asura_Vector_3& xPos)	{ m_xFirstPersonPosition = xPos; }

	void		OrientatePlayerRelativeToCamera	(float fDirectionZ, float fDirectionX, Asura_Euler_3& xPlayerOrientation);
	void		PlaceCameraBehindPlayer			(void);

	CameraType				GetCameraType		(void) const	{ return m_eCameraType; }

	const Asura_Vector_3&	GetPosition			(void) const	{ return m_xCameraActualPosition; }
	const Asura_Matrix_3x3&	GetOrientation		(void) const	{ return m_xCameraActualOrientation; }
	bool					GetInside			(void) const	{ return m_bInside; }
	bool					GetGrappling		(void) const	{ return m_bGrappling; }
	float					GetAlpha			(void) const	{ return m_fAlpha; }

	float					GetTransitionDone		(void)		{ return m_xFirstToThirdFactor.GetComplete(); }
	float					GetTransitionInProgress	(void)		{ return m_xFirstToThirdFactor.IsMoving(); }

	float					GetZoomScale		(void)			{ return Asura_Maths::Tangent(m_afFovZoom[m_uZoomLevel] / m_afFovZoom[0]); }

	void					SetRemovedClientFromRenderableList(bool bSet)		{ m_bRemovedClientFromRenderableList = bSet; }
	bool					GetRemovedClientFromRenderableList() const			{ return m_bRemovedClientFromRenderableList; }

	void					SetFOVModifier(float fMod, FovModifier eModiferType) { m_afFOVModifier[ eModiferType ] = fMod; }

#if defined(ASURA_SUPPORT_FLYCAM)
	static bool	GetFlyCam() { return s_bFlyCam; }
	static bool	GetFixCam() { return s_bFixCam; }
#endif	// defined(ASURA_SUPPORT_FLYCAM)

protected:

	virtual void UpdateCameraOrientation( AVP_ClientEntity_Actor& xOwner, Asura_Matrix_3x3& xOrientation ) {}
	virtual void RefreshBlueprintData(void);

	Asura_Guid			m_uOwnerGuid;						// The GUID of the actor this camera handler is for.
	CameraType			m_eCameraType;						// The type of camera to use.
public:
	Asura_Angle			m_fCameraFieldOfViewFirstPerson;	// The current field-of-view for the first person camera.
protected:
	Asura_Matrix_3x3	m_xCameraOrientation;				// The current orientation of the camera.
	Asura_Matrix_3x3	m_xCameraOrientationThirdToFirst;	// Orientation saved here at beginning of transition to first person.
	Asura_Vector_3		m_xCameraPosition;					// The current position of the camera.
	u_int				m_uZoomLevel;
	float				m_afFOVModifier[ MAX_FOVMOD ];

	float				m_fWallDuration;
	float				m_fWallExp;

	AVP_Actor_CameraProperties&	m_xCameraProperties;	// Constant properties for the actor's camera.

	float m_afFovZoom[AVP_CAMERA_NUM_ZOOM_LEVELS];
	float m_fZoomSpeed;

private:

	union
	{
		u_int	m_uFlags;
		struct
		{
			u_int	m_bInside			: 1,			// Are we using inside camera? (or outside)
					m_bGrappling		: 1,			// Are we using the grapple camera?
					m_bLocked			: 1,			// Are we locked on to a target?
										: 0;			// 30 spare-bits
		};
	};

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE)
	static int	s_iConsoleVarsCount;
#endif	// defined(ASURA_SUPPORT_COMMAND_CONSOLE)

	AVP_Actor_VelocityInterceptor <float>		m_xFirstToThirdFactor;		// Transition between first and third person
	AVP_Actor_VelocityInterceptor <float>		m_xInsideOutside;			// Transition between inside and outside camera distances

	Asura_Vector_3								m_xCameraActualPosition;	// The current camera position
	Asura_Matrix_3x3							m_xCameraActualOrientation;	// The current camera orientation

	float										m_fCameraDistance;			// 3rd person camera distance
	float										m_fCameraRotation;			// 3rd person current rotation speed around the player
	float										m_fCameraRotationSmoothed;	// 3rd person smoothed rotation speed around the player
	float										m_fAlpha;					// 3rd person alpha value

	Asura_Vector_3								m_xFirstPersonPosition;		// First person camera position
	bool										m_bRemovedClientFromRenderableList;		// Whether we removed client from renderable list

	Asura_Vector_3								m_xPlayerPositionSmoothed;	// The smoothed player position
	Asura_Vector_3								m_xFromPlayerTarget;		// The target vector from the player to the camera

	//AVP_Actor_CameraMovement					m_xCameraMovement;			// Camera movement - may be used for grapple camera

#if defined(ASURA_SUPPORT_COMMAND_CONSOLE) 
	float										m_fDistanceFollowInsideLast;	// For checking if distance has changed via DVS
	float										m_fDistanceFollowOutsideLast;	// For checking if distance has changed via DVS
#endif

	float	MovementQuery					(const Asura_Vector_3& xStart, Asura_Vector_3 xDirection, float fMaximumDistance, float fRadius);
	float	LineOfSight						(const Asura_Vector_3& xStart, Asura_Vector_3 xDirection, float fMaximumDistance, Asura_Vector_3& xNormal);
// 	bool	IsMoreSpacePerpendicularLeft	(const AVP_ClientEntity_Actor& xPlayer);

#if defined(ASURA_SUPPORT_FLYCAM)
	static bool s_bFlyCam;							// are we in fly cam mode?
	static bool s_bFixCam;							// are we in fix cam mode?
#endif	// defined(ASURA_SUPPORT_FLYCAM)
};


#endif   // ifndef __AVP_ACTOR_CAMERAHANDLER_H__



/// End of file. ///


