/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2004.
-------------------------------------------------------------------------
$Id$
$DateTime$
Description: Directs camera shots, as requested by the CFilmDirector.
-------------------------------------------------------------------------
History:
- 30:6:2005: Created by Nick Hesketh

*************************************************************************/

#include "StdAfx.h"

#include ".\FilmCamera.h"

#include ".\FilmDirector.h"

#include "Player.h"
#include "G4Player.h"
#include "Game.h"
#include "Game04.h"
#include "GameUtils.h"

#include <IViewSystem.h>
#include <IItemSystem.h>
#include <IPhysics.h>
#include <ICryAnimation.h>
#include "IAISystem.h"
#include "IAgent.h"
#include <IVehicleSystem.h>
#include <ISerialize.h>
#include "AutoCombat.h"


#include <IRenderAuxGeom.h>

#include <IGameTokens.h>

#include <cry_camera.h>

//#define LIMIT_LOOKAROUND
//#define LIMIT_LOOKAROUND_ANGLEDEG  (120)

#define RWI             (rwi_ignore_noncolliding |  rwi_stop_at_pierceable)

#define HEAD_RADIUS     0.25f
#define TORSO_RADIUS    0.75f
#define LEGS_RADIUS     0.75f

#define ECU_RADIUS      (HEAD_RADIUS*0.7f)
#define CU_RADIUS       HEAD_RADIUS
#define MCU_RADIUS      (2*CU_RADIUS)
#define MED_RADIUS      (1.5f*MCU_RADIUS)
#define FULL_RADIUS     (HEAD_RADIUS+TORSO_RADIUS+LEGS_RADIUS)


//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------

static ColorB clrRed(255,64,64,128);
static ColorB clrDkRed(128,0,0,128);
static ColorB clrGreen(64,255,64,128);
static ColorB clrDkGreen(0,128,0,128);
static ColorB clrBlue(64,64,255,128);
static ColorB clrDkBlue(0,0,128,128);
static ColorB clrYellow(255,255,64,128);
static ColorB clrDkYellow(128,128,0,128);
static ColorB clrCyan(64,255,255,128);
static ColorB clrDkCyan(0,128,128,128);
static ColorB clrMagenta(255,64,255,128);
static ColorB clrDkMagenta(128,0,128,128);
static ColorB clrOrange(255,128,32,128);
static ColorB clrDkOrange(128,64,0,128);

static ColorB clrDkGrey(64,64,64,128);
static ColorB clrGrey(192,192,192,128);

static ColorF clrGreyF(0.75f,0.75f,0.75f);
static ColorF clrRedGreyF(0.80f,0.60f,0.60f);

//static ColorF clrCinematicIntensity(0.80f,0.20f,0.10f);
static ColorF clrCinematicIntensity(0.50f,0.50f,0.50f);

static bool gbEnableEffects=true;
static bool bCamConsoleDebugDraw=false;
bool bCamDebugDraw=false;
static bool bSavedDebugDraw=false;


static void DrawSphere(Vec3 vPos,float radius, ColorB &rColor=clrGreen)
{
  if(bCamDebugDraw || bCamConsoleDebugDraw)
  {
//    IRenderAuxGeom* pRenderAuxGeom( GetISystem()->GetIRenderer()->GetIRenderAuxGeom() ); 
//    pRenderAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
//    pRenderAuxGeom->DrawSphere( vPos, radius, rColor);
  }
}

static void DrawLineV1V2(Vec3 vFrom,Vec3 vTo,ColorB &rColorTo=clrOrange,ColorB &rColorFrom=clrDkGrey)
{
  if(bCamDebugDraw || bCamConsoleDebugDraw)
  {
//    IRenderAuxGeom* pRenderAuxGeom( GetISystem()->GetIRenderer()->GetIRenderAuxGeom() ); 
//    pRenderAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
//    pRenderAuxGeom->DrawLine( vFrom, rColorFrom, vTo, rColorTo );
  }
}

static void DrawLineV1Dir(Vec3 vFrom,Vec3 vDir,float length=20,ColorB &rColorTo=clrOrange,ColorB &rColorFrom=clrDkGrey)
{
  if(bCamDebugDraw || bCamConsoleDebugDraw)
  {
//    IRenderAuxGeom* pRenderAuxGeom( GetISystem()->GetIRenderer()->GetIRenderAuxGeom() ); 
//    pRenderAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
//    pRenderAuxGeom->DrawLine( vFrom, rColorFrom, vFrom+vDir*length, rColorTo );
  }
}

struct SDofState
{
  float amount;
  float focusMin;
  float focusMax;
  float limit;
  float active;
};

// If DOF worked suitably we'd have close, middle and distant focal distances with: low, med, high and extreme DOF strengths.
// DOF doesn't have a good near focus, so keep amount low for near and mid cameras (most of our cams)
//                          {amt,     min,        max,        lim,        act}
const SDofState gDofNone=   {0.0f,    0.0f,       50.0f,      300.0f,     0.0f};
const SDofState gDofClose=  {0.25f,   0.0f,       50.0f,      300.0f,     1.0f};
const SDofState gDofMedium= {0.25f,   0.0f,       50.0f,      300.0f,     1.0f};
const SDofState gDofDistant={0.7f,    10.0f,      500.0f,     800.0f,     1.0f};

bool gbDofTickState=false;
SDofState gDofTickState;

static void GetDOF(SDofState &state)
{
  GetISystem()->GetI3DEngine()->GetPostProcessFxParam("Dof_BlurAmount", state.amount);
  GetISystem()->GetI3DEngine()->GetPostProcessFxParam("Dof_FocusMin", state.focusMin);
  GetISystem()->GetI3DEngine()->GetPostProcessFxParam("Dof_FocusMax", state.focusMax);
  GetISystem()->GetI3DEngine()->GetPostProcessFxParam("Dof_FocusLimit", state.limit);
  GetISystem()->GetI3DEngine()->GetPostProcessFxParam("Dof_Active", state.active);
}

static void SetDOF(const SDofState &inState)
{
  SDofState state=inState;

  if(GetISystem()->GetIConsole()->GetCVar("cl_cam_dof_on")->GetIVal())
  {
    float dofScale=1.0f; //GetISystem()->GetIConsole()->GetCVar("cl_cam_dof_scale")->GetFVal();
    float active;

    state.amount*=dofScale;
    if(dofScale<1.0f)
    {
      state.focusMax*=(1-dofScale);
      state.limit*=(1-dofScale)*2;
      state.limit=max(state.limit,state.focusMax+250);
    }
    if(state.amount >1.0f) state.amount=1.0f;

    GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_BlurAmount", state.amount);
    GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_FocusMin", state.focusMin);
    GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_FocusMax", state.focusMax);
    GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_FocusLimit", state.limit);

    if((state.amount<0.01f) || (!gbEnableEffects))
      active=0;
    else
      active=state.active;

    //CryLogAlways(" Set DOF(%f) %f",active,state.amount);
    GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Dof_Active", active);

    gbDofTickState=true;
    GetDOF(gDofTickState);
  }
}

static void SetDOF(float amount,float focusMin,float focusMax,float limit)
{
  SDofState state;

  state.active=true;
  state.amount=amount;
  state.focusMin=focusMin;
  state.focusMax=focusMax;
  state.limit=limit;

  SetDOF(state);
}

void TickDOF()
{
  if(gbDofTickState)
  {
    SetDOF(gDofTickState);
  }
}

#define CAM_DOFNONE     0
#define CAM_DOFLOW      1
#define CAM_DOFMED      2
#define CAM_DOFHIGH     3
#define CAM_DOFEXTREME  4
#define CAM_DOFAUTO     5

static void SetDOF(int iStrength,float dist)
{
  const SDofState *pDof=&gDofNone;
  // If DOF worked suitably we'd have close, middle and distant focal distances with: low, med, high and extreme dof strengths
  switch(iStrength)
  {
  default:
  case CAM_DOFNONE:
    pDof=&gDofNone;
    break;
  case CAM_DOFAUTO:
  case CAM_DOFLOW:
    pDof=&gDofClose;
    break;
  case CAM_DOFMED:
    pDof=&gDofClose;
    break;
  case CAM_DOFHIGH:
    pDof=&gDofClose;
    break;
  case CAM_DOFEXTREME:
    pDof=&gDofClose;
    break;
  };
  SetDOF(*pDof);
}


static void SetDesat(float amount, ColorF clr)
{
  if(GetISystem()->GetIConsole()->GetCVar("cl_cam_desat_on")->GetIVal())
  {
    if((amount>0.01f) && (gbEnableEffects))
    {
      GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Test_Amount", amount);
      GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Test_Red", clr.r);
      GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Test_Green", clr.g);
      GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Test_Blue", clr.b);
      GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Test_Active", 1);
    }
    else
    {
      GetISystem()->GetI3DEngine()->SetPostProcessFxParam("Test_Active", 0.0f);
    }
  }

  //CryLogAlways(" Set Desat(%f) %f",amount>0.01f ? 1.0f :0.0f,amount);
}

// Some basic camera positions. temp implementation. will be data driven.
struct TCamCut
{
  float Dist;

  float vOff;
  float hOff;

  float roll;
  float pitch;
  float yaw;
  float intensity;
  int iSubject;
  int iDof;
  int iType;
};

static TCamCut editSaveCut;
static TCamCut *pEditActiveCut=0;
const char *psEditActiveCutName="none";
bool bEditRefresh=false;
bool bEditAutoRefresh=false;

// Helper so nums can be entered in same order they come from the console when editing.
#define CAM_DHPRVY(d,h,p,r,v,y,i,tgt,dof,typ)   { d,v,h,r,p,y,i,tgt,dof,typ }

#define CAM_ILOW      0.25f
#define CAM_IMED      0.5f
#define CAM_IHIGH     0.75f

#define CAM_PLAYER      1
#define CAM_PLAYERHEAD  2
#define CAM_PLAYERWEAP  3

#define CAM_GIRL				10
#define CAM_GIRLHEAD		11
#define CAM_GIRLWEAP		12

#define CAM_PLAYERGIRL			20
#define CAM_PLAYERGIRLCLOSE	21


#define CAM_TARGET      30
#define CAM_TARGETHEAD  31
#define CAM_TARGETWEAP  32


#define CAM_STATIC      1
#define CAM_PAN         2
#define CAM_FOLLOW      3

//----------------------------------------------------------------------------------------------------------------------

TCamCut aCamCutsDeath[]=
{
  // impact front mcu
  CAM_DHPRVY(-1.219f,0.1097f,-7.45f,0,-0.192f,4.725f,CAM_ILOW,CAM_TARGET,CAM_DOFMED,CAM_PAN),
    // impact high pov
    CAM_DHPRVY(-1.843f,0.18f,29.72f,0,-0.509f,12.95f,CAM_ILOW,CAM_TARGET,CAM_DOFMED,CAM_STATIC),
    // Medium front
    CAM_DHPRVY(-1.3865f,-0.2425f,-7.45f,0,-0.1926f,-24.8f,CAM_ILOW,CAM_TARGET,CAM_DOFMED,CAM_PAN)
    // Close over Player shoulder
    //CAM_DHPRVY(-0.511,-0.2425,-19.972f,0,-0.1239,-157.68,CAM_IMED,CAM_PLAYER,CAM_DOFNONE,CAM_STATIC)
};
int nCamCutsDeath=sizeof(aCamCutsDeath)/sizeof(aCamCutsDeath[0]);

TCamCut aCamCutsFire[]=
{
  // front medium (due to rifle)
  CAM_DHPRVY(-1.3865f,-0.2425f,-7.45f,0,-0.1926f,-24.8f,CAM_ILOW,CAM_PLAYER,CAM_DOFMED,CAM_STATIC),
    // close over shoulder
    CAM_DHPRVY(-0.511f,-0.2425f,-19.972f,0,-0.1239f,-157.68f,CAM_IMED,CAM_PLAYER,CAM_DOFNONE,CAM_STATIC)
};

int nCamCutsFire=sizeof(aCamCutsFire)/sizeof(aCamCutsFire[0]);

#define NAV_CAM     0
#define COM_CAM     1
#define COM_CAM2    2

extern TCamCut aCamCutsFollow[];
/*=
{
  // Nav cam - behind player
  CAM_DHPRVY(1.3f,-0.0632f,13.78f,0,-0.154f,13.19f,CAM_ILOW,CAM_PLAYER,CAM_DOFNONE,CAM_FOLLOW),
    // Combat cam - behind and to right of player
    CAM_DHPRVY(1.3f,-0.374f,0.949f,0,-0.0854f,17.597f,CAM_ILOW,CAM_PLAYER,CAM_DOFNONE,CAM_FOLLOW),
    // Extreme Close over shoulder
    CAM_DHPRVY(-0.511f,-0.2425f,-19.972f,0,-0.1239f,-157.68f,CAM_IMED,CAM_PLAYER,CAM_DOFNONE,CAM_FOLLOW)
};

int nCamCutsFollow=sizeof(aCamCutsFollow)/sizeof(aCamCutsFollow[0]);
*/
extern int nCamCutsFollow;

#define  SET_CamCut(pCut)                         \
{ thirdPersonDistance=pCut->Dist;                 \
  thirdPersonRoll=pCut->roll*(gf_PI/180.0f);      \
  thirdPersonPitch=pCut->pitch*(gf_PI/180.0f);    \
  thirdPersonYaw=pCut->yaw*(gf_PI/180.0f);        \
  thirdPersonVOff=pCut->vOff;                     \
  thirdPersonHOff=pCut->hOff; }

TCamCut aCamCuts[]=
{
  // Flat
  { 1.0f, 0.3f,0.0f, 0.0f,0.0f,0.0f },    // MCU
  { 1.0f, 0.3f,-0.2f, 0.0f,0.0f,0.0f },   // MCU Left of frame
  { 1.5f, -0.1f,0.0f, 0.0f,0.0f,0.0f },   // M
  { 1.5f, -0.1f,-0.3f, 0.0f,0.0f,0.0f },  // M Left of frame
  // Near Flat
  { 1.0f, 0.3f,0.0f, 0.0f,0.0f,5.0f },    // MCU
  { 1.0f, 0.3f,-0.2f, 0.0f,0.0f,5.0f },   // MCU Left of frame
  { 1.5f, -0.1f,0.0f, 0.0f,0.0f,5.0f },   // M
  { 1.5f, -0.1f,-0.3f, 0.0f,0.0f,5.0f },  // M Left of frame
  // 20 degree
  { 1.0f, 0.3f,0.0f, 0.0f,0.0f,20 },    // MCU
  { 1.0f, 0.3f,-0.2f, 0.0f,0.0f,20 },   // MCU Left of frame
  { 1.5f, -0.1f,0.0f, 0.0f,0.0f,20 },   // M
  { 1.5f, -0.1f,-0.3f, 0.0f,0.0f,20 },  // M Left of frame
  // Near profile
  { 1.0f, 0.3f,0.0f, 0.0f,0.0f,85 },    // MCU
  { 1.0f, 0.3f,-0.2f, 0.0f,0.0f,85 },   // MCU Left of frame
  { 1.5f, -0.1f,0.0f, 0.0f,0.0f,85 },   // M
  { 1.5f, -0.1f,-0.3f, 0.0f,0.0f,85 },  // M Left of frame
  // Reverse
  { 1.0f, 0.3f,0.0f, 0.0f,0.0f,180.0f },    // MCU
  { 1.0f, 0.3f,-0.2f, 0.0f,0.0f,180.0f },   // MCU Left of frame
  { 1.5f, -0.1f,0.0f, 0.0f,0.0f,180.0f },   // M
  { 1.5f, -0.1f,-0.3f, 0.0f,0.0f,180.0f },  // M Left of frame
  // Near Reverse
  { 1.0f, 0.3f,0.0f, 0.0f,0.0f,175.0f },    // MCU
  { 1.0f, 0.3f,-0.2f, 0.0f,0.0f,175.0f },   // MCU Left of frame
  { 1.5f, -0.1f,0.0f, 0.0f,0.0f,175.0f },   // M
  { 1.5f, -0.1f,-0.3f, 0.0f,0.0f,175.0f },  // M Left of frame
};

int nCamCuts=sizeof(aCamCuts)/sizeof(aCamCuts[0]);
//----------------------------------------------------------------------------------------------------------------------

class CFilmCamShot;

//----------------------------------------------------------------
//--- To be replaced by data file.
//----------------------------------------------------------------

struct SFilmCamNamedShots
{
  const char *psName;
  CFilmCamShot *pShot;
  TCamCut cut;
};

SFilmCamNamedShots aNamedShots[]=
{
  //  facing player from low near front bias
  { "GUN.1.2.a",0, CAM_DHPRVY(-1.500000f,0.012471f,12.871430f,0.000000f,0.157966f,30.990164f,CAM_IMED*1.0f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },
  //  facing player from middle bias
  { "GUN.1.2.b",0, CAM_DHPRVY(-1.100000f,-0.071550f,11.837118f,0.000000f,0.341170f,30.510584f,CAM_IMED*1.5f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },//CAM_DHPRVY(-1.300000f,0.012471f,12.666741f,0.000000f,0.157966f,31.234102f,CAM_IMED*1.2f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },
  //  facing player, from low left bias
  { "GUN.1.2.c",0, CAM_DHPRVY(-1.100000f,-0.005473f,11.615600f,0.000000f,0.440715f,18.478754f,CAM_IMED,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) }, //CAM_DHPRVY(-0.900000f,0.245082f,12.277581f,0.000000f,0.361978f,30.990164f,CAM_IMED*1.5f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },
  //  facing player, from low left bias
  { "GUN.1.2.d",0, CAM_DHPRVY(-1.100000f,-0.185439f,12.912279f,0.000000f,0.440715f,17.696032f,CAM_IMED,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) }, //CAM_DHPRVY(-0.900000f,0.230169f,9.281816f,0.000000f,-0.546810f,20.339737f,CAM_IMED*1.5f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },
  { "GUN.1.2.e",0, CAM_DHPRVY(-1.500000f,-0.185439f,12.912279f,0.000000f,0.440715f,16.996032f,CAM_IMED,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) }, //CAM_DHPRVY(-0.900000f,0.230169f,9.281816f,0.000000f,-0.546810f,20.339737f,CAM_IMED*1.5f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },
  // facing player, low left bias
  //{ "GUN.1.2.e",0, CAM_DHPRVY(-0.900000f,-0.176772f,12.919259f,0.000000f,0.477446f,-0.755571f,CAM_ILOW*1.5f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },
  // facing player, low left bias
  //{ "GUN.1.2.f",0, CAM_DHPRVY(-0.700000f,-0.176772f,-1.416006f,0.000000f,0.598432f,-0.158073f,CAM_ILOW*1.5f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },


  // full/medium - from player to target medium, angled?
  { "GUN.1.4.a",0, CAM_DHPRVY(-2.386500f,0.489940f,-3.356001f,0.000000f,0.125807f,-23.395338f,CAM_IMED,CAM_TARGET,CAM_DOFLOW,CAM_STATIC) },
  // full ??
  { "GUN.1.4.b",0, CAM_DHPRVY(-3.586500f,0.482753f,-6.506195f,0.000000f,0.907714f,-24.087791f,CAM_ILOW,CAM_TARGET,CAM_DOFLOW,CAM_STATIC) },
  // full ??
  { "GUN.1.4.c",0, CAM_DHPRVY(-3.586500f,0.482753f,-6.506195f,0.000000f,-0.610025f,-24.087791f,CAM_ILOW,CAM_TARGET,CAM_DOFLOW,CAM_STATIC) },
  //{ "GUN.1.4.d",0, CAM_DHPRVY(-3.586500f,0.482753f,-6.506195f,0.000000f,-0.610025f,-24.087791f,CAM_ILOW,CAM_TARGET,CAM_DOFLOW,CAM_STATIC) },
  //{ "GUN.1.4.e",0, CAM_DHPRVY(-3.586500f,0.482753f,-6.506195f,0.000000f,-0.610025f,-24.087791f,CAM_ILOW,CAM_TARGET,CAM_DOFLOW,CAM_STATIC) },
  //{ "GUN.1.4.f",0, CAM_DHPRVY(-3.586500f,0.482753f,-6.506195f,0.000000f,-0.610025f,-24.087791f,CAM_ILOW,CAM_TARGET,CAM_DOFLOW,CAM_STATIC) },
  // medium close ??            -1.386500f
  { "GUN.1.3.a",0, CAM_DHPRVY(-1.686500f,0.065044f,-6.440596f,0.000000f,0.134257f,-22.049414f,CAM_IMED,CAM_TARGET,CAM_DOFMED,CAM_STATIC) },
  { "GUN.1.3.b",0, CAM_DHPRVY(-1.686500f,0.065044f,-6.440596f,0.000000f,0.134257f,-40.049414f,CAM_IMED,CAM_TARGET,CAM_DOFMED,CAM_STATIC) },
  { "GUN.1.3.c",0, CAM_DHPRVY(-1.686500f,0.065044f,-6.440596f,0.000000f,0.134257f,-7.049414f,CAM_IMED,CAM_TARGET,CAM_DOFMED,CAM_STATIC) },
  // from behind victom towards player
  { "GUN.x.1.1.a",0, CAM_DHPRVY(-1.586500f,0.042022f,-5.611092f,0.000000f,0.134257f,-35.062569f,CAM_ILOW,CAM_TARGET,CAM_DOFMED,CAM_PAN) },

	// Locational camera - player + girl
	{ "PG.1.1.a", 0, CAM_DHPRVY(-1.100000f,-0.071550f,11.837118f,0.000000f,0.341170f,30.510584f,CAM_IMED*1.5f,CAM_PLAYER,CAM_DOFHIGH,CAM_STATIC) },

  // Distant
  { "EXP.1.1.a",0, CAM_DHPRVY(-4.586500f,0.482753f,-6.506195f,0.000000f,0.907714f,-24.087791f,CAM_ILOW,CAM_TARGET,CAM_DOFLOW,CAM_STATIC) }
};

int nNamedShots=sizeof(aNamedShots)/sizeof(aNamedShots[0]);

// test func - to be deleted
static SFilmCamNamedShots * PickRandomShot()
{
  int iRnd=rand()%nNamedShots;

  return &aNamedShots[iRnd];
}

static SFilmCamNamedShots * PickShotByIndex(int iCut)
{
  if(iCut<0) iCut=0;

  iCut=iCut%nNamedShots;

  return &aNamedShots[iCut];
}


// shot equivalence groups
const char *aEquiv_GUN_1_2[] = { "GUN.1.2.a","GUN.1.2.b","GUN.1.2.c","GUN.1.2.d",0 };
const char *aEquiv_GUN_1_4[] = { "GUN.1.4.a","GUN.1.4.b","GUN.1.4.c",0 };
const char *aEquiv_GUN_1_4x[] = { "GUN.1.4.a","GUN.1.4.b","GUN.1.4.c","GUN.x.1.1.a",0 };
const char *aEquiv_GUN_1_3x[] = { "GUN.1.3.a",0 };


//--- Sequences:

// Player then victim (2 shots). All low frequency.
// 1.a: 1.2, 1.3, (slow),       1+2 sec  low
// 1.b: 1.2, 1.4, (slow),       1+2 sec  low
// 1.c: 1.2, 1.3, (slow,norm),  1+1 sec  low
// 1.d: 1.2, 1.4, (slow,norm),  1+1 sec  low

// Victim (1 shot). slow: med freq, norm: med freq
// 2.a: 1.3, (slow),            2 sec   med
// 2.b: 1.4, (slow),            2 sec   med
// 2.c: 1.3, (norm),            1.5 sec med+
// 2.d: 1.4, (norm),            1.5 sec med+

// P+V (1 shot). slow: med-- freq, norm:  med- freq
// 4.4  low to high out to side
// 8.1 but more too the side ( for 20% diff rule )

// Extremes: (1 shot). low freq

//  ( shot 7.2 )
// ( shot 9.1  high angle down gun when player is shooting up )
// ( shot 11.1 high DOF on gun/arm -- version of 1.2)
// ( shot 12.1 along line of gun (held horz) to head


#define LOW_FREQ    0.20f
#define MED_FREQ    0.5f
#define MEDP_FREQ   0.75f

SFilmShotSeq  aShotSeq_ShootDeath[]=
{
  // Player then victim (2 shots). All low frequency.
  // 1.a: 1.2, 1.3, (slow),       1+2 sec  low
  // 1.b: 1.2, 1.4, (slow),       1+2 sec  low
  // 1.c: 1.2, 1.3, (slow,norm),  1+1 sec  low
  // 1.d: 1.2, 1.4, (slow,norm),  1+1 sec  low

  // Victim (1 shot). slow: med freq, norm: med freq
  // 2.a: 1.3, (slow),            2 sec   med
  // 2.b: 1.4, (slow),            2 sec   med
  // 2.c: 1.3, (norm),            1.5 sec med+
  // 2.d: 1.4, (norm),            1.5 sec med+

  //Player then victim:  { "name",intensity,freq,count { { "shotName",[]"apsShotNames",iShotName,time,deltaT,slow,ease } } }
  { "Seq.Gun.1.a", 0.3f, LOW_FREQ,0,
    {
      { "GUN.1.2.a",0,0, 1.0f, 0.02f,eSlowMo_VHigh,eSlowMoEase_Hold },  // 1.2
      { "GUN.1.3.a",0,0, 2.0f, 0.02f,eSlowMo_High,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { "Seq.Gun.1.b", 0.3f, LOW_FREQ,0,
    {
      { "GUN.1.2.a",0,0, 1.0f, 0.02f,eSlowMo_VHigh,eSlowMoEase_Hold },  // 1.2
      { "GUN.1.4.a",0,0, 2.0f, 0.02f,eSlowMo_High,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { "Seq.Gun.1.c", 0.3f, LOW_FREQ,0,
    {
      { "GUN.1.2.a",0,0, 1.0f, 0.1f,eSlowMo_VHigh,eSlowMoEase_Hold },  // 1.2
      { "GUN.1.3.a",0,0, 1.0f, 0.1f,eSlowMo_None,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { "Seq.Gun.1.d", 0.3f, LOW_FREQ,0,
    {
      { "GUN.1.2.a",0,0, 1.0f, 0.1f,eSlowMo_VHigh,eSlowMoEase_Hold },  // 1.2
      { "GUN.1.4.a",0,0, 1.0f, 0.1f,eSlowMo_None,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { "Seq.Gun.2.a", 0.3f, MED_FREQ,0,
    {
      { "GUN.1.3.a",0,0, 2.0f, 0.1f,eSlowMo_VHigh,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { "Seq.Gun.2.b", 0.3f, MED_FREQ,0,
    {
      { "GUN.1.4.a",0,0, 2.0f, 0.1f,eSlowMo_High,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { "Seq.Gun.2.c", 0.3f, MEDP_FREQ,0,
    {
      { "GUN.1.3.a",0,0, 1.5f, 0.1f,eSlowMo_None,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { "Seq.Gun.2.d", 0.3f, MEDP_FREQ,0,
    {
      { "GUN.1.4.a",0,0, 1.5f, 0.1f,eSlowMo_None,eSlowMoEase_Hold  },  // 1.5
      { 0,0,0,0 }
    }
  },
  { 0,0,0,0, { { 0,0,0,0,0 } } }
};


bool CFilmCamera::EvalSeq(SFilmShotSeq *pSeq)
{
  bool bRet=true;
  int iShot;
  int nShot=0;

  for(iShot=0 ; (iShot<8) && bRet ; ++iShot)
  {
    if(pSeq->aSeqShot[iShot].psShot)
    {
      pSeq->aSeqShot[iShot].iShot=0;
      bRet = bRet && TestShot(pSeq->aSeqShot[iShot].psShot);
      ++nShot;
    }
    else  // it's a set of possibilities
    {
      int iFound=-1;
      if(pSeq->aSeqShot[iShot].apsShot)
      {
        ++nShot;
        int iPos,nPos,iFirst;
        for(nPos=0 ; pSeq->aSeqShot[iShot].apsShot[nPos] ; ++nPos) ;
        iFirst=rand()%nPos;
        for(iPos=iFirst ; iPos<nPos ; ++iPos )
        {
          if(TestShot(pSeq->aSeqShot[iShot].apsShot[iPos]))
          {
            pSeq->aSeqShot[iShot].iShot=iPos;
            iFound=iPos;
            break;
          }
        }
        for(iPos=0 ; iPos<iFirst ; ++iPos )
        {
          if(TestShot(pSeq->aSeqShot[iShot].apsShot[iPos]))
          {
            pSeq->aSeqShot[iShot].iShot=iPos;
            iFound=iPos;
            break;
          }
        }
        if(-1==iFound)
          bRet=false;
      }
    }
  }

  return bRet && (nShot>0);
}

const char *gpsTestSeq=0;
int gITestSeq=-1;  // -1 => free choice 0..N => seq 0..N

const SFilmShotSeq  *CFilmCamera::PickSeqFromList(SFilmShotSeq *paSeqList)
{
  SFilmShotSeq *pRet=0,*pSeq,*pLowest;
  float lowestFreqFactor=0,freqFactor;
  int maxEvals=8;
  int nEval=0;

  if(gpsTestSeq)  // edit request
  {
    for(pSeq=paSeqList ; pSeq->psName ; ++pSeq)
    {
      if(0==strcmp(pSeq->psName,gpsTestSeq))
      {
        pRet=pSeq;
          break;
      }
    }
  }
  else if(gITestSeq>=0)
  {
    int iSeq;
    for(pSeq=paSeqList,iSeq=0 ; pSeq->psName && iSeq<gITestSeq ; ++pSeq,++iSeq)
      ;
    if((iSeq==gITestSeq)&& pSeq->psName)
      pRet=pSeq;
  }
  else
  if(paSeqList)
  {
    pLowest=paSeqList;
    lowestFreqFactor=(1+pLowest->count+(rand()%3)-1)/pLowest->frequency;

    for(pSeq=paSeqList ; pSeq->psName ; ++pSeq)
    {
      freqFactor=(1+pSeq->count)/pSeq->frequency;
      if((freqFactor)<(lowestFreqFactor))
      {
        pLowest=pSeq;
        lowestFreqFactor=(1+pLowest->count+(rand()%3)-1)/pLowest->frequency;
      }
    }

    for(pSeq=pLowest ; pSeq->psName ; ++pSeq)
    {
      if(EvalSeq(pSeq))
      {
        pRet=pSeq;
        break;
      }
      else if (pSeq==pLowest)
      {
        ++pLowest->count; // stops us getting stuck on one seq (the first one that works after this one)
      }
      if(++nEval>maxEvals)
        break;
    }
    if(!pRet)
    {
      for(pSeq=paSeqList; pSeq<pLowest ;  ++pSeq)
      {
        if(nEval++>maxEvals)
          break;
        if(EvalSeq(pSeq))
        {
          pRet=pSeq;
          break;
        }
      }
    }

    if(!pRet)
    {
      pRet=pLowest; // Hack for debugging.
    }

    if(pRet)
      ++pRet->count;
  }
  return pRet;
}

const SFilmShotSeq  *CFilmCamera::GetShotSeqForEvent(CFilmEvent &event)
{
  const SFilmShotSeq *pRet=0;
  //SFilmShotSeq *pSeq;
  CFilmShotCast cast;
  memset(&cast,0,sizeof(cast));
  int iCast,nCast;

  nCast=min( (sizeof(cast.aIdActors)/sizeof(cast.aIdActors[0])) , (sizeof(event.aIdActors)/sizeof(event.aIdActors[0])) );

  for(iCast=0 ; iCast<nCast ; ++iCast)
    cast.aIdActors[0]=event.aIdActors[0];
  m_cast.vInterestPoint=event.vPos;


  switch (event.type)
  {
  case eDeathCut:
    {
      pRet=PickSeqFromList(aShotSeq_ShootDeath);
    } break;
  case eExplosion:
    {
      //pSeq=PickSeqFromList(aShotSeq_Explosion);
    } break;
  default:
  case eEstablish:
    {
    } break;
  }

  return pRet;
}


//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------


struct SCamShotParams
{
};

struct SCamCutNode
{
  TCamCut camCut;
  float priority;
  float freq;
  unsigned lastShotNumber;
};

typedef std::list<SCamCutNode> TCamCutNodes;
typedef std::list<SCamCutNode>::iterator TCamCutNodesIter;



// Base class for camera shots. Provides common API, plus utility funcs.
class CFilmCamShot
{
public:

  virtual bool CheckRequirements(float *pExpectedQuality)=0;
  //virtual float GetExpectedQuality()=0;

  virtual float GetTickedQuality()=0;

  virtual bool Begin(SCamShotParams &rParams)=0;
  virtual void End()=0;

  virtual bool Tick(SViewParams &viewParams)=0;

  virtual ~CFilmCamShot();
  CFilmCamShot();

  void Init(CFilmCamera *pFilmCamera,const char *psName);

  void AddCamCut(TCamCut &rCamCut,float priority=0.25f,float freq=0.25f);

  TCamCut & GetActiveCamCut() { return m_camCutNode.camCut; }

protected:
  CFilmCamera *m_pFilmCamera;
  SCamShotParams m_params;
  bool m_bInit;
  bool m_bBegin;
  unsigned m_iTick;
  unsigned m_lastShotNumber;
  float m_lastShotTime;
  float m_startTime;
  SCamCutNode m_camCutNode;
  const char *m_psName;
  //TCamCutNodes m_camCutNodes;
  
  SCamCutNode *m_pCamCutNode;
  CCamera m_cam;  // used for camera based visibility enquiries
  Vec3 m_vPos;
  Quat m_qRot;

  //--- Camera visibility queries ---------
  void InitTestCam(IView *pView=0);
  void SetTestCamPosDirRoll(Vec3 pos,Vec3 dir,float roll=0.0f);
  void SetTestCamPosQuat(Vec3 pos,Quat qRot);

  bool IsSphereInTestCamFrame(Vec3 pos,float radius,float tolerance=0.0f)
    { return IsSphereInFrame(m_cam,pos,radius,tolerance);}

  bool IsSphereInFrame(CCamera &rCam,Vec3 pos,float radius,float tolerance=0.0f);

  //--- Object visibility (obstruction) queries -------
  // Find the point closest to vViewPos that can see vTargetPos.
  bool GetVisibleViewPos(Vec3 vViewPos,Vec3 vTargetPos,float minDist,Vec3 &vOutViewPos,IPhysicalEntity *pIgnore1,IPhysicalEntity *pIgnore2=0);
  bool GetVisibleViewPos(Vec3 vViewPos,Vec3 vTargetPos,float minDist,Vec3 &vOutViewPos,IEntity *pIgnore1=0,IEntity *pIgnore2=0);
  bool IsVisibleViewPos(Vec3 vViewPos,Vec3 vTargetPos,IEntity *pIgnore1=0,IEntity *pIgnore2=0);

  //--- In target is in frame, and visible?
  bool IsTargetInShot(Vec3 vViewPos,Vec3 vDir,float roll,float radius,Vec3 vTargetPos,IEntity *pIgnore1, IEntity *pIgnore2);
  bool IsTargetInShot(Vec3 vViewPos,Quat qRot,float radius,Vec3 vTargetPos,IEntity *pIgnore1,IEntity *pIgnore2);
  // test for intersections against the camera near plane
  bool CameraIntersectTest(Vec3 vViewPos,Vec3 vTargetPos);

  //--- Get world space locations of various parts of character.

  bool GetBonePos(IEntity *pEnt,const char *psBoneName,Vec3 &rVec);

  bool GetHeadBonePos(IEntity *pEnt,Vec3 &rVec)       { return GetBonePos(pEnt,"Bip01 Head",rVec); }
  bool GetWeaponBonePos(IEntity *pEnt,Vec3 &rVec)     { return GetBonePos(pEnt,"weapon_bone",rVec); }
  bool GetLeftHandBonePos(IEntity *pEnt,Vec3 &rVec)  { return GetBonePos(pEnt,"Bip01 L Hand",rVec); }
  bool GetRightHandBonePos(IEntity *pEnt,Vec3 &rVec) { return GetBonePos(pEnt,"Bip01 R Hand",rVec); }
  bool GetEyeBonePos(IEntity *pEnt,Vec3 &rVec);
};

bool CFilmCamShot::GetEyeBonePos(IEntity *pEnt,Vec3 &rVec)
{
  Vec3 v1,v2;
  static bool bInit=false;
  static uint32 idLeft;
  static uint32 idRight;

  bool bRet=false;
  ICharacterInstance *pCharacter;

  if(pEnt)
  {
    pCharacter=pEnt->GetCharacter(0);
    if(pCharacter)
    {
      if(!bInit)
      { // this id is consistent across all human skeletons, so we can cache it here for now..
        idLeft=pCharacter->GetISkeleton()->GetIDByName("eye_left_bone");
        idRight=pCharacter->GetISkeleton()->GetIDByName("eye_right_bone");
        bInit=true;
      }
      Vec3 v1=pCharacter->GetISkeleton()->GetAbsJPositionByID(idLeft);
      Vec3 v2=pCharacter->GetISkeleton()->GetAbsJPositionByID(idRight);
      
      v1=(v1+v2)*0.5f;
      rVec=pEnt->GetRotation()*v1;
      bRet=true;
    }
  }
  return bRet;
}

// Deprecated since we now need to cache a bone id.
bool CFilmCamShot::GetBonePos(IEntity *pEnt,const char *psName,Vec3 &rVec)
{
  bool bRet=false;
  ICharacterInstance *pCharacter;

  if(pEnt)
  {
     pCharacter=pEnt->GetCharacter(0);
     if(pCharacter)
     {
       int idBone=pCharacter->GetISkeleton()->GetIDByName(psName);
       Vec3 vPos=pCharacter->GetISkeleton()->GetAbsJPositionByID(idBone);
       rVec=pEnt->GetPos()+pEnt->GetRotation()*vPos;
       bRet=true;
     }
  }

  return bRet;
}


static const char *psError=0;
bool CFilmCamShot::IsTargetInShot(Vec3 vViewPos,Vec3 vDir,float roll,float radius,Vec3 vTargetPos,IEntity *pIgnore1, IEntity *pIgnore2)
{
  bool bRet;

  bool bInFrame;
  bool bUnObstructed;
  InitTestCam();
  SetTestCamPosDirRoll(vViewPos,vDir,roll);
  bInFrame=IsSphereInTestCamFrame(vTargetPos,radius);
  bUnObstructed=IsVisibleViewPos(vViewPos,vTargetPos,pIgnore1,pIgnore2);

  bRet=bInFrame && bUnObstructed;
  if(!bRet)
  {
    psError= bInFrame ? "Target not in shot: obstructed" : "Target not in shot:  not in frame";
  }
  return bRet;
}

bool CFilmCamShot::IsTargetInShot(Vec3 vViewPos,Quat qRot,float radius,Vec3 vTargetPos,IEntity *pIgnore1,IEntity *pIgnore2)
{
  bool bRet;
  bool bInFrame;
  bool bUnObstructed;
  InitTestCam();
  SetTestCamPosQuat(vViewPos,qRot);
  bInFrame=IsSphereInTestCamFrame(vTargetPos,radius);
  bUnObstructed=IsVisibleViewPos(vViewPos,vTargetPos,pIgnore1,pIgnore2);

  bRet=bInFrame && bUnObstructed;
  if(!bRet)
  {
    psError= bInFrame ? "Target not in shot: obstructed" : "Target not in shot:  not in frame";
  }
  return bRet;
}


bool CFilmCamShot::GetVisibleViewPos(Vec3 vViewPos,Vec3 vTargetPos,float minDist,Vec3 &vOutViewPos,IEntity *pIgnore1,IEntity *pIgnore2)
{
  IPhysicalEntity *pPhys1=0,*pPhys2=0;

  if(pIgnore1)  pPhys1=pIgnore1->GetPhysics();
  if(pIgnore2)  pPhys2=pIgnore2->GetPhysics();

  return GetVisibleViewPos(vViewPos,vTargetPos,minDist,vOutViewPos,pPhys1,pPhys2);
}

// Does the camera intersect anything?
bool CFilmCamShot::CameraIntersectTest(Vec3 vViewPos,Vec3 vTargetPos)
{
  ray_hit rayHit;
  Vec3 vF[8];
  Vec3 vT0[8];
  Vec3 vT1[8];
  Vec3 vDir;
  const float safety=0.3f;

  //IView *pView=m_pFilmCamera->GetView();
  ISystem *pSys=GetISystem();
  CCamera *pSysCam = &pSys->GetViewCamera();
  IPhysicalWorld *pW=GetISystem()->GetIPhysicalWorld();
  bool bInside=false;

  //--- Replace with frustum plane intersection tests when available.
  
  // Test the near plane for intersection along any of its edges, diagonals, or centre lines
  pSysCam->GetFrustumVertices(vF);

  // 4 near plane edges
  vT0[0]=vF[4]; vT1[0]=vF[5];
  vT0[1]=vF[6]; vT1[1]=vF[7];
  vT0[2]=vF[4]; vT1[2]=vF[6];
  vT0[3]=vF[5]; vT1[3]=vF[7];
  // 2 near plane diagonals
  vT0[4]=vF[4]; vT1[4]=vF[7];
  vT0[5]=vF[5]; vT1[5]=vF[6];
  // centre lines  h & v
  vT0[6]=(vF[4]+vF[6])*0.5f;  vT1[6]=(vF[5]+vF[7])*0.5f;
  vT0[7]=(vF[4]+vF[5])*0.5f;  vT1[7]=(vF[6]+vF[7])*0.5f;

  int iT;
  for(iT=0;iT<8;++iT)
  {
      vDir=(vT0[iT]-vT1[iT]).GetNormalizedSafe(Vec3(0,1,0));
      if (pW->RayWorldIntersection(vT1[iT]-vDir*safety,vT0[iT]-vT1[iT]+2*vDir*safety, ent_terrain|ent_static,RWI, &rayHit, 1,0, 0))
      {
        bInside=true;
//        DrawLineV1V2(vT1[iT],vT0[iT]);
        break;
      }
  }

  #if 0
    // test for entities inside a thin box at the near plane.
    if(!bInside)
    {
      const float thickness=0.25f;  // thickness of the box around the near plane.
      Vec3 vDHNear=(vF[4]-vF[5]);
      Vec3 vDVNear=(vF[4]-vF[6]);
      Vec3 vDir=(vTargetPos-vViewPos).GetNormalizedSafe(Vec3(0,1,0));
      Vec3 vMin=vViewPos-vDHNear-vDVNear-vDir*thickness;
      Vec3 vMax=vViewPos+vDHNear+vDVNear+vDir*thickness;

      IPhysicalEntity **pPhysArray=NULL,*pPhys;
      int nEntities,iEntity;
      nEntities=pW->GetEntitiesInBox(vMin, vMax, pPhysArray,IEntitySystem::PHYS_ENTITY_ALL);
      if(nEntities)
        bInside=true; // there's somebody in here.

      //who is it?
      //for(iEntity=0;iEntity<nEntities;++iEntity)
      //{
      //}
    }
  #endif

  return bInside;
}

bool CFilmCamShot::GetVisibleViewPos(Vec3 vViewPos,Vec3 vTargetPos,float minDist,Vec3 &vOutViewPos,IPhysicalEntity *pIgnore1,IPhysicalEntity *pIgnore2)
{
  ray_hit rayHit;
  bool bRet=false;
  Vec3 vPos1=vViewPos;    //vPos1.z+=0.5f;
  Vec3 vPos2=vTargetPos;  //vPos1.z+=0.5f;
  const float safetyDist=0.3f;    // this dist is added on to checks to make sure there's a reasonable distance behind the camera.

  Vec3 vDirLen(vPos1-vPos2);
  float viewDist=vDirLen.len();
  float newDist;

  if(viewDist)
  {
    vDirLen+=vDirLen.GetNormalized()*safetyDist;
    // shoot from target towards camera, and see where we end up..
    if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(vPos2, vDirLen, 
      ent_terrain|ent_static,RWI, &rayHit, 1,pIgnore1, pIgnore2))
    {
      // we've hit something!
      newDist=rayHit.dist-safetyDist;   // move closer to target to get past obstruction
      if(newDist > minDist)
      {
        vOutViewPos=vPos2+newDist*vDirLen.GetNormalized();
        bRet=true;
      }
      else
      {
        psError="No visible view pos: Target obstructed and can't get closer";
      }
    }
    else
    {
      // no hits, all OK

      bRet=true;
      vOutViewPos=vViewPos;
    }
  }
  else
  {
    psError="No visible view pos: Target and camera at same point";
  }

  return bRet;
}

bool CFilmCamShot::IsVisibleViewPos(Vec3 vViewPos,Vec3 vTargetPos,IEntity *pIgnore1,IEntity *pIgnore2)
{
  ray_hit rayHit;
  bool bRet=false;
  Vec3 vPos1=vViewPos;    //vPos1.z+=0.5f;
  Vec3 vPos2=vTargetPos;  //vPos1.z+=0.5f;
  const float safetyDist=0.3f;    // this dist is added on to checks to make sure there's a reasonable distance behind the camera.

  IPhysicalEntity *pPhys1=0,*pPhys2=0;

  if(pIgnore1)  pPhys1=pIgnore1->GetPhysics();
  if(pIgnore2)  pPhys2=pIgnore2->GetPhysics();

  Vec3 vDirLen(vPos1-vPos2);
  float viewDist=vDirLen.len();
  float newDist;

  if(viewDist)
  {
    vDirLen+=vDirLen.GetNormalized()*safetyDist;
    // shoot from target towards camera, and see where we end up..
    if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(vPos2, vDirLen, 
      ent_terrain|ent_static,RWI, &rayHit, 1,pPhys1, pPhys2))
    {
      // we've hit something!
      newDist=rayHit.dist-safetyDist;

      if( fabs(newDist-viewDist) < 0.001f ) // is it within 1 mm of cam pos ?
      {
        bRet=true;
      }
      else
        psError="IsVisibleViewPos Failed";
    }
    else
    {
      // no hits, all OK
      bRet=true;
    }
  }
  else
    psError="No visible view pos: Target and camera at same point";

  return bRet;
}
void CFilmCamShot::AddCamCut(TCamCut &rCamCut,float priority,float freq)
{
  // Should add to a list
  m_camCutNode.camCut=rCamCut;
  m_camCutNode.freq=freq;
  m_camCutNode.priority=priority;
  m_camCutNode.lastShotNumber=0;
}
char *STR_DUP(const char *psStr)
{
  char *psRet=0;
  if(psStr)
  {
    int len=strlen(psStr);
    psRet=(char*)malloc(len+1);
    strncpy(psRet,psStr,len+1);
  }
  return psRet;
}
void CFilmCamShot::Init(CFilmCamera *pFilmCam,const char *psName)
{
  if(m_psName)
    free((void*)m_psName);
  if(psName)
    m_psName=STR_DUP(psName);
  else
    m_psName=STR_DUP("UnNamed");
  m_pFilmCamera=pFilmCam;
  m_bInit=true;
}

void CFilmCamShot::InitTestCam(IView *pView)
{
  if(!pView)
  {
    pView=m_pFilmCamera->GetView();  //g_pGame->GetIGameFramework()->GetIViewSystem()->GetActiveView();
  }

  if(pView)
  {
    ISystem *pSys=GetISystem();
    CCamera *pSysCam = &pSys->GetViewCamera();

    Vec3 pos=pView->GetCurrentParams()->position;
    Quat qRot=pView->GetCurrentParams()->rotation;
    qRot.Normalize();
    float fov=pView->GetCurrentParams()->fov;   // 0 => use system defaults
    fov = fov < 0.001 ? DEFAULT_FOV : fov;
    float nearPlane=pView->GetCurrentParams()->nearplane; // 0 => use system defaults
    nearPlane = (nearPlane > 0.01f)? nearPlane : DEFAULT_NEAR;
    float farPlane = pSys->GetI3DEngine()->GetMaxViewDistance();

    m_cam.SetFrustum(pSysCam->GetViewSurfaceX(),pSysCam->GetViewSurfaceZ(),fov,nearPlane,farPlane);

    Matrix34 mat(Matrix33(qRot),pos);
    m_cam.SetMatrix(mat);
  }
}

void CFilmCamShot::SetTestCamPosDirRoll(Vec3 pos,Vec3 dir,float roll)
{
  Quat qRot=Quat::CreateRotationVDir(dir,roll);
  Matrix34 mat(Matrix33(qRot),pos);
  m_cam.SetMatrix(mat);
  //m_cam.SetPosition(pos);
}

void CFilmCamShot::SetTestCamPosQuat(Vec3 pos,Quat qRot)
{
  Matrix34 mat(Matrix33(qRot),pos);
  m_cam.SetMatrix(mat);
  //m_cam.SetPosition(pos);
}

bool CFilmCamShot::IsSphereInFrame(CCamera &rCam,Vec3 pos,float radius,float tolerance)
{
  Sphere sph(pos,radius);
  uint8 visCode;
  bool bRet;

  if( (tolerance<radius) && (radius>0.01f) )
  {
//    DrawSphere(pos,radius);

    radius-=tolerance;

    //visCode=rCam.IsSphereVisible_hierarchical(sph);
    visCode=rCam.IsSphereVisible_FH(sph);

    switch (visCode)
    {
    case CULL_EXCLUSION :   // sphere outside of frustum
      bRet=false;
      psError="Sphere outside frame";
      break;
    case CULL_OVERLAP :   // sphere intersects the borders of the frustum, further checks necessary
      bRet=false;         // ( could do a quality setting here based on a couple more tests )
      psError="Sphere overlaps frame";
      break;
    case CULL_INCLUSION :  // sphere is complete inside the frustum, no further checks necessary
      bRet=true;
      break;
    }
  }
  else  // Do point check
  {
    bRet=rCam.IsPointVisible(pos);
    if(!bRet)
      psError="Point sphere outside frame";
  }

  return bRet;
}


CFilmCamShot::CFilmCamShot()
{
  memset(&m_params,0,sizeof(SCamShotParams));
  m_bBegin=0;
  m_iTick=0;
  m_lastShotNumber=0;
  m_lastShotTime=0;
  m_startTime=0;
  m_psName=0;
}

CFilmCamShot::~CFilmCamShot()
{
  if(m_psName)
    free((void*)m_psName);
}
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------

class CFilmCamShot_PlayerFrontShoot : public CFilmCamShot
{
public:
  virtual bool CheckRequirements(float *pExpectedQuality);
  //virtual float GetExpectedQuality()=0;

  virtual float GetTickedQuality();

  virtual bool Begin(SCamShotParams &rParams);
  virtual void End();

  virtual bool Tick(SViewParams &viewParams);

  CFilmCamShot_PlayerFrontShoot();
  ~CFilmCamShot_PlayerFrontShoot();

protected:
  SCamShotParams m_baseParams;
  SCamShotParams m_activeParams;
  float m_quality;
  bool m_bTickSetupCamera;

  virtual bool BeginSetupCamera(SCamShotParams &rParams);
  virtual bool TickSetupCamera(SViewParams viewParams);
};

CFilmCamShot_PlayerFrontShoot::CFilmCamShot_PlayerFrontShoot()
{
  memset(&m_baseParams,0,sizeof(m_baseParams));
  memset(&m_activeParams,0,sizeof(m_activeParams));
  m_quality=0.0f;
  m_bTickSetupCamera=false;
}

CFilmCamShot_PlayerFrontShoot::~CFilmCamShot_PlayerFrontShoot()
{
}


bool CFilmCamShot_PlayerFrontShoot::CheckRequirements(float *pExpectedQuality)
{
  bool bRet=false;
  *pExpectedQuality=0.0f;
  // player has shot and killed enemy using pistol (any small handgun).
  // shot happened VERY recently (so we'll get to see flash and smoke).
  // player not dieing
  

  // we can find a position for the camera.
  // player will (probably) stay in shot (ie not falling/jumping/sprinting/caught in explosion) 

  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  Vec3 vPlayPos=pPlayer->GetEntity()->GetPos();
  Vec3 vCamPos;
  Vec3 vLineOfAction=m_pFilmCamera->GetLineOfAction();
  Vec3 vDir;
  float actionDist=m_pFilmCamera->GetActionDist();
  IView *pView=m_pFilmCamera->GetView();
  float yaw,roll,pitch,dist;
  bool bIsVisible=false;
  bool bIsInFrame=false;

  m_pCamCutNode=&m_camCutNode;

  roll=m_pCamCutNode->camCut.roll;
  pitch=m_pCamCutNode->camCut.pitch;
  yaw=m_pCamCutNode->camCut.yaw;

  dist=m_pCamCutNode->camCut.Dist;

  Quat qRot=Quat::CreateRotationXYZ(Ang3(pitch,roll,yaw));

  vDir=qRot*vLineOfAction;

  vCamPos=vPlayPos+vDir*dist;

  // Shoot ray(s) from target to cam to test for obstructions
  Vec3 vOutPos,vDiff;
  bIsVisible=GetVisibleViewPos(vCamPos,vPlayPos,1.0f,vOutPos,pPlayer->GetEntity(),0);
  if(bIsVisible)
  {
    *pExpectedQuality=1.0f;

    vDiff=(vOutPos-vCamPos);
    float newDiff=vDiff.len();
    float pctDiff=fabs(newDiff/dist);
    *pExpectedQuality*=(1-pctDiff);
    vCamPos=vOutPos;

    // Set test cam up, and check if object will be in frame
    SetTestCamPosDirRoll(vCamPos,vDir,0.0f);
    bIsInFrame=IsSphereInTestCamFrame(vPlayPos,CU_RADIUS*1.1f,0.0f);

    //bRet=bIsInFrame; // && bIsInFrame;
    bRet=true;
  }

  return bRet;
}


//float CFilmCamShot_PlayerFrontShoot::GetExpectedQuality()
//{
  // how much of player in shot?
  // how good is the position
//}

float CFilmCamShot_PlayerFrontShoot::GetTickedQuality()
{
  // how much of player was in shot?
  // how much of player is expected to remain in shot?

  return m_quality;
}

bool CFilmCamShot_PlayerFrontShoot::BeginSetupCamera(SCamShotParams &rParams)
{
  bool bRet=false;
  m_quality=0.0f;

  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  Vec3 vPlayPos=pPlayer->GetEntity()->GetPos();
  Vec3 vCamPos;
  Vec3 vLineOfAction=m_pFilmCamera->GetLineOfAction();
  Vec3 vDir;
  float actionDist=m_pFilmCamera->GetActionDist();
  IView *pView=m_pFilmCamera->GetView();
  float yaw,roll,pitch,dist;
  bool bIsVisible=false;
  bool bIsInFrame=false;

  m_pCamCutNode=&m_camCutNode;

  roll=m_pCamCutNode->camCut.roll;
  pitch=m_pCamCutNode->camCut.pitch;
  yaw=m_pCamCutNode->camCut.yaw;

  dist=m_pCamCutNode->camCut.Dist;

    Quat qRot=Quat::CreateRotationXYZ(Ang3(pitch,roll,yaw));

  vDir=qRot*vLineOfAction;

  vCamPos=vPlayPos+vDir*dist;

  qRot=Quat::CreateRotationVDir(vDir);
  Matrix33 mat = Matrix33(qRot);

  vCamPos += mat.GetColumn(0)*(-m_pCamCutNode->camCut.hOff) + mat.GetColumn(2) * (m_pCamCutNode->camCut.vOff);

  int iEditHold=GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->GetIVal();

  if(iEditHold)
  {
    m_vPos=vCamPos;
    qRot=Quat::CreateRotationVDir(vDir);
    m_qRot=qRot;
    bRet=true;
  }
  else
  {
    // Shoot ray(s) from target to cam to test for obstructions
    Vec3 vOutPos,vDiff;
    bIsVisible=GetVisibleViewPos(vCamPos,vPlayPos,1.0f,vOutPos,pPlayer->GetEntity(),0);
    if(bIsVisible)
    {
      m_quality=1.0f;

      vDiff=(vOutPos-vCamPos);
      float newDiff=vDiff.len();
      float pctDiff=fabs(newDiff/dist);
      m_quality*=(1-pctDiff);
      vCamPos=vOutPos;

      // Set test cam up, and check if object will be in frame
      SetTestCamPosDirRoll(vCamPos,vDir,0.0f);
      bIsInFrame=IsSphereInTestCamFrame(vPlayPos,CU_RADIUS*1.1f,0.0f);

      m_vPos=vCamPos;
      qRot=Quat::CreateRotationVDir(vDir);
      m_qRot=qRot;

      //bRet=bIsInFrame; // && bIsInFrame;
      bRet=true;
    }
  }
  return bRet;
}

bool CFilmCamShot_PlayerFrontShoot::TickSetupCamera(SViewParams viewParams)
{
  return true;
}

bool CFilmCamShot_PlayerFrontShoot::Begin(SCamShotParams &rParams)
{
  bool bRet;
  bool bIntersect;

  m_baseParams=rParams;
  m_activeParams=rParams;

  pEditActiveCut=&m_camCutNode.camCut;
  psEditActiveCutName=m_psName;

  bRet=BeginSetupCamera(m_activeParams);

  if(bRet)
  {
    bIntersect=CameraIntersectTest(m_vPos,m_vPos+m_qRot*Vec3(0,1,0)*10.0f);

    if(bIntersect)
    {
      bRet=false;
    }
  }

  SetDOF(m_camCutNode.camCut.iDof,m_camCutNode.camCut.Dist);

  return bRet;
}

void CFilmCamShot_PlayerFrontShoot::End()
{
}

bool CFilmCamShot_PlayerFrontShoot::Tick(SViewParams &viewParams)
{
  bool bRet=false;

  if(bEditRefresh || bEditAutoRefresh)
  {
    SCamShotParams shotParams;
    BeginSetupCamera(shotParams);
    bEditRefresh=false;
  }

  // test that target is still in shot and not obstructed.
  // test that target to camera dist is still in limits.
  // if we're a pan or track camera, then do any required movement

  //-- get cam pos and rotation
  Vec3 vCamPos;
  //IView *pView=m_pFilmCamera->GetView();  //g_pGame->GetIGameFramework()->GetIViewSystem()->GetActiveView();
  //const SViewParams *pViewParams;
  //pViewParams=pView->GetCurrentParams();
  //vCamPos=viewParams.position;
  //Quat qRot=viewParams.rotation;
  vCamPos=m_vPos;
  Quat qRot=m_qRot;

  //-- get player (target) pos
  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  Vec3 vPlayPos=pPlayer->GetEntity()->GetPos();
  Vec3 vTestPos=vPlayPos;
  float radius;

  Vec3 vPlayerUp=(pPlayer->GetEntity()->GetRotation()*Vec3(0,0,1));
  switch(m_camCutNode.camCut.iSubject)
  {
  default :
  case CAM_PLAYER :
    radius=CU_RADIUS*0.9f;
    vTestPos=vPlayPos+vPlayerUp*0.7f;
    break;
  case CAM_PLAYERHEAD :
    radius=CU_RADIUS*0.7f;
    vTestPos=vPlayPos+vPlayerUp*0.7f;
    break;
  case CAM_PLAYERWEAP :
    radius=CU_RADIUS*0.7f;
    vTestPos=vPlayPos+vPlayerUp*0.5f;
    break;
  }

  int iEditHold=GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->GetIVal();

  if(iEditHold)
  {
    Vec3 vTmpPos=vCamPos;
    Quat qTmpRot=qRot;
    IsTargetInShot(vTmpPos,qTmpRot,radius,vTestPos,pPlayer->GetEntity(),0);
    bRet=true;
    m_quality=1.0f;
  }
  else
  {
    bRet=IsTargetInShot(vCamPos,qRot,radius,vTestPos,pPlayer->GetEntity(),0);
  }

  if(!bRet)
    m_quality=0.0f;

  viewParams.position=vCamPos;
  viewParams.rotation=qRot;

  return bRet;  // false means the shot has either finished, or is no longer suitable.
}


//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------

class CFilmCamShot_PlayerTargetImpact : public CFilmCamShot
{
public:
  virtual bool CheckRequirements(float *pExpectedQuality);
  //virtual float GetExpectedQuality()=0;

  virtual float GetTickedQuality();

  virtual bool Begin(SCamShotParams &rParams);
  virtual void End();

  virtual bool Tick(SViewParams &viewParams);

  CFilmCamShot_PlayerTargetImpact();
  ~CFilmCamShot_PlayerTargetImpact();

protected:
  SCamShotParams m_baseParams;
  SCamShotParams m_activeParams;
  float m_quality;
  bool m_bTickSetupCamera;
  Vec3 m_vTargetPos;
  EntityId m_idTarg;
  float m_distScale;
  float m_yawScale;
  //Vec3 vLineOfAction;

  virtual bool CalcCameraPos(float *pExpectedQuality,Vec3 *pvPos=0,Quat *pQRot=0);
  virtual bool BeginSetupCamera(SCamShotParams &rParams);
  virtual bool TickSetupCamera(SViewParams viewParams);
};

CFilmCamShot_PlayerTargetImpact::CFilmCamShot_PlayerTargetImpact()
{
  memset(&m_baseParams,0,sizeof(m_baseParams));
  memset(&m_activeParams,0,sizeof(m_activeParams));
  m_quality=0.0f;
  m_bTickSetupCamera=false;
  m_vTargetPos=Vec3(0,0,0);
  m_idTarg=0;
  m_distScale=1.0f;
  m_yawScale=1.0f;
}

CFilmCamShot_PlayerTargetImpact::~CFilmCamShot_PlayerTargetImpact()
{
}


bool CFilmCamShot_PlayerTargetImpact::CalcCameraPos(float *pExpectedQuality,Vec3 *pvPos,Quat *pQRot)
{
  bool bRet=false;
  *pExpectedQuality=0.0f;
  // player has shot and killed enemy using pistol (any small handgun).
  // shot happened VERY recently (so we'll get to see flash and smoke).
  // player not dieing


  // we can find a position for the camera.
  // player will (probably) stay in shot (ie not falling/jumping/sprinting/caught in explosion) 

  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  Vec3 vPlayPos=pPlayer->GetEntity()->GetPos();
  Vec3 vCamPos;
  Vec3 vLineOfAction=m_pFilmCamera->GetLineOfAction();
  Vec3 vDir;
  float actionDist=m_pFilmCamera->GetActionDist();
  IView *pView=m_pFilmCamera->GetView();
  float yaw,roll,pitch,dist;
  bool bIsVisible=false;
  bool bIsInFrame=false;

  IEntity *pEnt=0;
  m_vTargetPos=vPlayPos;
  int iCast;

  for(iCast=0;iCast<4;++iCast)
  {
    m_idTarg=m_pFilmCamera->GetCast().aIdActors[iCast];
    pEnt=GetISystem()->GetIEntitySystem()->GetEntity(m_idTarg);
    if(pEnt && (pEnt->GetId()!= pPlayer->GetEntity()->GetId()) )
    {
      m_vTargetPos=pEnt->GetPos();
      break;
    }
  }

  if(!pEnt || pEnt==pPlayer->GetEntity())
  {
    pEnt=pPlayer->GetAutoCombat()->GetLastAiming();
    {
      if(pEnt && m_pFilmCamera->IsValidPlayer(pEnt))
        m_vTargetPos=pEnt->GetPos();
      else
      {
        m_vTargetPos=vPlayPos;
      }
    }
  }

  m_pCamCutNode=&m_camCutNode;

  roll=m_pCamCutNode->camCut.roll*(gf_PI/180.0f);
  pitch=m_pCamCutNode->camCut.pitch*(gf_PI/180.0f);
  //yaw=(m_pCamCutNode->camCut.yaw+90)*(gf_PI/180.0f);

  yaw=(m_pCamCutNode->camCut.yaw)*m_yawScale*(gf_PI/180.0f);
  dist=m_pCamCutNode->camCut.Dist*m_distScale;

  Quat qRot=Quat::CreateRotationXYZ(Ang3(pitch,roll,-yaw));
  
  qRot.Normalize();
  vDir=qRot*Vec3(0,1,0);
  vDir=Quat::CreateRotationV0V1(Vec3(0,1,0),-vLineOfAction).GetNormalized()*vDir;
  vDir.Normalize();
  //vLineOfAction.Normalize();

  //vDir=qRot* -vLineOfAction;
  //Vec3 vDir2=qRot*Vec3(0,1,0);

  //DrawLineV1Dir(m_vTargetPos,vDir,40,clrDkMagenta,clrMagenta);
  //DrawLineV1Dir(vPlayPos,vDir,40,clrRed,clrRed);
  //DrawLineV1Dir(vPlayPos+Vec3(0,0,0.2f),vDir,40,clrDkYellow,clrDkYellow);

  //vDir=vDir2;

  //vCamPos=vPlayPos+vDir*dist;
  vCamPos=m_vTargetPos+vDir*dist;

  qRot=Quat::CreateRotationVDir(vDir);
  Matrix33 mat = Matrix33(qRot);

  //vCamPos += mat.GetColumn(0)*(-m_pCamCutNode->camCut.hOff) + mat.GetColumn(2) * (m_pCamCutNode->camCut.vOff);

  int iEditHold=GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->GetIVal();

  if(iEditHold)
  {
    *pvPos=vCamPos;
    qRot=Quat::CreateRotationVDir(vDir);
    *pQRot=qRot;
    bRet=true;
  }
  else
  {
    // Shoot ray(s) from target to cam to test for obstructions
    Vec3 vOutPos,vDiff;
    bIsVisible=GetVisibleViewPos(vCamPos,m_vTargetPos,1.0f,vOutPos,pEnt,0);//pPlayer->GetEntity(),pEnt);
    if(bIsVisible)
    {
      *pExpectedQuality=1.0f;

      vDiff=(vOutPos-vCamPos);
      float newDiff=vDiff.len();
      float pctDiff=fabs(newDiff/dist);
      *pExpectedQuality*=(1-pctDiff);
      vCamPos=vOutPos;

      // Set test cam up, and check if object will be in frame
      SetTestCamPosDirRoll(vCamPos,vDir,0.0f);
      bIsInFrame=IsSphereInTestCamFrame(m_vTargetPos,CU_RADIUS*1.1f,0.0f);

      *pvPos=vCamPos;
      qRot=Quat::CreateRotationVDir(vDir);
      *pQRot=qRot;

      //bRet=bIsInFrame; // && bIsInFrame;
      bRet=true;
    }
  }

  return bRet;
}


bool CFilmCamShot_PlayerTargetImpact::CheckRequirements(float *pExpectedQuality)
{
  bool bRet=false;
  *pExpectedQuality=0.0f;
  // player has shot and killed enemy using pistol (any small handgun).
  // shot happened VERY recently (so we'll get to see flash and smoke).
  // player not dieing


  // we can find a position for the camera.
  // player will (probably) stay in shot (ie not falling/jumping/sprinting/caught in explosion) 

  Quat qRot;
  Vec3 vPos;

  bRet=CalcCameraPos(pExpectedQuality,&vPos,&qRot);

  if(bRet)
  {
    DrawSphere(vPos,0.2f,clrDkGrey);
    Vec3 vDir=qRot*Vec3(0,1,0);
    DrawLineV1Dir(m_vPos,vDir,5,clrDkGrey,clrDkGrey);

  }


  return bRet;
}


float CFilmCamShot_PlayerTargetImpact::GetTickedQuality()
{
  // how much of player was in shot?
  // how much of player is expected to remain in shot?

  return m_quality;
}

bool CFilmCamShot_PlayerTargetImpact::BeginSetupCamera(SCamShotParams &rParams)
{
  bool bRet=false;

  bRet=CalcCameraPos(&m_quality,&m_vPos,&m_qRot);

  return bRet;
}

bool CFilmCamShot_PlayerTargetImpact::TickSetupCamera(SViewParams viewParams)
{
  return true;
}

bool CFilmCamShot_PlayerTargetImpact::Begin(SCamShotParams &rParams)
{
  bool bRet;

  m_baseParams=rParams;
  m_activeParams=rParams;

  pEditActiveCut=&m_camCutNode.camCut;
  psEditActiveCutName=m_psName;

  int iTest;

  bRet=false;
  for(iTest=0;(iTest<4) && !bRet ;++iTest)
  {
    m_distScale=1.0f+((rand()%5)-2)*0.1f;
    m_yawScale=1.0f+((rand()%5)-2)*0.1f;


    bRet=BeginSetupCamera(m_activeParams);
  }

  if(bRet)
  {
    bool bIntersect=CameraIntersectTest(m_vPos,m_vPos+m_qRot*Vec3(0,1,0)*10.0f);

    if(bIntersect)
    {
      bRet=false;
    }
  }
   CryLogAlways("DistYaw: %f,%f",m_distScale,m_yawScale);

   SetDOF(m_camCutNode.camCut.iDof,m_pCamCutNode->camCut.Dist*m_distScale);
  return bRet;
}


void CFilmCamShot_PlayerTargetImpact::End()
{
}

bool CFilmCamShot_PlayerTargetImpact::Tick(SViewParams &viewParams)
{
  bool bRet=false;

  if(bEditRefresh || bEditAutoRefresh)
  {
    SCamShotParams shotParams;
    BeginSetupCamera(shotParams);
    bEditRefresh=false;
  }

  // test that target is still in shot and not obstructed.
  // test that target to camera dist is still in limits.
  // if we're a pan or track camera, then do any required movement

  //-- get cam pos and rotation
  Vec3 vCamPos;
  //IView *pView=m_pFilmCamera->GetView();  //g_pGame->GetIGameFramework()->GetIViewSystem()->GetActiveView();
  //const SViewParams *pViewParams;
  //pViewParams=pView->GetCurrentParams();
  //vCamPos=viewParams.position;
  //Quat qRot=viewParams.rotation;
  vCamPos=m_vPos;
  Quat qRot=m_qRot;

  //-- get player (target) pos
  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  IEntity *pPlayEnt=pPlayer->GetEntity();
  Vec3 vPlayPos=pPlayEnt->GetPos();
  Vec3 vTestPos=m_vTargetPos;// vPlayPos;
  float radius;
  IEntity *pEnt=GetISystem()->GetIEntitySystem()->GetEntity(m_idTarg);

  Vec3 vPlayerUp=(pPlayEnt->GetRotation()*Vec3(0,0,1));
  Vec3 vTargetUp;

  if(pEnt)
    vTargetUp=(pEnt->GetRotation()*Vec3(0,0,1));
  else
    vTargetUp=Vec3(0,0,1);

  switch(m_camCutNode.camCut.iSubject)
  {
  default :
  case CAM_PLAYER :
    radius=CU_RADIUS*0.9f;
    vTestPos=vPlayPos+vPlayerUp*0.7f;
    break;
  case CAM_PLAYERHEAD :
    radius=CU_RADIUS*0.7f;
    vTestPos=vPlayPos+vPlayerUp*0.7f;
    break;
  case CAM_PLAYERWEAP :
    radius=CU_RADIUS*0.7f;
    vTestPos=vPlayPos+vPlayerUp*0.5f;
    break;
  case CAM_TARGET :
    radius=CU_RADIUS*0.9f;
    if( !GetHeadBonePos(pEnt,vTestPos) )
      vTestPos=m_vTargetPos+vTargetUp*0.7f;
    break;
  case CAM_TARGETHEAD :
    radius=CU_RADIUS*0.7f;
    if( !GetEyeBonePos(pEnt,vTestPos) )
      vTestPos=m_vTargetPos+vTargetUp*0.7f;
    break;
  case CAM_TARGETWEAP :
    radius=CU_RADIUS*0.7f;
    if( !GetWeaponBonePos(pEnt,vTestPos) )
      vTestPos=m_vTargetPos+vTargetUp*0.5f;
    break;
  }

  int iEditHold=GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->GetIVal();

  if(iEditHold)
  {
    Vec3 vTmpPos=vCamPos;
    Quat qTmpRot=qRot;
    if(!IsTargetInShot(vTmpPos,qTmpRot,radius,vTestPos,pPlayEnt,pEnt))
      CryLogAlways("Shot failed('%s'): '%s'",m_psName,psError);
    bRet=true;
    m_quality=1.0f;
  }
  else
  {
    psError=0;
    bRet=IsTargetInShot(vCamPos,qRot,radius,vTestPos,pPlayEnt,pEnt);
    if(!bRet && psError)
      CryLogAlways("Shot failed('%s'): '%s'",m_psName,psError);
  }

  if(!bRet)
    m_quality=0.0f;

  viewParams.position=vCamPos;
  viewParams.rotation=qRot;

  return bRet;  // false means the shot has either finished, or is no longer suitable.
}


//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------


// Refactoring structure
struct SPlayerView
{
  IVehicle *pVehicle;
  float frameTime;

  Vec3 lastPos;
  Quat lastQuat;

  float defaultFov;

  Ang3 wAngles;

  ICharacterInstance *pCharacter;

  float bobMul;
  bool usePivot;
  Matrix33 m_viewMtxForWeapon;
  Vec3 eyeOffsetGoal;

  bool bWasCut;
};


SPlayerView v;

IView *CFilmCamera::GetView()
{
  return g_pGame->GetIGameFramework()->GetIViewSystem()->GetActiveView();
}

bool FlattenRotationMatrixToZPlane(Matrix33 &rMat)
{
  bool bRet=true;
  Vec3 v;
  float mag;

  v=rMat.GetColumn(0);      // x
  v.z=0;
  mag=v.len();
  if(mag>0.0001f) v/=mag; else { v=Vec3(1,0,0); bRet=false; } // false => lying on our side
  rMat.SetColumn(0,v);

  v=rMat.GetColumn(1);      // y (view dir)
  v.z=0;
  mag=v.len();
  if(mag>0.0001f) v/=mag; else { v=Vec3(0,1,0); bRet=false; }  // false => looking straight up
  rMat.SetColumn(1,v);

  v=rMat.GetColumn(2);      // z (up dir)
  if(v.z>=0) v=Vec3(0,0,1); else v=Vec3(0,0,-1);
  rMat.SetColumn(2,v);

  return bRet;
}


//
//
//
//
//
//
//
//



#if 0
void CFilmCamera::UVCollision(CPlayerG4 *pPlayer,SViewParams &viewParams)
{
	//--- Collision ----------------------------------------------------------------
	ray_hit rayHit;
	Vec3 vDirLen=(viewParams.position-targetPos);
	float distLen = vDirLen.len();
	const float safetyDistUp=0.05f;
	const float safetyDist=0.7f;
	const float safetyDistLook=0.7f;
	const float safetySideDist=1.0f;

	if(distLen>0.01f)
	{
		Vec3 vLook=vDirLen.GetNormalizedSafe(Vec3(0,1,0));
		Vec3 vRight=-vLook.Cross(Vec3(0,0,1));
		vDirLen+=vDirLen.GetNormalized()*safetyDist;
		float tL=1,tR=1,tM=1,tMR=1,tML=1;
		//vHitDir/=distHit;

		// Obstruction between target and camera?
		//if(0)
		// centre
		if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos, vDirLen, 
			ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
		{
			float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
			if(newDist<=0.1f)
				newDist=0.1f;
			float t=newDist/distLen;
			if(t>1) t=1;
			tM=t;
			//float dHgt=(1.0f-t)*safetyDistUp;
			//viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
			//targetPos.z+=dHgt;
		}
#if 0
		int iTest=0;
		bool bRepeatTest=true;
		Vec3 vDirLenR,vDirLenL,vDirLenLR,vTestPos;
		for(iTest=0;iTest<8 && bRepeatTest;++iTest)
		{
			vDirLenLR=vDirLen*min(tML,tMR);
			vTestPos=targetPos+vDirLenLR;
			vDirLenR=((vTestPos+vRight*safetyDistLook)-targetPos);
			vDirLenL=((vTestPos-vRight*safetyDistLook)-targetPos);
			bRepeatTest=false;
			// right edge to target
			if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos, vDirLenR, 
				ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
			{
				float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
				if(newDist<=0.1f)
					newDist=0.1f;
				float t=newDist/distLen;
				if(t>1) t=1;
				tMR=t;
				bRepeatTest=true;
				//float dHgt=(1.0f-t)*safetyDistUp;
				//viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
				//targetPos.z+=dHgt;
			}
			// left edge to target
			if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos, vDirLenL, 
				ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
			{
				float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
				if(newDist<=0.1f)
					newDist=0.1f;
				float t=newDist/distLen;
				if(t>1) t=1;
				tML=t;
				bRepeatTest=true;
				//float dHgt=(1.0f-t)*safetyDistUp;
				//viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
				//targetPos.z+=dHgt;
			}
		}
		if(bRepeatTest)
		{
			// to right side of target
			if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos+vRight*safetyDistLook, vDirLen, 
				ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
			{
				float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
				if(newDist<=0)
					newDist=0.1f;
				float t=newDist/distLen;
				if(t>1) t=1;
				tR=t;
				//float dHgt=(1.0f-t)*safetyDistUp;
				//viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
				//targetPos.z+=dHgt;
			}
			// to left side of target
			if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos-vRight*safetyDistLook, vDirLen, 
				ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
			{
				float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
				if(newDist<=0)
					newDist=0.1f;
				float t=newDist/distLen;
				if(t>1) t=1;
				tL=t;
				//float dHgt=(1.0f-t)*safetyDistUp;
				//viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
				//targetPos.z+=dHgt;
			}
		}

		if(bRepeatTest)
#endif
		{
			tM=min(tM,min(tL,tR));
		}
		//else
		{
			tM=min(tM,min(tML,tMR));
		}
		float dHgt=(1.0f-tM)*safetyDistUp;
		viewParams.position=targetPos+Vec3(0,0,dHgt)+tM*vDirLen;
		targetPos.z+=dHgt;

#if 0
		bool bCheckLeft=false;
		bool bCheckRight=true;
		// Obstruction ahead, to right of camera/target?
		if(bCheckRight)
			//if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(viewParams.position+vRight*safetySideDist, vDirLen*-1.3f, 
			if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(viewParams.position, vRight*safetyDistUp*3, 
				ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
			{
				float newDist=rayHit.dist;   // move closer to target to get past obstruction
				float t=newDist/(safetyDistUp*3);
				//float dHgt=(t)*safetyDist;
				float dRight=(1-t)*safetyDistUp*3;
				{
					IRenderAuxGeom* pRenderAuxGeom( GetISystem()->GetIRenderer()->GetIRenderAuxGeom() ); 
					pRenderAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
					pRenderAuxGeom->DrawLine( viewParams.position+vRight*safetyDist, clrDkRed,viewParams.position+vRight*safetyDist+rayHit.dist*-vDirLen.GetNormalized() , clrDkRed );
				}
				viewParams.position+=-vRight*dRight;
			}
			// Obstruction ahead, to left of camera/target?
			if(bCheckLeft)
				if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(viewParams.position-vRight*safetyDist, vDirLen*-1.3f, 
					ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
				{
					float newDist=rayHit.dist;   // move closer to target to get past obstruction
					float t=newDist/(distLen*1.3f);
					//float dHgt=(t)*safetyDist;
					float dRight=-((1-t)*safetyDist);
					{
						IRenderAuxGeom* pRenderAuxGeom( GetISystem()->GetIRenderer()->GetIRenderAuxGeom() ); 
						pRenderAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
						pRenderAuxGeom->DrawLine( viewParams.position-vRight*safetyDist, clrDkBlue,viewParams.position-vRight*safetyDist+rayHit.dist*-vDirLen.GetNormalized() , clrDkBlue );
					}

					viewParams.position+=vRight*dRight;
				}
#endif
				// Now from right of camera =>  flag for move to left if obstr within 5 m
				// Now from left of camera => flag for move to right if obstr within 5 m
				// if bLeft, or bRight then move left or right, else move up, and point down more
	}
}
#else
void CFilmCamera::UVCollision(CPlayerG4 *pPlayer,SViewParams &viewParams)
{
}
#endif


//void CFilmCamera::UVGetDesiredCameraState(CPlayerG4 *pPlayer,SViewParams &viewParams,Vec3 &vPosOut,Quat &rotOut,float &fovOut,float &posDamping,float &angDamping)
void CFilmCamera::UVGetDesiredCameraState(CPlayerG4 *pPlayer,SViewParams &viewParams,SCamState &state)
{
	Vec3 posRef,posDesired;
	Quat rotRef,rotDesired;
	//-- 1.1 Apply player entity angle offsets and bob.

	//viewParams.rotation = GetQuatFromMat33(pPlayer->m_viewMtxFinal);

	//--- Get reference position and orientation:
	// reference pos is player pos
	posRef=pPlayer->GetEntity()->GetWorldPos();

	// if not in cover, then reference orientation is player movement orientation
	if(!pPlayer->m_modes.bCoverMode)
	{
		rotRef=Quat::CreateRotationVDir(pPlayer->GetEntity()->GetWorldTM().GetColumn(1));
	}
	else	// otherwise is defined by cover (orientation when entering cover).
	{
		Quat qRot=Quat::CreateRotationVDir(pPlayer->m_cover.vCoverDir);
		rotRef=qRot;
	}
	

	//-- 1.2 Apply third person cam parameters:- angles, distance, and horizontal and vertical offsets.    
	float thirdPersonDistance;
	float thirdPersonYaw;
	float thirdPersonPitch;
	float thirdPersonRoll;
	float thirdPersonHOff;
	float thirdPersonVOff;

	float thirdPersonYaw2=0;
	float thirdPersonPitch2=0;

	eGamePlayMode eMode=pPlayer->m_pAutoCombat->GetCurrentMode();

	{
		thirdPersonYaw2=GetISystem()->GetIConsole()->GetCVar("cl_tpvYawNav")->GetFVal();

		thirdPersonYaw2*=gf_PI/180.0f;

		thirdPersonPitch2=GetISystem()->GetIConsole()->GetCVar("cl_tpvPitchNav")->GetFVal();

		if(abs(thirdPersonPitch2)>65)
		{
			if(thirdPersonPitch2>65)
				thirdPersonPitch2=65;
			else
				thirdPersonPitch2=-65;
		}

		thirdPersonPitch2*=gf_PI/180.0f;

		TCamCut *pCut;
		const char *psName;
		float distCam;

		switch (eMode)
		{
		case eCombatMode:
			pCut=&aCamCutsFollow[COM_CAM];
			psName="Combat";
			distCam=GetISystem()->GetIConsole()->GetCVar("cl_tpvDistCombat")->GetFVal();
			break;
		default:
		case eNavigationMode:
			pCut=&aCamCutsFollow[NAV_CAM];
			psName="Follow";
			distCam=GetISystem()->GetIConsole()->GetCVar("cl_tpvDistNav")->GetFVal();
			break;
		}
		SET_CamCut(pCut);
		thirdPersonDistance=distCam;
		pEditActiveCut=pCut;
		psEditActiveCutName=psName;
	}

	float thirdPersonPosDamping;
	float thirdPersonAngDamping;

	float kVel;
	float kDist;
	float desiredFOV;

	if(eNavigationMode == eMode)
	{
		desiredFOV=GetISystem()->GetIConsole()->GetCVar("cl_tpvFOVNav")->GetFVal();
		thirdPersonPosDamping=GetISystem()->GetIConsole()->GetCVar("cl_tpvPosDampingNav")->GetFVal();
		thirdPersonAngDamping=GetISystem()->GetIConsole()->GetCVar("cl_tpvAngDampingNav")->GetFVal();
		thirdPersonHOff+=GetISystem()->GetIConsole()->GetCVar("cl_tpvHOffNav")->GetFVal();
		thirdPersonVOff+=GetISystem()->GetIConsole()->GetCVar("cl_tpvVOffNav")->GetFVal();
		kVel=GetISystem()->GetIConsole()->GetCVar("cl_tpvDeltaVelNav")->GetFVal();
		kDist=GetISystem()->GetIConsole()->GetCVar("cl_tpvDeltaDistNav")->GetFVal();

		thirdPersonYaw=0;
		thirdPersonPitch=0;
	}
	else
	{
		desiredFOV=GetISystem()->GetIConsole()->GetCVar("cl_tpvFOVCombat")->GetFVal();

		thirdPersonYaw2=0;

		//--- Nick: add pitch control back in for now.
		thirdPersonPitch=0;	//thirdPersonPitch2=0;

		thirdPersonPosDamping=GetISystem()->GetIConsole()->GetCVar("cl_tpvPosDamping")->GetFVal();
		thirdPersonAngDamping=GetISystem()->GetIConsole()->GetCVar("cl_tpvAngDamping")->GetFVal();
		thirdPersonHOff+=GetISystem()->GetIConsole()->GetCVar("cl_tpvHOffCombat")->GetFVal();
		thirdPersonVOff+=GetISystem()->GetIConsole()->GetCVar("cl_tpvVOffCombat")->GetFVal();
		kVel=GetISystem()->GetIConsole()->GetCVar("cl_tpvDeltaVel")->GetFVal();
		kDist=GetISystem()->GetIConsole()->GetCVar("cl_tpvDeltaDist")->GetFVal();
	}
	if(desiredFOV < 5.0f) desiredFOV=5.0f;
	if(desiredFOV > 178.0f) desiredFOV=178.0f;
	desiredFOV*=g_PI/180.0f;	// was Nav: 60 degrees, Combat: 45 degrees

	thirdPersonYaw+=thirdPersonYaw2;
	//thirdPersonPitch=0;//+=thirdPersonPitch2;
	thirdPersonPitch+=thirdPersonPitch2;


	SCamState desired;

	desired.pos.dist=thirdPersonDistance;
	desired.pos.pitch=thirdPersonPitch;
	desired.pos.yaw=thirdPersonYaw;

	desired.look.pitch=thirdPersonPitch;
	desired.look.yaw=thirdPersonYaw;
	desired.look.roll=thirdPersonRoll;

	desired.look.focalDist=5000;
	desired.look.focalWidth=5000;
	desired.look.focalAmount=0;

	desired.effect.fov=viewParams.fov;
	desired.effect.dofAmount=desired.look.focalAmount;
	desired.effect.dofFar=desired.look.focalDist+desired.look.focalWidth;
	desired.effect.dofNear=desired.look.focalDist-desired.look.focalWidth;

	if(desired.effect.dofNear<0.01f)
		desired.effect.dofNear=0.01f;


	rotDesired=rotRef;
	Quat rotPos=rotRef;

	float yawSph,pitchSph,distSph;
	Vec3 vDir=rotDesired*Vec3(0,-1,0);
	CartesianToSpherical(vDir,yawSph,pitchSph,distSph);
	yawSph+=desired.look.yaw;
	pitchSph+=desired.look.pitch;
	distSph=thirdPersonDistance;

#if 0
	if ((desired.look.yaw*desired.look.yaw+desired.look.roll*desired.look.roll+desired.look.pitch*desired.look.pitch)>0.00001f)
	{
		rotDesired *= Quat::CreateRotationXYZ(Ang3(desired.look.pitch,desired.look.roll,desired.look.yaw));
	}

	if ((desired.pos.yaw*desired.pos.yaw+desired.pos.pitch*desired.pos.pitch)>0.00001f)
	{
		rotPos *= Quat::CreateRotationXYZ(Ang3(desired.pos.pitch,0,desired.pos.yaw));
	}
#else
	if(distSph<0.1f)	distSph=0.1f;
	//vDir=SphericalToCartesian(yawSph,g_PI/2.0f,1.0f);
	vDir=SphericalToCartesian(yawSph,pitchSph,1.0f);

	rotDesired=Quat::CreateRotationVDir(vDir.GetNormalized());
	rotPos=Quat::CreateRotationVDir(vDir.GetNormalized());

#endif

	pPlayer->m_camViewMtxFinal = Matrix33(rotDesired);
	Matrix33 matPos=Matrix33(rotPos);

	posDesired=posRef;
	posDesired += matPos.GetColumn(1) * -thirdPersonDistance + matPos.GetColumn(2) * (pPlayer->m_params.viewHeightOffset + 0.25f);//(0.25f + m_params.viewHeightOffset + thirdPersonVOff);
	//posDesired += matPos.GetColumn(2) * (pPlayer->m_params.viewHeightOffset + 0.25f);//(0.25f + m_params.viewHeightOffset + thirdPersonVOff);


	//--------------------------------------
	state=desired;
	state.pos.vPos=posDesired;
	state.pos.posDamping=thirdPersonPosDamping;
	state.pos.angDamping=thirdPersonAngDamping;
	state.pos.horzFraming=thirdPersonHOff;
	state.pos.vertFraming=thirdPersonVOff;
	
	state.look.angDamping=thirdPersonAngDamping;
	state.look.rotation=rotDesired;
	state.look.focalAmountDamping=thirdPersonAngDamping;	// temp, needs own value
	state.look.focalDistDamping=thirdPersonPosDamping;		// temp, needs own value
	state.look.focalWidthDamping=thirdPersonPosDamping;		// temp, needs own value
	state.effect.fovDamping=thirdPersonAngDamping;				// temp, needs own value
	state.effect.fov=desiredFOV;//viewParams.fov;

}

//--- NickH 
bool gbFlattenViewDir=true; //false;

// This needs to move into SViewParams or a related struct as soon as 
// Crysis code freeze/milestone ends.
static float gDanglePrev2=0;
static float gDanglePrev=0;

// dampTime affects the rate of convergence. 
template <class TVal,class TFloat>
inline TVal InterpolateDamped(TFloat dt,TFloat dampTime,const TVal & v0, const TVal & v1, TVal & velInOut)
{
	if(dampTime<TFloat(0.001)) dampTime=TFloat(0.001);

	TFloat damping=TFloat(2)/dampTime;
	TFloat e = TFloat(exp(-damping*dt));	// exp(-x) == 1/exp(x)
	TVal dV=v0-v1;
	TVal temp=(velInOut+dV*damping)*dt;
	velInOut=(velInOut-temp*damping)*e;
	return (v1+(dV+temp)*e);
}

// Adjusts a pair of angles to maintain relative numerical/directional distance as they
// cross into/out of negative angle space.
// i.e after being adjusted they should be ready for interpolation. Assumes less than 180 degree increment per call
inline void AdjustAnglePair(float &alpha,float &alphaPrev,float &beta,float &betaPrev,int &nAlphaWraps,int &nBetaWraps)
{
	// Track which direction each angle entered/left negative angle space
	if((alpha<0) && (alphaPrev>=0))
	{
		if((alphaPrev-alpha)<180.0f*g_PI/180.0f)	// clockwise transition past 0
		{
		}
		else													// anticlockwise transition past +/- 180
		{
			++nAlphaWraps;
		}
	}
	else if((alpha>=0) && (alphaPrev<0))
	{
		if((alphaPrev+360*g_PI/180.0f-alpha)<180.0f*g_PI/180.0f)	// clockwise transition past +/- 180
			--nAlphaWraps;
	}
	
	// Track which direction each angle entered/left negative angle space
	if((beta<0) && (betaPrev>=0))
	{
		if((betaPrev-beta)<180.0f*g_PI/180.0f)	// clockwise transition past 0
		{
		}
		else													// anticlockwise transition past +/- 180
		{
			++nBetaWraps;
		}
	}
	else if((beta>=0) && (betaPrev<0))
	{
		if((betaPrev+360*g_PI/180.0f-beta)<180.0f*g_PI/180.0f)	// clockwise transition past +/- 180
			--nBetaWraps;
	}

	betaPrev=beta;
	alphaPrev=alpha;

	if((alpha+nAlphaWraps*360*g_PI/180)>(beta+nBetaWraps*360*g_PI/180))
	{
		if(alpha<beta)
				alpha+=360*g_PI/180;
	}
	else
	{
		if(beta<alpha)
			beta+=360*g_PI/180;
	}
}

void CFilmCamera::UpdateViewThirdPersonDamped(CPlayerG4 *pPlayer,SViewParams &viewParams)
{
  Vec3 targetPos=pPlayer->GetEntity()->GetWorldPos();

  targetPos.z+=0.25f;

  Vec3 prevTargetPos=viewParams.targetPos;
  EntityId idPrevTarget=viewParams.idTarget;
  EntityId idTarget=pPlayer->GetEntity()->GetId();
  Vec3 prevCamPos=viewParams.position;
  Quat prevCamRot=viewParams.rotation;
  float pos_kStiffness=15.0f;
  float pos_kDamping=0.0f;
  float ang_kStiffness=20.0f;
  float ang_kDamping=0.0f;
  float ang_maxRate=gf_PI/0.5f;   // 0.5 sec to turn cam by 180
  float prevDist=viewParams.dist;
  Vec3 vSavedTargetPos=targetPos;
	float thirdPersonPosDamping;
	float thirdPersonAngDamping;
  ang_maxRate=gf_PI/1.5f;

  eGamePlayMode eMode=pPlayer->m_pAutoCombat->GetCurrentMode();

	float dt=viewParams.frameTime;
	// View is system called by CCryAction::PreUpdate which currently passes in a max frame time of 0.1 sec
	// we'll clamp to max of 1 sec here, and base our clamping of the damping factors upon that.
	if(dt>1.0f)
		dt=1.0f;


  //
  //--- 1. Calculate the desired (undamped/instantaneous) view position and angle
  //
	SCamState state;
	//UVGetDesiredCameraState(pPlayer,viewParams,viewParams.position,viewParams.rotation,fov,thirdPersonPosDamping,thirdPersonAngDamping);
	UVGetDesiredCameraState(pPlayer,viewParams,state);

	viewParams.position=state.pos.vPos;
	viewParams.rotation=state.look.rotation;

	static float fovVel=0.0f;
	float fovPrev=viewParams.fov;

	float kFovDamp=GetISystem()->GetIConsole()->GetCVar("cl_tpvFOVDamping")->GetFVal();
	float fovDamped;

	if(kFovDamp>=0.01f)
		fovDamped=InterpolateDamped(dt,kFovDamp,fovPrev*1000,state.effect.fov*1000,fovVel)/1000;
	else
		fovDamped=state.effect.fov;

	//viewParams.fov=state.effect.fov;
	viewParams.fov=fovDamped;

	thirdPersonPosDamping=state.pos.posDamping;
	thirdPersonAngDamping=state.pos.angDamping;
	float thirdPersonHOff = state.pos.horzFraming;
	float thirdPersonVOff = state.pos.vertFraming;
	
	//viewParams.fov=state.effect.fov;

	// Hack for build
	float kVel=GetISystem()->GetIConsole()->GetCVar("cl_tpvDeltaVelNav")->GetFVal();
	float kDist=GetISystem()->GetIConsole()->GetCVar("cl_tpvDeltaDistNav")->GetFVal();


  //--- Collision ----------------------------------------------------------------
  ray_hit rayHit;
  Vec3 vDirLen=(viewParams.position-targetPos);
  float distLen = vDirLen.len();
  const float safetyDistUp=0.05f;
  const float safetyDist=0.7f;
  const float safetyDistLook=0.7f;
  const float safetySideDist=1.0f;

  if(distLen>0.01f)
  {
    Vec3 vLook=vDirLen.GetNormalizedSafe(Vec3(0,1,0));
    Vec3 vRight=-vLook.Cross(Vec3(0,0,1));
    vDirLen+=vDirLen.GetNormalized()*safetyDist;
    float tL=1,tR=1,tM=1,tMR=1,tML=1;
    //vHitDir/=distHit;

    // Obstruction between target and camera?
    //if(0)
    // centre
    if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos, vDirLen, 
      ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
    {
      float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
      if(newDist<=0.1f)
        newDist=0.1f;
      float t=newDist/distLen;
      if(t>1) t=1;
      tM=t;
      //float dHgt=(1.0f-t)*safetyDistUp;
      //viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
      //targetPos.z+=dHgt;
    }
#if 0
    int iTest=0;
    bool bRepeatTest=true;
    Vec3 vDirLenR,vDirLenL,vDirLenLR,vTestPos;
    for(iTest=0;iTest<8 && bRepeatTest;++iTest)
    {
      vDirLenLR=vDirLen*min(tML,tMR);
      vTestPos=targetPos+vDirLenLR;
      vDirLenR=((vTestPos+vRight*safetyDistLook)-targetPos);
      vDirLenL=((vTestPos-vRight*safetyDistLook)-targetPos);
      bRepeatTest=false;
      // right edge to target
      if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos, vDirLenR, 
        ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
      {
        float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
        if(newDist<=0.1f)
          newDist=0.1f;
        float t=newDist/distLen;
        if(t>1) t=1;
        tMR=t;
        bRepeatTest=true;
        //float dHgt=(1.0f-t)*safetyDistUp;
        //viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
        //targetPos.z+=dHgt;
      }
      // left edge to target
      if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos, vDirLenL, 
        ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
      {
        float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
        if(newDist<=0.1f)
          newDist=0.1f;
        float t=newDist/distLen;
        if(t>1) t=1;
        tML=t;
        bRepeatTest=true;
        //float dHgt=(1.0f-t)*safetyDistUp;
        //viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
        //targetPos.z+=dHgt;
      }
    }
    if(bRepeatTest)
    {
      // to right side of target
      if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos+vRight*safetyDistLook, vDirLen, 
        ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
      {
        float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
        if(newDist<=0)
          newDist=0.1f;
        float t=newDist/distLen;
        if(t>1) t=1;
        tR=t;
        //float dHgt=(1.0f-t)*safetyDistUp;
        //viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
        //targetPos.z+=dHgt;
      }
      // to left side of target
      if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(targetPos-vRight*safetyDistLook, vDirLen, 
        ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
      {
        float newDist=rayHit.dist-safetyDistLook;   // move closer to target to get past obstruction
        if(newDist<=0)
          newDist=0.1f;
        float t=newDist/distLen;
        if(t>1) t=1;
        tL=t;
        //float dHgt=(1.0f-t)*safetyDistUp;
        //viewParams.position=targetPos+Vec3(0,0,dHgt)+newDist*vDirLen.GetNormalized();
        //targetPos.z+=dHgt;
      }
    }

    if(bRepeatTest)
#endif
    {
      tM=min(tM,min(tL,tR));
    }
    //else
    {
      tM=min(tM,min(tML,tMR));
    }
    float dHgt=(1.0f-tM)*safetyDistUp;
    viewParams.position=targetPos+Vec3(0,0,dHgt)+tM*vDirLen;
    targetPos.z+=dHgt;

#if 0
    bool bCheckLeft=false;
    bool bCheckRight=true;
    // Obstruction ahead, to right of camera/target?
    if(bCheckRight)
    //if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(viewParams.position+vRight*safetySideDist, vDirLen*-1.3f, 
    if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(viewParams.position, vRight*safetyDistUp*3, 
      ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
    {
      float newDist=rayHit.dist;   // move closer to target to get past obstruction
      float t=newDist/(safetyDistUp*3);
      //float dHgt=(t)*safetyDist;
      float dRight=(1-t)*safetyDistUp*3;
      {
        IRenderAuxGeom* pRenderAuxGeom( GetISystem()->GetIRenderer()->GetIRenderAuxGeom() ); 
        pRenderAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
        pRenderAuxGeom->DrawLine( viewParams.position+vRight*safetyDist, clrDkRed,viewParams.position+vRight*safetyDist+rayHit.dist*-vDirLen.GetNormalized() , clrDkRed );
      }
      viewParams.position+=-vRight*dRight;
    }
    // Obstruction ahead, to left of camera/target?
    if(bCheckLeft)
    if (GetISystem()->GetIPhysicalWorld()->RayWorldIntersection(viewParams.position-vRight*safetyDist, vDirLen*-1.3f, 
      ent_terrain|ent_static,RWI, &rayHit, 1,pPlayer->GetEntity()->GetPhysics(), 0))
    {
      float newDist=rayHit.dist;   // move closer to target to get past obstruction
      float t=newDist/(distLen*1.3f);
      //float dHgt=(t)*safetyDist;
      float dRight=-((1-t)*safetyDist);
      {
        IRenderAuxGeom* pRenderAuxGeom( GetISystem()->GetIRenderer()->GetIRenderAuxGeom() ); 
        pRenderAuxGeom->SetRenderFlags( e_Def3DPublicRenderflags );
        pRenderAuxGeom->DrawLine( viewParams.position-vRight*safetyDist, clrDkBlue,viewParams.position-vRight*safetyDist+rayHit.dist*-vDirLen.GetNormalized() , clrDkBlue );
      }

      viewParams.position+=vRight*dRight;
    }
#endif
    // Now from right of camera =>  flag for move to left if obstr within 5 m
    // Now from left of camera => flag for move to right if obstr within 5 m
    // if bLeft, or bRight then move left or right, else move up, and point down more
  }

  //------------------------------------------------------------------------------

  Vec3 dir = targetPos - viewParams.position;
  float dist=dir.len();

  if(v.bWasCut || (idPrevTarget!=idTarget))  // New Camera or target ?
  {
    prevTargetPos=targetPos;
    prevCamPos=viewParams.position;
    prevCamRot=viewParams.rotation;
    prevDist=dist;
    viewParams.angleVel=0;
    viewParams.vel=0;
    v.bWasCut=false;
  }

  Vec3 vMove=targetPos-prevTargetPos;
  float moveDist=vMove.len();
  float desiredDist=dist;
  if( ((moveDist*moveDist)>0.0001f) && ((dist*dist)>0.0001f) )
  {
    Vec3 vDir=dir/dist;
    float alignedDist;
    vMove.Normalize();
    alignedDist= vMove.Dot(vDir) * moveDist;

    // used normalised vMove so we can play with the mix of dist(cam) and dist(target movement)
    if(alignedDist*dist >= 0 )  // same sign
      desiredDist+=alignedDist;
    else
      desiredDist=dist;
  }
  else
  {
    vMove=Vec3(0,1,0);
  }


  //-- viewParams position and rotation now have the desired undamped pos and rotation.

  //
  //--- 2. Apply damping to smooth the changes in position and angle
  //

  //-- 2.1. Get/Calc damping parameters.

  // Sqrt gives finer curve control for lower damping values.
  pos_kDamping = 2*(float)(cry_sqrtf(thirdPersonPosDamping));   // default: 2*sqrt(15)
  ang_kDamping = 2*(float)(cry_sqrtf(thirdPersonAngDamping));   // default: 2*sqrt(20)

	// These need to be clamped to prevent range errors further on, since they are used in an exp() expression
	// dt is clamped to a max of 1 second above.
	// e=(float) cry_expf(-ang_kDamping*0.5f*dt); // e=(float) cry_expf(-pos_kDamping*0.5f*dt);
	// limiting the input to expf to -/+40 limits our output to approx:  4e-18 .. 2e+17

	ang_kDamping = ang_kDamping<-80 ? -80 : ang_kDamping>80 ? 80 : ang_kDamping;
	pos_kDamping = pos_kDamping<-80 ? -80 : pos_kDamping>80 ? 80 : pos_kDamping;



  //-- 2.2. Calculate the desired angle and distance changes in a form suitable for damping.

  // Calc the change in angle(dangle) and the plane it moves on.
  Vec3 camdir;
  camdir = prevCamRot*Vec3(0,1,0);//viewParams.rotation * Vec3(0,1,0);
  camdir.normalize();
  Vec3 rotax;
  bool bDampAngle=false;
  Vec3 camdiry;

  if( (dist>0.001f) )
  {
    dir/=dist;
    //if( fabs(dir.Dot(camdir))<0.9999f ) // make sure the vectors aren't near_parallel
    {
      rotax = (camdir^dir);
      rotax.NormalizeSafe(Vec3(0,1,0));
			bDampAngle=true;
      camdiry = (rotax^camdir);
    }
    //else
    {
      //rotax=Vec3(0,0,1);
      //bDampAngle=false;
      //camdiry = (rotax^dir);
    }
  }
  else
  {
    dir=camdir;
    rotax=Vec3(0,0,1);
    camdiry = (rotax^camdir);
  }

	float yawPrev,pitchPrev,distPrev;
	float yawNew,pitchNew,distNew;
	float yawDamped,pitchDamped;

	float kDamp=ang_kDamping;//4/(0.1f+abs(ang_kDamping));
	// Move these
	static float yawVel=0;
	static float pitchVel=0;
	static float yawNewPrev=0;
	static float yawPrevPrev=0;
	static int nYawWrap=0;
	static int nNewWrap=0;

	CartesianToSpherical(camdir,yawPrev,pitchPrev,distPrev);
	Vec3 vTest=SphericalToCartesian(yawPrev,pitchPrev,distPrev);

	CartesianToSpherical(dir.GetNormalized(),yawNew,pitchNew,distNew);

	// Adjust angles so that interpolation will work.
	AdjustAnglePair(yawNew,yawNewPrev,yawPrev,yawPrevPrev,nNewWrap,nYawWrap);


	yawDamped=InterpolateDamped(dt,kDamp,yawPrev,yawNew,yawVel);
	pitchDamped=InterpolateDamped(dt,kDamp,pitchPrev,pitchNew,pitchVel);

	// clamp angles to max separation of 1 wrap (360+diff(yawNew,yawPrev))
	if(nNewWrap<nYawWrap)
	{
		nYawWrap-=nNewWrap;
		nNewWrap=0;
		if(nYawWrap>1)
			nYawWrap=1;
	}
	else
	{
		nNewWrap-=nYawWrap;
		nYawWrap=0;
		if(nNewWrap>1)
			nNewWrap=1;
	}

	{
		char acBuf[1024];
		Vec3 vDiff=camdir-vTest;
		sprintf(acBuf,"Yaw:(%0.3f(%d) .. %0.3f(%d) => %0.3f vel:%0.3f)\n",
			yawPrev*180.0f/g_PI,nYawWrap,
			yawNew*180.0f/g_PI,nNewWrap,
			yawDamped*180.0f/g_PI,yawVel*180.0f/g_PI);
		//CryLogAlways(acBuf);
		//pPlayer->CreateActorEvent("printhud",0,acBuf);
	}

	Vec3 vDirDamped=SphericalToCartesian(yawDamped,pitchDamped,1.0f);



  if(bDampAngle)
  {
		camdiry.NormalizeSafe(Vec3(0,1,0));
		//float dangle=0;
    float dangle = cry_acosf(min(1.0f,max(-1.0f,camdir*dir.GetNormalized())));
    Vec3 camdirTmp = camdir*(float)(cry_cosf(dangle))+camdiry*(float)(cry_sinf(dangle));
    camdirTmp.Normalize();
    Vec3 camposTmp = targetPos+camdirTmp*-dist;

    float thetaTmp=camdirTmp*camdir;
    //thetaTmp*=180.0f/gf_PI;
    float thetaDir=camdir*dir;
    Vec3 vUp;

    //-----------------------------------------------

    //-- 2.3. Damp the change of angle

    float e,v0,C1,C2;

    // Angle damping.
    // in: dAngle = desired angle change. viewParams.angleVel = prev rate of change of angle.

    // Rate of turn no higher than ang_maxRate
    if((fabs(dangle)/viewParams.frameTime)>ang_maxRate)
    {
//      if(dangle<0)
//        dangle=-ang_maxRate*viewParams.frameTime;
//      else
//        dangle=ang_maxRate*viewParams.frameTime;
    }

    if (dangle>gf_PI/2)
    {
  //    dangle = gf_PI/2;
    }
    else if (dangle<-gf_PI/2)
    {
  //    dangle=-gf_PI/2;
    }

		float dangleDesired=0;
		dangleDesired=dangle;
    if (ang_kDamping==0)
    {
      //dangle = 0;
			e=1.0f;
    }
    else
    {
			//dangle=InterpolateDamped(dt,4/ang_kDamping,dangle,(float)0,viewParams.angleVel);

			// This is subject to float range errors, see the clamping of dt and
			// ang_kDamping above if you change this expression.
      e=(float) cry_expf(-ang_kDamping*0.5f*dt);
      v0 = viewParams.angleVel*0.0;
      C1 = dangle;
      C2 = v0 + C1*ang_kDamping*0.5f;
      dangle = 0 + C1*e + C2*dt*e;		// desired angle is zero
      viewParams.angleVel = -C1*ang_kDamping*0.5f*e + C2*(e-ang_kDamping*0.5f*dt*e);
    }
		dangle=dangleDesired;
		if(dangle<0)
			dangle=max(dangle,(float)-g_PI/2.01f);
		else
			dangle=min(dangle,(float)g_PI/2.01f);

		{
			char acBuf[1024];
			sprintf(acBuf,"dangle:%0.3f (%0.3f)  (e:%f) (k:%0.3f)\n",dangle,dangleDesired,e,ang_kDamping);
//			CryLogAlways(acBuf);
			//pPlayer->CreateActorEvent("printhud",0,acBuf);
		}

		gDanglePrev2=gDanglePrev;
		gDanglePrev=dangle;
		
    //-- dangle is output angle from new desired angle towards old angle. i.e. (dangle==0) => at target

    //-- 2.4. Damp the change in position

    // Position damping.

    // in: dist = desired dist. dDist = prevDist-desiredDist. viewParams.vel = prev rate of change of dist.
    {
      float dDistVel;
      float vel=(vSavedTargetPos-prevTargetPos).len()/dt;
      if(kVel<1.0f) kVel=1.0f;
      float tVel=vel/kVel;
      tVel=min(1.0f,tVel);
      dDistVel=tVel*kDist;  // damping proveded below
      
			// This changes the desired dist from player/target based on player/target speed
      desiredDist+=dDistVel;

			//newDist=InterpolateDamped(dt,4/pos_kDamping,prevDist,desiredDist,viewParams.vel);

			// This is subject to float range errors, see the clamping of dt and
			// ang_kDamping above if you change this expression.
      e = (float) cry_expf(-pos_kDamping*0.5f*dt);
      v0 = viewParams.vel;// - (dist-m_sParam.m_cur_cam_dist)/dt;
      //prevDist=(targetPos - prevCamPos).len();
      //C1 = prevDist-dist; //dist-cam_dist;
      C1 = prevDist - desiredDist;
      C2 = v0 + C1*pos_kDamping*0.5f;
      //float prevDist=0;
      float newDist;
      //dist;
      //newDist = dist + C1*e + C2*dt*e;
      newDist = desiredDist + C1*e + C2*dt*e;

      //float dDist=fabs(newDist-dist);
      float dDist=fabs(newDist-desiredDist);
   //   if( dDist < 10)
      {
        dist=newDist;
        viewParams.vel = -C1*pos_kDamping*0.5f*e + C2*(e-pos_kDamping*0.5f*dt*e);
      }
    }
    //-- dist is output dist from target.

    //-- 2.5. convert the damped angle and position back into the form required for output.

    //if(bDampAngle)
    {
      Quat q;
      //camdir = camdir*(float)(cry_cosf(dangle))+camdiry*(float)(cry_sinf(dangle));
#if 0
			camdir = camdir*(float)(cry_cosf(dangle))+camdiry*(float)(cry_sinf(dangle));
#else
			camdir=vDirDamped;
			//camdir=dir.GetNormalized();
#endif
      q.SetRotationVDir(camdir.GetNormalized());

      //prevCamRot*=q;
      viewParams.rotation=q;
      viewParams.position = targetPos+camdir*-dist;
      viewParams.dist=dist;
    }

  }

  //--- Screen offsets. Not part of the damping.

  pPlayer->m_camViewMtxFinal = Matrix33(viewParams.rotation);

// Put this back..
 viewParams.position += pPlayer->m_camViewMtxFinal.GetColumn(0)*(-thirdPersonHOff) + pPlayer->m_camViewMtxFinal.GetColumn(2) * (thirdPersonVOff);

  if(gbFlattenViewDir)
  {
    if(eNavigationMode == eMode)
    {
      float pitchOff=GetISystem()->GetIConsole()->GetCVar("cl_tpvPitchNavOffset")->GetFVal();

      // Flatten rotation back onto Z plane, then apply pitch
      pPlayer->m_camViewMtxFinal=Matrix33(viewParams.rotation);
      //  FlattenRotationMatrixToZPlane(pPlayer->m_viewMtxFinal);
      viewParams.rotation=GetQuatFromMat33(pPlayer->m_camViewMtxFinal);
      viewParams.rotation*=Quat::CreateRotationXYZ(Ang3(pitchOff*(gf_PI/180.0f),0,0));
    }
  }

	// Where the camera's looking
	pPlayer->m_camViewMtxFinal=Matrix33(viewParams.rotation);
	// Where the player's looking
  pPlayer->m_viewMtxFinal = Matrix33(viewParams.rotation);
	
	if(!pPlayer->m_modes.bCoverMode)
	{
		//pPlayer->m_viewMtxFinal = Matrix33(viewParams.rotation);
	}
  //

  //vOldDir=viewParams.rotation*(vOldDir*prevCamRot);//vOldDir;
  viewParams.targetPos=targetPos;
  viewParams.idTarget=idTarget;
}

bool m_bWasCameraCut;

SDofState gDofState;  // hack - used to store Dof state round cam shots. to be replaced by an effect state bitmap stack similar to render states.

// Returns true if the view has been handled
bool CFilmCamera::UpdateViewCameraShot(CPlayerG4 *pPlayer,SViewParams &viewParams)
{
  bool bRet=false;

  if (pPlayer->m_bCameraCut)
  {
    // Newly requested shot
    if(!m_bWasCameraCut || m_pRequestedNode)
    {
      //SetShot("GUN.1.2");
      const char *psShotName;
      if(m_pRequestedNode)
      {
        psShotName=m_pRequestedNode->acShotName;
      }
      else
      {
        int iCut=GetISystem()->GetIConsole()->GetCVar("cl_cam_cut")->GetIVal();
        psShotName=PickShotByIndex(iCut)->psName;
      }
      SetShot(psShotName);
      m_pRequestedNode=0;
    }

    if(m_pActiveNode)
    {
      int iEditHold=GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->GetIVal();
      if(!m_pActiveNode->pShotHandler->Tick(viewParams) && !iEditHold)
      {
        // shot failed for some reason.
        g_pGame04->GetDirector()->ShotFailed(m_pActiveNode);
      }
    }
    m_bWasCameraCut=true;
    viewParams.idTarget=0;
    bRet=true;
  }
  else if (m_bWasCameraCut)
  {
    v.bWasCut=true;
    m_bWasCameraCut=false;
  }
  else
  {
    v.bWasCut=false;

    // for debugging
    if(bCamDebugDraw || bCamConsoleDebugDraw)
    {
      int iCut=GetISystem()->GetIConsole()->GetCVar("cl_cam_cut")->GetIVal();
      const char *psShotName=PickShotByIndex(iCut)->psName;
      TestShot(psShotName);
    }
  }

  return bRet;
}

// Returns true if the view has been handled
bool CFilmCamera::UpdateView2(CPlayerG4 *pPlayer,SViewParams &viewParams)
{
  bool bRet=false;
  //SPlayerView v;  // refactoring struct

  if (!pPlayer->GetEntity())
    return false;

  if(UpdateViewCameraShot(pPlayer,viewParams))
    return true;

  v.pVehicle = pPlayer->GetLinkedVehicle();

  if(!pPlayer->IsThirdPerson() || v.pVehicle || pPlayer->m_stats.followCharacterHead)
    return false;

	bRet=true;

  v.frameTime=min(GetISystem()->GetITimer()->GetFrameTime(),0.1f);

  v.lastPos=viewParams.position;
  v.lastQuat=viewParams.rotation;

  v.defaultFov=GetISystem()->GetIConsole()->GetCVar("cl_fov")->GetFVal();

	//v.defaultFov=90;

  v.bobMul=GetISystem()->GetIConsole()->GetCVar("cl_bob")->GetFVal() * pPlayer->m_params.headBobbingMultiplier;

  v.usePivot=(pPlayer->m_params.viewPivot.len2() > 0) && !pPlayer->IsThirdPerson();

  viewParams.fov = v.defaultFov*pPlayer->m_params.viewFoVScale*(gf_PI/180.0f);
  viewParams.nearplane = 0.0f;
	
	/*	
  if (pPlayer->m_lastPos.len2()>0.01f)
  {
    pPlayer->m_blendViewOffset = pPlayer->m_lastPos - pPlayer->GetEntity()->GetWorldPos(); 
    pPlayer->m_lastPos.Set(0,0,0);
  }	
	*/

  v.wAngles=Ang3(0,0,0);

  v.pCharacter = pPlayer->GetEntity()->GetCharacter(0);
  

  //if m_firstPersonBody is set to follow the head bone just override the position
  {
    v.m_viewMtxForWeapon=pPlayer->m_viewMtxFinal;

    v.eyeOffsetGoal=pPlayer->GetStanceViewOffset(pPlayer->m_stance);

    if(pPlayer->IsThirdPerson())
    {
      UpdateViewThirdPersonDamped(pPlayer,viewParams);
    }

    // adjust eye offset due to head bobbing, and stance changes etc.
    Interpolate(pPlayer->m_eyeOffset,v.eyeOffsetGoal,10.0f,v.frameTime);

    //apply eye offset
    if (!v.usePivot)
    {
      //m_eyeOffset represent the shift on the side/forward/up (x,y,z)
      viewParams.position += pPlayer->m_baseMtx * pPlayer->m_eyeOffset;//m_baseMtx.GetColumn(0) * m_eyeOffset.x + m_baseMtx.GetColumn(1) * m_eyeOffset.y + m_baseMtx.GetColumn(2) * m_eyeOffset.z;

      if (pPlayer->m_stats.firstPersonBody==2)
      {
        float lookDown(pPlayer->m_viewMtxFinal.GetColumn(1) * pPlayer->m_baseMtx.GetColumn(2));
        float forwardOffset(0.0f);
        if (pPlayer->m_stance == STANCE_STAND)
          forwardOffset = 0.15f;

        viewParams.position += pPlayer->m_viewMtxFinal.GetColumn(1) * max(-lookDown,0.0f) * forwardOffset + pPlayer->m_viewMtxFinal.GetColumn(2) * max(-lookDown,0.0f) * 0.0f;
      }
    }

    //FIXME: this should be done in the player update anyway.
    //And all the view position update. (because the game may need to know other players eyepos and such)
    //upadte first person weapon model position/angles

		/*
    Quat wQuat(v.m_viewMtxForWeapon * Matrix33::CreateRotationXYZ(pPlayer->m_FPWeaponAngleOffset * gf_PI/180.0f));
    wQuat *= Quat::CreateSlerp(viewParams.shakeQuat,Quat(IDENTITY),0.5f);
    wQuat.Normalize();

    v.wAngles = Ang3(wQuat);
		*/
  }

	//if necessary blend the view
	if (pPlayer->m_stats.viewIDLast != pPlayer->m_stats.viewID)
	{
		pPlayer->m_blendViewOffset = v.lastPos - viewParams.position;

		pPlayer->m_blendViewRotOffset = (v.lastQuat / viewParams.rotation).GetNormalized();

		pPlayer->m_stats.viewIDLast = pPlayer->m_stats.viewID;
	}
	else
  {
    //Interpolate(pPlayer->m_blendViewOffset,Vec3(0,0,0),5.0f,v.frameTime);
		Interpolate(pPlayer->m_blendViewOffset,Vec3(0,0,0),10.0f,v.frameTime);
    pPlayer->m_blendViewRotOffset = Quat::CreateSlerp( pPlayer->m_blendViewRotOffset, Quat(IDENTITY), v.frameTime * 5.0f );
  }

  //if (v.pVehicle || pPlayer->m_stats.followCharacterHead)
  //{
  //  pPlayer->m_viewMtx = pPlayer->m_viewMtxFinal = Matrix33(viewParams.rotation.GetNormalized());
  //  pPlayer->m_input.viewVector = pPlayer->m_viewMtxFinal.GetColumn(1);
  //}

  //set first person weapon position/rotation
  pPlayer->m_stats.FPWeaponAngles = v.wAngles;
  pPlayer->m_stats.FPWeaponPos = viewParams.position + pPlayer->m_FPWeaponOffset;

  pPlayer->m_stats.FPSecWeaponAngles =v.wAngles;
  pPlayer->m_stats.FPSecWeaponPos = viewParams.position + pPlayer->m_FPWeaponOffset;

	/*
  if (pPlayer->m_viewShake.amount>0.001f)
  {
    viewParams.shakeDuration = pPlayer->m_viewShake.duration;
    viewParams.shakeFrequency = pPlayer->m_viewShake.frequency;
    viewParams.shakeAmount = pPlayer->m_viewShake.angle;

    memset(&pPlayer->m_viewShake,0,sizeof(SViewShake));
  }
	*/

  Quat hudQuat(Matrix33(viewParams.rotation) * Matrix33::CreateRotationXYZ(pPlayer->m_params.hudAngleOffset));
  //hudQuat *= Quat::CreateSlerp(viewParams.shakeQuat,Quat(IDENTITY),0.05f);
  hudQuat.Normalize();

  Matrix33 hudMtx(hudQuat);

  pPlayer->m_stats.HUDAngles = Ang3::GetAnglesXYZ(hudMtx);
  pPlayer->m_stats.HUDPos = viewParams.position - pPlayer->m_FPWeaponOffset * 0.3f;
  pPlayer->m_stats.HUDPos += hudMtx.GetColumn(2)*pPlayer->m_params.hudOffset.z;
  pPlayer->m_stats.HUDPos += hudMtx.GetColumn(1)*pPlayer->m_params.hudOffset.y;
  pPlayer->m_stats.HUDPos += hudMtx.GetColumn(0)*pPlayer->m_params.hudOffset.x;

  Script::CallMethod(pPlayer->GetEntity()->GetScriptTable(), "OnUpdateView", v.frameTime);

	GetISystem()->GetIGame()->GetIGameFramework()->GetIGameTokenSystem()->SetOrCreateToken("hud.offsetx",TFlowInputData(pPlayer->m_FPWeaponOffset.x,true));
	GetISystem()->GetIGame()->GetIGameFramework()->GetIGameTokenSystem()->SetOrCreateToken("hud.offsety",TFlowInputData(pPlayer->m_FPWeaponOffset.z,true));

  pPlayer->m_stats.shakeAmount = viewParams.shakingRatio;

  //for testing sake
  float nearPlaneOverride(GetISystem()->GetIConsole()->GetCVar("cl_nearPlane")->GetFVal());
  if (nearPlaneOverride > 0.001f)
    viewParams.nearplane = nearPlaneOverride;

	//FIXME:find a better place?
	/*
	if (GetEntity()->GetSlotFlags(2) & ENTITY_SLOT_RENDER)
	{
		Matrix34 handsMtx(m_viewMtxFinal * Matrix33::CreateRotationZ(gf_PI),viewParams.position);
		GetEntity()->SetSlotLocalTM(2,GetEntity()->GetWorldTM().GetInverted() * handsMtx);
		ICharacterInstance *pHands = GetEntity()->GetCharacter(2);
		if (pHands)
		{
			pHands->GetISkeleton()->SetAlwaysUpdate(1);
			pHands->Update(handsMtx, GetISystem()->GetViewCamera(), 0);
		}
	}
	*/

  return bRet;
}

void CFilmCamera::UpdateCinematicIntensity(float intensity)
{

  if(intensity<0.5f)  // expand the lower intensity range for now
  {
      intensity*=1+(1-(intensity/0.5f));
  }
  else
    intensity=0.5f;

  float desat=(1-intensity*2)*0.75f; // start desat, and gain colour
  float delta=min(fabs(desat-m_desat),m_dt/64.0f);   // 64 sec for 0..1 change
  m_desat= m_desat + desat/fabs(desat) * delta;
  SetDesat(desat,clrCinematicIntensity);

  TickDOF();
}

void CFilmCamera::CalcLineOfAction()  // Invoked a max of once per frame.
{
  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  IEntity *pEntPlayer=g_pGame04->GetClientPlayer()->GetEntity();
  CAutoCombat *pAuto=0;
  IActor *pActCombat=0;
  IEntity *pEntCombat=0;
  IEntity *pEnt=0;
  int iCast;
  EntityId idFrom;
  Vec3 vFrom;
  Vec3 vTo;

  vFrom=pEntPlayer->GetPos();
  idFrom=pEntPlayer->GetId();

  for(iCast=0;iCast<FilmShot_MaxActors;++iCast)
  {
    if(m_cast.aIdActors[iCast] && (idFrom != m_cast.aIdActors[iCast]))
    {
      pEnt=GetISystem()->GetIEntitySystem()->GetEntity(m_cast.aIdActors[iCast]);
      if(pEnt)
        break;
    }
  }

  if(!pEnt)
  {
    pAuto=pPlayer->GetAutoCombat(); // Auto combat?
    if(pAuto)
    {
      pEntCombat=pAuto->GetLastAiming();    // Auto combat has/had a target ?
      if(pEntCombat)
        if((pEntCombat->GetPos()-vFrom).len2()<400) // target is less than 20 metres away ?
        {
          pActCombat=g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pEntCombat->GetId());
          if(pActCombat && pActCombat->GetHealth()>0) // target is alive ?
            pEnt=pEntCombat;
        }
    }
  }

  if(pEnt)
  {
    vTo=pEnt->GetPos();
    m_vLineOfAction=-(vTo-vFrom);
  }
  else
  {
//SAIBodyInfo bodyInfo;
//pPlayer->GetActorInfo(bodyInfo);

    m_vLineOfAction=pEntPlayer->GetRotation()*Vec3(0,1,0); //bodyInfo.vEyeDir;
  }
  
  m_vLineOfAction.Normalize();

  m_bLineOfAction=true;

  if(pEnt)
    DrawLineV1V2(vFrom,vTo,clrOrange,clrGreen);
  else
    DrawLineV1Dir(vFrom,-m_vLineOfAction,800,clrCyan,clrBlue);
}


CFilmCamera::CFilmCamera(CFilmDirector *pDirector,CGame *pGame)
{
  m_pDirector=pDirector;
  m_pGame=pGame;
  m_bInit=false;
  m_pActiveNode=0;
  memset(&m_cast,0,sizeof(m_cast));
  m_desat=0.0f;
  m_dt=0.0f;
  m_pRequestedNode=0;
}

void CFilmCamera::OnEditorSetGameMode(bool bGameMode)
{
  memset(&m_cast,0,sizeof(m_cast));
  m_deadPlayerEntities.clear();
  m_deadPlayers.clear();
  m_pActiveNode=0;
  m_pRequestedNode=0;
}

void CFilmCamera::PlayerDeleted(CPlayerG4 *pPlayer)
{
  m_deadPlayerEntities.push_front(pPlayer->GetEntity());
  m_deadPlayers.push_front(pPlayer);
}

void CFilmCamera::Update(float frameTime)
{
  m_bLineOfAction=false;

  bCamConsoleDebugDraw=GetISystem()->GetIConsole()->GetCVar("cl_cam_debug")->GetIVal() ? true : false;

  if(!m_bInit)
  {
    // Init the shot database -- will come from a data file in near future
    SFilmCamShotNode shotNode;

    int iShot,nShot;
    
    for(iShot=0,nShot=nNamedShots ; iShot<nNamedShots ; ++iShot)
    {
      switch(aNamedShots[iShot].cut.iSubject)
      {
      case CAM_PLAYER :
      case CAM_PLAYERHEAD :
      case CAM_PLAYERWEAP :
        shotNode.pShotHandler=new CFilmCamShot_PlayerFrontShoot();
        break;
      default:
      case CAM_TARGET :
      case CAM_TARGETHEAD :
      case CAM_TARGETWEAP :
        shotNode.pShotHandler=new CFilmCamShot_PlayerTargetImpact();
        break;
      }

      // Add the various cuts for shot type GUN.1.2
      //shotNode.pShotHandler=new CFilmCamShot_PlayerFrontShoot();

      strncpy(shotNode.acShotName,aNamedShots[iShot].psName,sizeof(shotNode.acShotName));
      shotNode.acShotName[sizeof(shotNode.acShotName)-1]=0;

      shotNode.pShotHandler->Init(this,shotNode.acShotName);

      shotNode.pShotHandler->AddCamCut(aNamedShots[iShot].cut);
      m_shotTable.push_back(shotNode);
    }
#if 0
    {
      // Add the various cuts for shot type GUN.1.3
      shotNode.pShotHandler=new CFilmCamShot_PlayerTargetImpact();
      shotNode.pShotHandler->Init(this);

      strncpy(shotNode.acShotName,"GUN.1.3",sizeof(shotNode.acShotName));
      shotNode.acShotName[sizeof(shotNode.acShotName)-1]=0;

      int iCut;
      for(iCut=0;iCut<nCamCutsDeath;++iCut)
        shotNode.pShotHandler->AddCamCut(aCamCutsDeath[iCut]);

      m_shotTable.push_back(shotNode);
    }
#endif
    m_bInit=true;
  }

  // For console notification of which shot has been selected via "cl_cam_cut"
  static int iCutPrev=-1;
  int iCut=GetISystem()->GetIConsole()->GetCVar("cl_cam_cut")->GetIVal();
  const char *psShot;
  if(iCut!=iCutPrev)
  {
    iCutPrev=iCut;
    psShot=PickShotByIndex(iCut)->psName;
    CryLogAlways("*** Next shot: '%s' ***",psShot);
  }
}

void CFilmCamera::SetCast(CFilmEvent &event)
{
  CFilmShotCast cast;
  memset(&m_cast,0,sizeof(m_cast));
  int iCast,nCast;

  nCast=min( (sizeof(cast.aIdActors)/sizeof(cast.aIdActors[0])) , (sizeof(event.aIdActors)/sizeof(event.aIdActors[0])) );

  for(iCast=0 ; iCast<nCast ; ++iCast)
    m_cast.aIdActors[0]=event.aIdActors[0];
  m_cast.vInterestPoint=event.vPos;
}

void CFilmCamera::SetCast(CFilmShotCast &rCast)
{
  m_cast=rCast;
}


void CFilmCamera::DumpShots()
{
  char acCamSetup[1024];
  TCamShotNodesIter itShot,itShotEnd;
  SFilmCamShotNode *pNode=0;
  TCamCut *pCut;
  bool bRet=false;

  for(itShot=m_shotTable.begin(),itShotEnd=m_shotTable.end() ; itShot!=itShotEnd ; ++itShot)
  {
      pNode=&(*itShot);
      pCut=&pNode->pShotHandler->GetActiveCamCut();
      sprintf(acCamSetup,"  CAM_DHPRVY(%ff,%ff,%ff,%ff,%ff,%ff,%ff,%d,%d,%d)",
        pCut->Dist,pCut->hOff,pCut->pitch,pCut->roll,pCut->vOff,pCut->yaw,pCut->intensity,pCut->iSubject,pCut->iDof,pCut->iType);
      CryLogAlways("--- Camera '%s' : %s",(*itShot).acShotName,acCamSetup);
  }
}

int nShotCalls=0;
void CFilmCamera::NotifyBeginShot()
{
  ++nShotCalls;
  if(1!=nShotCalls)
  {
    nShotCalls=1; // put a breakpoint here!
  }
  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  if(pPlayer)
  {
    pPlayer->BeginPlayerCameraCut();
    pPlayer->m_bCameraCut=true;
  }
  GetDOF(gDofState);
}

void CFilmCamera::NotifyEndShot()
{
  --nShotCalls;
  if(0!=nShotCalls)
  {
    nShotCalls=0; // put a breakpoint here
  }
  SetDOF(gDofState);
  SetDOF(0,0.1f,2000.0f,3000.0f);
  CPlayerG4 *pPlayer=g_pGame04->GetClientPlayer();
  if(pPlayer)
    pPlayer->EndPlayerCameraCut();
  memset(&m_cast,0,sizeof(m_cast));
  m_bWasCameraCut=false;
  m_pRequestedNode=0;
}

bool CFilmCamera::TestShot(const char *psShotName)
{
  TCamShotNodesIter itShot,itShotEnd;
  SFilmCamShotNode *pNode=0;
  bool bRet=false;

  for(itShot=m_shotTable.begin(),itShotEnd=m_shotTable.end() ; itShot!=itShotEnd ; ++itShot)
  {
    if(0==strcmp(psShotName,(*itShot).acShotName))
    {
      pNode=&(*itShot);
      break;
    }
  }

  if(pNode)
  {
    //SCamShotParams shotParams;
    float quality;

    psError=0;
    bRet=pNode->pShotHandler->CheckRequirements(&quality);
    if(psError)
      CryLogAlways("Error testing shot '%s': '%s'",pNode->acShotName,psError);
  }

  return bRet;
}


void CFilmCamera::RequestShot(const char *psShotName)
{
  TCamShotNodesIter itShot,itShotEnd;
  SFilmCamShotNode *pNode=0;
  bool bRet=false;

  if(psShotName)
  {
    for(itShot=m_shotTable.begin(),itShotEnd=m_shotTable.end() ; itShot!=itShotEnd ; ++itShot)
    {
      if(0==strcmp(psShotName,(*itShot).acShotName))
      {
        pNode=&(*itShot);
        break;
      }
    }
    m_pRequestedNode=pNode;
  }
  else
    m_pRequestedNode=0;
}
bool CFilmCamera::SetShot(const char *psShotName)
{
  TCamShotNodesIter itShot,itShotEnd;
  SFilmCamShotNode *pNode=0;
  bool bRet=false;

  for(itShot=m_shotTable.begin(),itShotEnd=m_shotTable.end() ; itShot!=itShotEnd ; ++itShot)
  {
    if(0==strcmp(psShotName,(*itShot).acShotName))
    {
      pNode=&(*itShot);
      break;
    }
  }

  if(pNode)
  {
    SCamShotParams shotParams;
    float quality;

    psError=0;
    pNode->pShotHandler->CheckRequirements(&quality);
    if(psError)
      CryLogAlways("Error testing shot '%s': '%s'",pNode->acShotName,psError);

    if(quality>0.1f)
    {
      bRet=pNode->pShotHandler->Begin(shotParams);
      if(bRet)
        m_pActiveNode=pNode;
    }

    CryLogAlways("SetShot: '%s' (quality:%0.3f)",psShotName,quality);
  }

  return bRet;
}

bool CFilmCamera::TickCamera(CPlayerG4 *pPlayer,SViewParams &viewParams)
{
  bool bRet;

  m_dt=viewParams.frameTime;

  UpdateCinematicIntensity(m_pDirector->GetIntensity());

  bRet=UpdateView2(pPlayer,viewParams);

  CalcLineOfAction();

  return bRet;
}

float _editScale=1.0f; // hack for finding a suitable value whilst debugging.
void CFilmCamera::EditKey(const char *psName,float value)
{
  int iEditHold=GetISystem()->GetIConsole()->GetCVar("cl_cam_edithold")->GetIVal();
  float editScale=_editScale;
  
  if( iEditHold )
  {
    if(!strcmp("start",psName))
    {
      // just entered edit mode
      editSaveCut=*pEditActiveCut;
      if(psEditActiveCutName)
        CryLogAlways("Editing shot: '%s'",psEditActiveCutName);
      else
        CryLogAlways("Editing unknown shot");

      bCamDebugDraw=true;
      m_editSavedTimeStep=GetISystem()->GetIConsole()->GetCVar("fixed_time_step")->GetFVal();
      GetISystem()->GetIConsole()->GetCVar("fixed_time_step")->Set(-0.004f);
    }
    else if(!strcmp("editcam_dleft",psName))
    {
    }
    else if(!strcmp("editcam_dright",psName))
    {
    }
    else if(!strcmp("editcam_dup",psName))
    {
      float delta=fabs(pEditActiveCut->Dist)*0.02f;
      delta=max(delta,0.2f);
      pEditActiveCut->Dist+=value*delta*_editScale;
      bEditRefresh=true;
    }
    else if(!strcmp("editcam_ddown",psName))
    {
      float delta=fabs(pEditActiveCut->Dist)*0.02f;
      delta=max(delta,0.2f);
      pEditActiveCut->Dist-=value*delta*_editScale;
      bEditRefresh=true;
    }
    else if (!strcmp("rotateyaw",psName))
    {
      pEditActiveCut->yaw+=value*_editScale;
      bEditRefresh=true;
    }
    else if (!strcmp("moveud",psName))
    {
      pEditActiveCut->pitch+=value*_editScale;
      bEditRefresh=true;
    }
    else if (!strcmp("movelr",psName))
    {
      pEditActiveCut->hOff+=value*_editScale;
      bEditRefresh=true;
    }
    else if (!strcmp("rotatepitch",psName))
    {
      pEditActiveCut->vOff+=value*_editScale;
      bEditRefresh=true;
    }
    else if (!strcmp("attack1",psName))
    {
      bSavedDebugDraw=!bSavedDebugDraw;
      bCamDebugDraw=bSavedDebugDraw;
      bEditRefresh=true;
    }
    else if (!strcmp("use",psName))
    {
      // restore
      *pEditActiveCut=editSaveCut;
      bEditRefresh=true;
    }
  }
  else
  {
    static int iCount=0;
    char acCamSetup[1024];
    // format suitable for copy and paste into the cam cut table
    sprintf(acCamSetup,"CAM_DHPRVY(%ff,%ff,%ff,%ff,%ff,%ff,CAM_ILOW,CAM_TARGET,CAM_DOFMED,CAM_PAN)",
      pEditActiveCut->Dist,pEditActiveCut->hOff,pEditActiveCut->pitch,pEditActiveCut->roll,pEditActiveCut->vOff,pEditActiveCut->yaw);
    CryLogAlways("--- Saved camera: '%s' ------------------------------",psEditActiveCutName);
    CryLogAlways(acCamSetup);
    // Restore the time step.
    GetISystem()->GetIConsole()->GetCVar("fixed_time_step")->Set(0);

    CFilmEvent ev;
    ev.type=eEndDeathCut;
    g_pGame04->GetDirector()->Event(ev);

  }
}

bool CFilmCamera::IsValidPlayer(IEntity *pPlayer)
{
  bool bRet=true;

  TDeadPlayerEntitiesIter itPlay,itEnd;
  for(itPlay=m_deadPlayerEntities.begin(),itEnd=m_deadPlayerEntities.end() ; itPlay != itEnd ; ++itPlay)
  {
    if(*itPlay == pPlayer)
    {
      bRet=false;
      break;
    }
  }

  return bRet;
}

bool CFilmCamera::IsValidPlayer(CPlayerG4 *pPlayer)
{
  bool bRet=true;

  TDeadPlayersIter itPlay,itEnd;
  for(itPlay=m_deadPlayers.begin(),itEnd=m_deadPlayers.end() ; itPlay != itEnd ; ++itPlay)
  {
    if(*itPlay == pPlayer)
    {
      bRet=false;
      break;
    }
  }

  return bRet;
}
void CFilmCamera::EnableEffects(bool bOn)
{
  gbEnableEffects=bOn;
}
