summaryrefslogtreecommitdiffstats
path: root/src/core/Camera.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/Camera.cpp')
-rw-r--r--src/core/Camera.cpp1301
1 files changed, 1301 insertions, 0 deletions
diff --git a/src/core/Camera.cpp b/src/core/Camera.cpp
new file mode 100644
index 00000000..660be333
--- /dev/null
+++ b/src/core/Camera.cpp
@@ -0,0 +1,1301 @@
+#include "common.h"
+#include "patcher.h"
+#include "main.h"
+#include "Draw.h"
+#include "World.h"
+#include "Vehicle.h"
+#include "Ped.h"
+#include "PlayerPed.h"
+#include "Pad.h"
+#include "General.h"
+#include "ZoneCull.h"
+#include "SurfaceTable.h"
+#include "MBlur.h"
+#include "Camera.h"
+
+const float DefaultFOV = 70.0f; // beta: 80.0f
+
+CCamera &TheCamera = *(CCamera*)0x6FACF8;
+bool &CCamera::m_bUseMouse3rdPerson = *(bool *)0x5F03D8;
+
+WRAPPER void CCamera::DrawBordersForWideScreen(void) { EAXJMP(0x46B430); }
+WRAPPER void CCamera::CalculateDerivedValues(void) { EAXJMP(0x46EEA0); }
+WRAPPER void CCamera::Restore(void) { EAXJMP(0x46F990); }
+WRAPPER void CCamera::SetWidescreenOff(void) { EAXJMP(0x46FF10); }
+
+bool
+CCamera::IsSphereVisible(const CVector &center, float radius, const CMatrix *mat)
+{
+ RwV3d c;
+ c = *(RwV3d*)&center;
+ RwV3dTransformPoints(&c, &c, 1, &mat->m_matrix);
+ if(c.y + radius < CDraw::GetNearClipZ()) return false;
+ if(c.y - radius > CDraw::GetFarClipZ()) return false;
+ if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > radius) return false;
+ if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > radius) return false;
+ if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > radius) return false;
+ if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > radius) return false;
+ return true;
+}
+
+bool
+CCamera::IsPointVisible(const CVector &center, const CMatrix *mat)
+{
+ RwV3d c;
+ c = *(RwV3d*)&center;
+ RwV3dTransformPoints(&c, &c, 1, &mat->m_matrix);
+ if(c.y < CDraw::GetNearClipZ()) return false;
+ if(c.y > CDraw::GetFarClipZ()) return false;
+ if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > 0.0f) return false;
+ if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > 0.0f) return false;
+ if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > 0.0f) return false;
+ if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > 0.0f) return false;
+ return true;
+}
+
+bool
+CCamera::IsBoxVisible(RwV3d *box, const CMatrix *mat)
+{
+ int i;
+ int frustumTests[6] = { 0 };
+ RwV3dTransformPoints(box, box, 8, &mat->m_matrix);
+
+ for(i = 0; i < 8; i++){
+ if(box[i].y < CDraw::GetNearClipZ()) frustumTests[0]++;
+ if(box[i].y > CDraw::GetFarClipZ()) frustumTests[1]++;
+ if(box[i].x*m_vecFrustumNormals[0].x + box[i].y*m_vecFrustumNormals[0].y > 0.0f) frustumTests[2]++;
+ if(box[i].x*m_vecFrustumNormals[1].x + box[i].y*m_vecFrustumNormals[1].y > 0.0f) frustumTests[3]++;
+// Why not test z?
+// if(box[i].y*m_vecFrustumNormals[2].y + box[i].z*m_vecFrustumNormals[2].z > 0.0f) frustumTests[4]++;
+// if(box[i].y*m_vecFrustumNormals[3].y + box[i].z*m_vecFrustumNormals[3].z > 0.0f) frustumTests[5]++;
+ }
+ for(i = 0; i < 6; i++)
+ if(frustumTests[i] == 8)
+ return false; // Box is completely outside of one plane
+ return true;
+}
+
+int
+CCamera::GetLookDirection(void)
+{
+ if(Cams[ActiveCam].Mode == CCam::MODE_CAMONASTRING ||
+ Cams[ActiveCam].Mode == CCam::MODE_FIRSTPERSON ||
+ Cams[ActiveCam].Mode == CCam::MODE_BEHINDBOAT ||
+ Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED)
+ return Cams[ActiveCam].DirectionWasLooking;
+ return LOOKING_FORWARD;;
+}
+
+WRAPPER void CCamera::Fade(float timeout, int16 direction) { EAXJMP(0x46B3A0); }
+WRAPPER void CCamera::ProcessFade(void) { EAXJMP(0x46F080); }
+WRAPPER void CCamera::ProcessMusicFade(void) { EAXJMP(0x46F1E0); }
+
+int
+CCamera::GetScreenFadeStatus(void)
+{
+ if(m_fFLOATingFade == 0.0f)
+ return FADE_0;
+ if(m_fFLOATingFade == 255.0f)
+ return FADE_2;
+ return FADE_1;
+}
+
+void
+CCamera::SetFadeColour(uint8 r, uint8 g, uint8 b)
+{
+ m_FadeTargetIsSplashScreen = r == 0 && g == 0 && b == 0;
+ CDraw::FadeRed = r;
+ CDraw::FadeGreen = g;
+ CDraw::FadeBlue = b;
+}
+
+void
+CCamera::SetMotionBlur(int r, int g, int b, int a, int type)
+{
+ m_BlurRed = r;
+ m_BlurGreen = g;
+ m_BlurBlue = b;
+ m_motionBlur = a;
+ m_BlurType = type;
+}
+
+void
+CCamera::SetMotionBlurAlpha(int a)
+{
+ m_imotionBlurAddAlpha = a;
+}
+
+void
+CCamera::RenderMotionBlur(void)
+{
+ if(m_BlurType == 0)
+ return;
+
+ CMBlur::MotionBlurRender(m_pRwCamera,
+ m_BlurRed, m_BlurGreen, m_BlurBlue,
+ m_motionBlur, m_BlurType, m_imotionBlurAddAlpha);
+}
+
+
+/*
+ *
+ * CCam
+ *
+ */
+
+
+// MaxSpeed is a limit of how fast the value is allowed to change. 1.0 = to Target in up to 1ms
+// Acceleration is how fast the speed will change to MaxSpeed. 1.0 = to MaxSpeed in 1ms
+void
+WellBufferMe(float Target, float *CurrentValue, float *CurrentSpeed, float MaxSpeed, float Acceleration, bool IsAngle)
+{
+ float Delta = Target - *CurrentValue;
+
+ if(IsAngle){
+ while(Delta >= PI) Delta -= 2*PI;
+ while(Delta < -PI) Delta += 2*PI;
+ }
+
+ float TargetSpeed = Delta * MaxSpeed;
+ // Add or subtract absolute depending on sign, genius!
+// if(TargetSpeed - *CurrentSpeed > 0.0f)
+// *CurrentSpeed += Acceleration * fabs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep();
+// else
+// *CurrentSpeed -= Acceleration * fabs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep();
+ // this is simpler:
+ *CurrentSpeed += Acceleration * (TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep();
+
+ // Clamp speed if we overshot
+ if(TargetSpeed < 0.0f && *CurrentSpeed < TargetSpeed)
+ *CurrentSpeed = TargetSpeed;
+ else if(TargetSpeed > 0.0f && *CurrentSpeed > TargetSpeed)
+ *CurrentSpeed = TargetSpeed;
+
+ *CurrentValue += *CurrentSpeed * min(10.0f, CTimer::GetTimeStep());
+}
+
+void
+CCam::GetVectorsReadyForRW(void)
+{
+ CVector right;
+ Up = CVector(0.0f, 0.0f, 1.0f);
+ Front.Normalise();
+ if(Front.x == 0.0f && Front.y == 0.0f){
+ Front.x = 0.0001f;
+ Front.y = 0.0001f;
+ }
+ right = CrossProduct(Front, Up);
+ right.Normalise();
+ Up = CrossProduct(right, Front);
+}
+
+// This code is really bad. wtf R*?
+CVector
+CCam::DoAverageOnVector(const CVector &vec)
+{
+ int i;
+ CVector Average = { 0.0f, 0.0f, 0.0f };
+
+ if(ResetStatics){
+ m_iRunningVectorArrayPos = 0;
+ m_iRunningVectorCounter = 1;
+ }
+
+ // TODO: make this work with NUMBER_OF_VECTORS_FOR_AVERAGE != 2
+ if(m_iRunningVectorCounter == 3){
+ m_arrPreviousVectors[0] = m_arrPreviousVectors[1];
+ m_arrPreviousVectors[1] = vec;
+ }else
+ m_arrPreviousVectors[m_iRunningVectorArrayPos] = vec;
+
+ for(i = 0; i <= m_iRunningVectorArrayPos; i++)
+ Average += m_arrPreviousVectors[i];
+ Average /= i;
+
+ m_iRunningVectorArrayPos++;
+ m_iRunningVectorCounter++;
+ if(m_iRunningVectorArrayPos >= NUMBER_OF_VECTORS_FOR_AVERAGE)
+ m_iRunningVectorArrayPos = NUMBER_OF_VECTORS_FOR_AVERAGE-1;
+ if(m_iRunningVectorCounter > NUMBER_OF_VECTORS_FOR_AVERAGE+1)
+ m_iRunningVectorCounter = NUMBER_OF_VECTORS_FOR_AVERAGE+1;
+
+ return Average;
+}
+
+// Rotate Beta in direction opposite of BetaOffset in 5 deg. steps.
+// Return the first angle for which Beta + BetaOffset + Angle has a clear view.
+// i.e. BetaOffset is a safe zone so that Beta + Angle is really clear.
+// If BetaOffset == 0, try both directions.
+float
+CCam::GetPedBetaAngleForClearView(const CVector &Target, float Dist, float BetaOffset, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies)
+{
+ CColPoint point;
+ CEntity *ent = nil;
+ CVector ToSource;
+ float a;
+
+ // This would be so much nicer if we just got the step variable before the loop...R*
+
+ for(a = 0.0f; a <= PI; a += DEGTORAD(5.0f)){
+ if(BetaOffset <= 0.0f){
+ ToSource = CVector(cos(Beta + BetaOffset + a), sin(Beta + BetaOffset + a), 0.0f)*Dist;
+ if(!CWorld::ProcessLineOfSight(Target, Target + ToSource,
+ point, ent, checkBuildings, checkVehicles, checkPeds,
+ checkObjects, checkDummies, true, true))
+ return a;
+ }
+ if(BetaOffset >= 0.0f){
+ ToSource = CVector(cos(Beta + BetaOffset - a), sin(Beta + BetaOffset - a), 0.0f)*Dist;
+ if(!CWorld::ProcessLineOfSight(Target, Target + ToSource,
+ point, ent, checkBuildings, checkVehicles, checkPeds,
+ checkObjects, checkDummies, true, true))
+ return -a;
+ }
+ }
+ return 0.0f;
+}
+
+static float DefaultAcceleration = 0.045f;
+static float DefaultMaxStep = 0.15f;
+
+void
+CCam::Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, float, float)
+{
+ const float GroundDist = 1.85f;
+
+ CVector TargetCoors, Dist, IdealSource;
+ float Length = 0.0f;
+ float LateralLeft = 0.0f;
+ float LateralRight = 0.0f;
+ float Center = 0.0f;
+ static bool PreviouslyObscured;
+ static bool PickedASide;
+ static float FixedTargetOrientation = 0.0f;
+ float AngleToGoTo = 0.0f;
+ float BetaOffsetAvoidBuildings = 0.45f; // ~25 deg
+ float BetaOffsetGoingBehind = 0.45f;
+ bool GoingBehind = false;
+ bool Obscured = false;
+ bool BuildingCheckObscured = false;
+ bool HackPlayerOnStoppingTrain = false;
+ static int TimeIndicatedWantedToGoDown = 0;
+ static bool StartedCountingForGoDown = false;
+ float DeltaBeta;
+
+ m_bFixingBeta = false;
+ bBelowMinDist = false;
+ bBehindPlayerDesired = false;
+
+ assert(CamTargetEntity->IsPed());
+
+ // CenterDist should be > LateralDist because we don't have an angle for safety in this case
+ float CenterDist, LateralDist;
+ float AngleToGoToSpeed;
+ if(m_fCloseInPedHeightOffsetSpeed > 0.00001f){
+ LateralDist = 0.55f;
+ CenterDist = 1.25f;
+ BetaOffsetAvoidBuildings = 0.9f; // ~50 deg
+ BetaOffsetGoingBehind = 0.9f;
+ AngleToGoToSpeed = 0.88254666f;
+ }else{
+ LateralDist = 0.8f;
+ CenterDist = 1.35f;
+ if(TheCamera.PedZoomIndicator == 1.0f || TheCamera.PedZoomIndicator == 4.0f){
+ LateralDist = 1.25f;
+ CenterDist = 1.6f;
+ }
+ AngleToGoToSpeed = 0.43254671f;
+ }
+
+ FOV = DefaultFOV;
+
+ if(ResetStatics){
+ Rotating = false;
+ m_bCollisionChecksOn = true;
+ FixedTargetOrientation = 0.0f;
+ PreviouslyObscured = false;
+ PickedASide = false;
+ StartedCountingForGoDown = false;
+ AngleToGoTo = 0.0f;
+ // unused LastAngleWithNoPickedASide
+ }
+
+
+ TargetCoors = CameraTarget;
+ IdealSource = Source;
+ TargetCoors.z += m_fSyphonModeTargetZOffSet;
+
+ CVector TempTargetCoors;
+ TempTargetCoors = DoAverageOnVector(TargetCoors);
+ TargetCoors = TempTargetCoors;
+ // Add this unknown offset, but later it's removed again
+ TargetCoors.z += m_fUnknownZOffSet;
+
+ Dist.x = IdealSource.x - TargetCoors.x;
+ Dist.y = IdealSource.y - TargetCoors.y;
+ Length = Dist.Magnitude2D();
+
+ // Cam on a string. With a fixed distance. Zoom in/out is done later.
+ if(Length != 0.0f)
+ IdealSource = TargetCoors + CVector(Dist.x, Dist.y, 0.0f)/Length * GroundDist;
+ else
+ IdealSource = TargetCoors + CVector(1.0f, 1.0f, 0.0f);
+
+ // TODO: what's transition beta?
+ if(TheCamera.m_bUseTransitionBeta && ResetStatics){
+ CVector VecDistance;
+ IdealSource.x = TargetCoors.x + GroundDist*cos(m_fTransitionBeta);
+ IdealSource.y = TargetCoors.y + GroundDist*sin(m_fTransitionBeta);
+ Beta = CGeneral::GetATanOfXY(IdealSource.x - TargetCoors.x, IdealSource.y - TargetCoors.y);
+ }else
+ Beta = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y);
+
+ if(TheCamera.m_bCamDirectlyBehind){
+ m_bCollisionChecksOn = true;
+ Beta = TargetOrientation + PI;
+ }
+
+ if(FindPlayerVehicle())
+ if(FindPlayerVehicle()->m_vehType == VEHICLE_TYPE_TRAIN)
+ HackPlayerOnStoppingTrain = true;
+
+ if(TheCamera.m_bCamDirectlyInFront){
+ m_bCollisionChecksOn = true;
+ Beta = TargetOrientation;
+ }
+
+ while(Beta >= PI) Beta -= 2.0f * PI;
+ while(Beta < -PI) Beta += 2.0f * PI;
+
+ // BUG? is this ever used?
+ // The values seem to be roughly m_fPedZoomValueSmooth + 1.85
+ if(ResetStatics){
+ if(TheCamera.PedZoomIndicator == 1.0) m_fRealGroundDist = 2.090556f;
+ if(TheCamera.PedZoomIndicator == 2.0) m_fRealGroundDist = 3.34973f;
+ if(TheCamera.PedZoomIndicator == 3.0) m_fRealGroundDist = 4.704914f;
+ if(TheCamera.PedZoomIndicator == 4.0) m_fRealGroundDist = 2.090556f;
+ }
+ // And what is this? It's only used for collision and rotation it seems
+ float RealGroundDist;
+ if(TheCamera.PedZoomIndicator == 1.0) RealGroundDist = 2.090556f;
+ if(TheCamera.PedZoomIndicator == 2.0) RealGroundDist = 3.34973f;
+ if(TheCamera.PedZoomIndicator == 3.0) RealGroundDist = 4.704914f;
+ if(TheCamera.PedZoomIndicator == 4.0) RealGroundDist = 2.090556f;
+ if(m_fCloseInPedHeightOffset > 0.00001f)
+ RealGroundDist = 1.7016;
+
+
+ bool Shooting = false;
+ CPed *ped = (CPed*)CamTargetEntity;
+ if(ped->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED)
+ if(CPad::GetPad(0)->GetWeapon())
+ Shooting = true;
+ if(ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR ||
+ ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT)
+ Shooting = false;
+
+
+ if(m_fCloseInPedHeightOffset > 0.00001f)
+ TargetCoors.z -= m_fUnknownZOffSet;
+
+ // Figure out if and where we want to rotate
+
+ if(CPad::GetPad(0)->ForceCameraBehindPlayer() || Shooting){
+
+ // Center cam behind player
+
+ GoingBehind = true;
+ m_bCollisionChecksOn = true;
+ float OriginalBeta = Beta;
+ // Set Beta behind player
+ Beta = TargetOrientation + PI;
+ TargetCoors.z -= 0.1f;
+
+ AngleToGoTo = GetPedBetaAngleForClearView(TargetCoors, CenterDist * RealGroundDist, 0.0f, true, false, false, true, false);
+ if(AngleToGoTo != 0.0f){
+ if(AngleToGoTo < 0.0f)
+ AngleToGoTo -= AngleToGoToSpeed;
+ else
+ AngleToGoTo += AngleToGoToSpeed;
+ }else{
+ float LateralLeft = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, BetaOffsetGoingBehind, true, false, false, true, false);
+ float LateralRight = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, -BetaOffsetGoingBehind, true, false, false, true, false);
+ if(LateralLeft == 0.0f && LateralRight != 0.0f)
+ AngleToGoTo += LateralRight;
+ else if(LateralLeft != 0.0f && LateralRight == 0.0f)
+ AngleToGoTo += LateralLeft;
+ }
+
+ TargetCoors.z += 0.1f;
+ Beta = OriginalBeta;
+
+ if(PickedASide){
+ if(AngleToGoTo == 0.0f)
+ FixedTargetOrientation = TargetOrientation + PI;
+ Rotating = true;
+ }else{
+ FixedTargetOrientation = TargetOrientation + PI + AngleToGoTo;
+ Rotating = true;
+ PickedASide = true;
+ }
+ }else{
+
+ // Rotate cam to avoid clipping into buildings
+
+ TargetCoors.z -= 0.1f;
+
+ Center = GetPedBetaAngleForClearView(TargetCoors, CenterDist * RealGroundDist, 0.0f, true, false, false, true, false);
+ if(m_bCollisionChecksOn || PreviouslyObscured || Center != 0.0f || m_fCloseInPedHeightOffset > 0.00001f){
+ if(Center != 0.0f){
+ AngleToGoTo = Center;
+ }else{
+ LateralLeft = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, BetaOffsetAvoidBuildings, true, false, false, true, false);
+ LateralRight = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, -BetaOffsetAvoidBuildings, true, false, false, true, false);
+ if(LateralLeft == 0.0f && LateralRight != 0.0f){
+ AngleToGoTo += LateralRight;
+ if(m_fCloseInPedHeightOffset > 0.0f)
+ RwCameraSetNearClipPlane(Scene.camera, 0.7f);
+ }else if(LateralLeft != 0.0f && LateralRight == 0.0f){
+ AngleToGoTo += LateralLeft;
+ if(m_fCloseInPedHeightOffset > 0.0f)
+ RwCameraSetNearClipPlane(Scene.camera, 0.7f);
+ }
+ }
+ if(LateralLeft != 0.0f || LateralRight != 0.0f || Center != 0.0f)
+ BuildingCheckObscured = true;
+ }
+
+ TargetCoors.z += 0.1f;
+ }
+
+ if(m_fCloseInPedHeightOffset > 0.00001f)
+ TargetCoors.z += m_fUnknownZOffSet;
+
+
+ // Have to fix to avoid collision
+
+ if(AngleToGoTo != 0.0f){
+ Obscured = true;
+ Rotating = true;
+ if(CPad::GetPad(0)->ForceCameraBehindPlayer() || Shooting){
+ if(!PickedASide)
+ FixedTargetOrientation = Beta + AngleToGoTo; // can this even happen?
+ }else
+ FixedTargetOrientation = Beta + AngleToGoTo;
+
+ // This calculation is only really used to figure out how fast to rotate out of collision
+
+ m_fAmountFractionObscured = 1.0f;
+ CVector PlayerPos = FindPlayerPed()->GetPosition();
+ float RotationDist = (AngleToGoTo == Center ? CenterDist : LateralDist) * RealGroundDist;
+ // What's going on here? - AngleToGoTo?
+ CVector RotatedSource = PlayerPos + CVector(cos(Beta - AngleToGoTo), sin(Beta - AngleToGoTo), 0.0f) * RotationDist;
+
+ CColPoint colpoint;
+ CEntity *entity;
+ if(CWorld::ProcessLineOfSight(PlayerPos, RotatedSource, colpoint, entity, true, false, false, true, false, false, false)){
+ if((PlayerPos - RotatedSource).Magnitude() != 0.0f)
+ m_fAmountFractionObscured = (PlayerPos - colpoint.point).Magnitude() / (PlayerPos - RotatedSource).Magnitude();
+ else
+ m_fAmountFractionObscured = 1.0f;
+ }
+ }
+ if(m_fAmountFractionObscured < 0.0f) m_fAmountFractionObscured = 0.0f;
+ if(m_fAmountFractionObscured > 1.0f) m_fAmountFractionObscured = 1.0f;
+
+
+
+ // Figure out speed values for Beta rotation
+
+ float Acceleration, MaxSpeed;
+ static float AccelerationMult = 0.35f;
+ static float MaxSpeedMult = 0.85f;
+ static float AccelerationMultClose = 0.7f;
+ static float MaxSpeedMultClose = 1.6f;
+ float BaseAcceleration = 0.025f;
+ float BaseMaxSpeed = 0.09f;
+ if(m_fCloseInPedHeightOffset > 0.00001f){
+ if(AngleToGoTo == 0.0f){
+ BaseAcceleration = 0.022f;
+ BaseMaxSpeed = 0.04f;
+ }else{
+ BaseAcceleration = DefaultAcceleration;
+ BaseMaxSpeed = DefaultMaxStep;
+ }
+ }
+ if(AngleToGoTo == 0.0f){
+ Acceleration = BaseAcceleration;
+ MaxSpeed = BaseMaxSpeed;
+ }else if(CPad::GetPad(0)->ForceCameraBehindPlayer() && !Shooting){
+ Acceleration = 0.051f;
+ MaxSpeed = 0.18f;
+ }else if(m_fCloseInPedHeightOffset > 0.00001f){
+ Acceleration = BaseAcceleration + AccelerationMultClose*sq(m_fAmountFractionObscured - 1.05f);
+ MaxSpeed = BaseMaxSpeed + MaxSpeedMultClose*sq(m_fAmountFractionObscured - 1.05f);
+ }else{
+ Acceleration = DefaultAcceleration + AccelerationMult*sq(m_fAmountFractionObscured - 1.05f);
+ MaxSpeed = DefaultMaxStep + MaxSpeedMult*sq(m_fAmountFractionObscured - 1.05f);
+ }
+ static float AccelerationLimit = 0.3f;
+ static float MaxSpeedLimit = 0.65f;
+ if(Acceleration > AccelerationLimit) Acceleration = AccelerationLimit;
+ if(MaxSpeed > MaxSpeedLimit) MaxSpeed = MaxSpeedLimit;
+
+
+ int MoveState = ((CPed*)CamTargetEntity)->m_nMoveState;
+ if(MoveState != PEDMOVE_NONE && MoveState != PEDMOVE_STILL &&
+ !CPad::GetPad(0)->ForceCameraBehindPlayer() && !Obscured && !Shooting){
+ Rotating = false;
+ BetaSpeed = 0.0f;
+ }
+
+ // Now do the Beta rotation
+
+ float Distance = (IdealSource - TargetCoors).Magnitude2D();
+ m_fDistanceBeforeChanges = Distance;
+
+ if(Rotating){
+ m_bFixingBeta = true;
+
+ while(FixedTargetOrientation >= PI) FixedTargetOrientation -= 2*PI;
+ while(FixedTargetOrientation < -PI) FixedTargetOrientation += 2*PI;
+
+ while(Beta >= PI) Beta -= 2*PI;
+ while(Beta < -PI) Beta += 2*PI;
+
+
+/*
+ // This is inlined WellBufferMe
+ DeltaBeta = FixedTargetOrientation - Beta;
+ while(DeltaBeta >= PI) DeltaBeta -= 2*PI;
+ while(DeltaBeta < -PI) DeltaBeta += 2*PI;
+
+ float ReqSpeed = DeltaBeta * MaxSpeed;
+ // Add or subtract absolute depending on sign, genius!
+ if(ReqSpeed - BetaSpeed > 0.0f)
+ BetaSpeed += SpeedStep * fabs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep();
+ else
+ BetaSpeed -= SpeedStep * fabs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep();
+ // this would be simpler:
+ // BetaSpeed += SpeedStep * (ReqSpeed - BetaSpeed) * CTimer::ms_fTimeStep;
+
+ if(ReqSpeed < 0.0f && BetaSpeed < ReqSpeed)
+ BetaSpeed = ReqSpeed;
+ else if(ReqSpeed > 0.0f && BetaSpeed > ReqSpeed)
+ BetaSpeed = ReqSpeed;
+
+ Beta += BetaSpeed * min(10.0f, CTimer::GetTimeStep());
+*/
+ WellBufferMe(FixedTargetOrientation, &Beta, &BetaSpeed, MaxSpeed, Acceleration, true);
+
+ if(ResetStatics){
+ Beta = FixedTargetOrientation;
+ BetaSpeed = 0.0f;
+ }
+
+ Source.x = TargetCoors.x + Distance * cos(Beta);
+ Source.y = TargetCoors.y + Distance * sin(Beta);
+
+ // Check if we can stop rotating
+ DeltaBeta = FixedTargetOrientation - Beta;
+ while(DeltaBeta >= PI) DeltaBeta -= 2*PI;
+ while(DeltaBeta < -PI) DeltaBeta += 2*PI;
+ if(fabs(DeltaBeta) < DEGTORAD(1.0f) && !bBehindPlayerDesired){
+ // Stop rotation
+ PickedASide = false;
+ Rotating = false;
+ BetaSpeed = 0.0f;
+ }
+ }
+
+
+ if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront ||
+ HackPlayerOnStoppingTrain || Rotating){
+ if(TheCamera.m_bCamDirectlyBehind){
+ Beta = TargetOrientation + PI;
+ Source.x = TargetCoors.x + Distance * cos(Beta);
+ Source.y = TargetCoors.y + Distance * sin(Beta);
+ }
+ if(TheCamera.m_bCamDirectlyInFront){
+ Beta = TargetOrientation;
+ Source.x = TargetCoors.x + Distance * cos(Beta);
+ Source.y = TargetCoors.y + Distance * sin(Beta);
+ }
+ if(HackPlayerOnStoppingTrain){
+ Beta = TargetOrientation + PI;
+ Source.x = TargetCoors.x + Distance * cos(Beta);
+ Source.y = TargetCoors.y + Distance * sin(Beta);
+ m_fDimensionOfHighestNearCar = 0.0f;
+ m_fCamBufferedHeight = 0.0f;
+ m_fCamBufferedHeightSpeed = 0.0f;
+ }
+ // Beta and Source already set in the rotation code
+ }else{
+ Source = IdealSource;
+ BetaSpeed = 0.0f;
+ }
+
+ // Subtract m_fUnknownZOffSet from both?
+ TargetCoors.z -= m_fUnknownZOffSet;
+ Source.z = IdealSource.z - m_fUnknownZOffSet;
+
+ // Apply zoom now
+ // m_fPedZoomValueSmooth makes the cam go down the further out it is
+ // 0.25 -> 0.20 for nearest dist
+ // 1.50 -> -0.05 for mid dist
+ // 2.90 -> -0.33 for far dist
+ Source.z += (2.5f - TheCamera.m_fPedZoomValueSmooth)*0.2f - 0.25f;
+ // Zoom out camera
+ Front = TargetCoors - Source;
+ Front.Normalise();
+ Source -= Front * TheCamera.m_fPedZoomValueSmooth;
+ // and then we move up again
+ // -0.375
+ // 0.25
+ // 0.95
+ Source.z += (TheCamera.m_fPedZoomValueSmooth - 1.0f)*0.5f + m_fCloseInPedHeightOffset;
+
+
+ // Process height offset to avoid peds and cars
+
+ float TargetZOffSet = m_fUnknownZOffSet + m_fDimensionOfHighestNearCar;
+ TargetZOffSet = max(TargetZOffSet, m_fPedBetweenCameraHeightOffset);
+ float TargetHeight = CameraTarget.z + TargetZOffSet - Source.z;
+
+ if(TargetHeight > m_fCamBufferedHeight){
+ // Have to go up
+ if(TargetZOffSet == m_fPedBetweenCameraHeightOffset && TargetZOffSet > m_fCamBufferedHeight)
+ WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.04f, false);
+ else if(TargetZOffSet == m_fUnknownZOffSet && TargetZOffSet > m_fCamBufferedHeight){
+ // TODO: figure this out
+ bool foo = false;
+ switch(((CPhysical*)CamTargetEntity)->m_nLastCollType)
+ case 2: case 3: case 5:
+ case 11: case 23: case 26:
+ foo = true;
+ if(foo)
+ WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.4f, 0.05f, false);
+ else
+ WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.025f, false);
+ }else
+ WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.025f, false);
+ StartedCountingForGoDown = false;
+ }else{
+ // Have to go down
+ if(StartedCountingForGoDown){
+ if(CTimer::GetTimeInMilliseconds() != TimeIndicatedWantedToGoDown){
+ if(TargetHeight > 0.0f)
+ WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false);
+ else
+ WellBufferMe(0.0f, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false);
+ }
+ }else{
+ StartedCountingForGoDown = true;
+ TimeIndicatedWantedToGoDown = CTimer::GetTimeInMilliseconds();
+ }
+ }
+
+ Source.z += m_fCamBufferedHeight;
+
+
+ // Clip Source if necessary
+
+ bool ClipSource = m_fCloseInPedHeightOffset > 0.00001f && m_fCamBufferedHeight > 0.001f;
+ if(GoingBehind || ResetStatics || ClipSource){
+ CColPoint colpoint;
+ CEntity *entity;
+ if(CWorld::ProcessLineOfSight(TargetCoors, Source, colpoint, entity, true, false, false, true, false, true, true)){
+ Source = colpoint.point;
+ if((TargetCoors - Source).Magnitude2D() < 1.0f)
+ RwCameraSetNearClipPlane(Scene.camera, 0.05f);
+ }
+ }
+
+ TargetCoors.z += min(1.0f, m_fCamBufferedHeight/2.0f);
+ m_cvecTargetCoorsForFudgeInter = TargetCoors;
+
+ Front = TargetCoors - Source;
+ m_fRealGroundDist = Front.Magnitude2D();
+ m_fMinDistAwayFromCamWhenInterPolating = m_fRealGroundDist;
+ Front.Normalise();
+ GetVectorsReadyForRW();
+ TheCamera.m_bCamDirectlyBehind = false;
+ TheCamera.m_bCamDirectlyInFront = false;
+ PreviouslyObscured = BuildingCheckObscured;
+
+ ResetStatics = false;
+}
+
+void
+CCam::Process_BehindCar(const CVector &CameraTarget, float TargetOrientation, float, float)
+{
+ FOV = DefaultFOV;
+
+ if(!CamTargetEntity->IsVehicle())
+ return;
+
+ CVector TargetCoors = CameraTarget;
+ TargetCoors.z -= 0.2f;
+ CA_MAX_DISTANCE = 9.95f;
+ CA_MIN_DISTANCE = 8.5f;
+
+ CVector Dist = Source - TargetCoors;
+ float Length = Dist.Magnitude2D();
+ m_fDistanceBeforeChanges = Length;
+ if(Length < 0.002f)
+ Length = 0.002f;
+ Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y);
+ if(Length > CA_MAX_DISTANCE){
+ Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE;
+ Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE;
+ }else if(Length < CA_MIN_DISTANCE){
+ Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE;
+ Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE;
+ }
+ TargetCoors.z += 0.8f;
+
+ WorkOutCamHeightWeeCar(TargetCoors, TargetOrientation);
+ RotCamIfInFrontCar(TargetCoors, TargetOrientation);
+ FixCamIfObscured(TargetCoors, 1.2f, TargetOrientation);
+
+ Front = TargetCoors - Source;
+ m_cvecTargetCoorsForFudgeInter = TargetCoors;
+ ResetStatics = false;
+ GetVectorsReadyForRW();
+}
+
+void
+CCam::WorkOutCamHeightWeeCar(CVector &TargetCoors, float TargetOrientation)
+{
+ CColPoint colpoint;
+ CEntity *ent;
+ float TargetZOffSet = 0.0f;
+ static bool PreviouslyFailedRoadHeightCheck = false;
+ static float RoadHeightFix = 0.0f;
+ static float RoadHeightFixSpeed = 0.0f;
+
+ if(ResetStatics){
+ RoadHeightFix = 0.0f;
+ RoadHeightFixSpeed = 0.0f;
+ Alpha = DEGTORAD(25.0f);
+ AlphaSpeed = 0.0f;
+ }
+ float AlphaTarget = DEGTORAD(25.0f);
+ if(CCullZones::CamNoRain() || CCullZones::PlayerNoRain())
+ AlphaTarget = DEGTORAD(14.0f);
+ WellBufferMe(AlphaTarget, &Alpha, &AlphaSpeed, 0.1f, 0.05f, true);
+ Source.z = TargetCoors.z + CA_MAX_DISTANCE*sin(Alpha);
+
+ if(FindPlayerVehicle()){
+ m_fUnknownZOffSet = 0.0f;
+ bool FoundRoad = false;
+ bool FoundRoof = false;
+ float RoadZ = 0.0f;
+ float RoofZ = 0.0f;
+
+ if(CWorld::ProcessVerticalLine(Source, -1000.0f, colpoint, ent, true, false, false, false, false, false, nil) &&
+ ent->IsBuilding()){
+ FoundRoad = true;
+ RoadZ = colpoint.point.z;
+ }
+
+ if(FoundRoad){
+ if(Source.z - RoadZ < 0.9f){
+ PreviouslyFailedRoadHeightCheck = true;
+ TargetZOffSet = RoadZ + 0.9f - Source.z;
+ }else{
+ if(m_bCollisionChecksOn)
+ PreviouslyFailedRoadHeightCheck = false;
+ else
+ TargetZOffSet = 0.0f;
+ }
+ }else{
+ if(CWorld::ProcessVerticalLine(Source, 1000.0f, colpoint, ent, true, false, false, false, false, false, nil) &&
+ ent->IsBuilding()){
+ FoundRoof = true;
+ RoofZ = colpoint.point.z;
+ }
+ if(FoundRoof){
+ if(Source.z - RoofZ < 0.9f){
+ PreviouslyFailedRoadHeightCheck = true;
+ TargetZOffSet = RoofZ + 0.9f - Source.z;
+ }else{
+ if(m_bCollisionChecksOn)
+ PreviouslyFailedRoadHeightCheck = false;
+ else
+ TargetZOffSet = 0.0f;
+ }
+ }
+ }
+ }
+
+ if(TargetZOffSet > RoadHeightFix)
+ RoadHeightFix = TargetZOffSet;
+ else
+ WellBufferMe(TargetZOffSet, &RoadHeightFix, &RoadHeightFixSpeed, 0.27f, 0.1f, false);
+
+ if((colpoint.surfaceB == SURFACE_DEFAULT || colpoint.surfaceB >= SURFACE_METAL6) &&
+ colpoint.surfaceB != SURFACE_STEEL && colpoint.surfaceB != SURFACE_STONE &&
+ RoadHeightFix > 1.4f)
+ RoadHeightFix = 1.4f;
+
+ Source.z += RoadHeightFix;
+}
+
+void
+CCam::WorkOutCamHeight(const CVector &TargetCoors, float TargetOrientation, float TargetHeight)
+{
+ static float LastTargetAlphaWithCollisionOn = 0.0f;
+ static float LastTopAlphaSpeed = 0.0f;
+ static float LastAlphaSpeedStep = 0.0f;
+ static bool PreviousNearCheckNearClipSmall = false;
+
+ bool CamClear = true;
+ float ModeAlpha = 0.0f;
+
+ if(ResetStatics){
+ LastTargetAlphaWithCollisionOn = 0.0f;
+ LastTopAlphaSpeed = 0.0f;
+ LastAlphaSpeedStep = 0.0f;
+ PreviousNearCheckNearClipSmall = false;
+ }
+
+ float TopAlphaSpeed = 0.15f;
+ float AlphaSpeedStep = 0.015f;
+
+ float zoomvalue = TheCamera.CarZoomValueSmooth;
+ if(zoomvalue < 0.1f)
+ zoomvalue = 0.1f;
+ if(TheCamera.CarZoomIndicator == 1.0f)
+ ModeAlpha = CGeneral::GetATanOfXY(23.0f, zoomvalue); // near
+ else if(TheCamera.CarZoomIndicator == 2.0f)
+ ModeAlpha = CGeneral::GetATanOfXY(10.8f, zoomvalue); // mid
+ else if(TheCamera.CarZoomIndicator == 3.0f)
+ ModeAlpha = CGeneral::GetATanOfXY(7.0f, zoomvalue); // far
+
+
+ float Length = (Source - TargetCoors).Magnitude2D();
+ if(m_bCollisionChecksOn){ // there's another variable (on PC) but it's uninitialised
+ CVector Forward = CamTargetEntity->GetForward();
+ float CarAlpha = CGeneral::GetATanOfXY(Forward.Magnitude2D(), Forward.z);
+ // this shouldn't be necessary....
+ while(CarAlpha >= PI) CarAlpha -= 2*PI;
+ while(CarAlpha < -PI) CarAlpha += 2*PI;
+
+ while(Beta >= PI) Beta -= 2*PI;
+ while(Beta < -PI) Beta += 2*PI;
+
+ float deltaBeta = Beta - TargetOrientation;
+ while(deltaBeta >= PI) deltaBeta -= 2*PI;
+ while(deltaBeta < -PI) deltaBeta += 2*PI;
+
+ float BehindCarNess = cos(deltaBeta); // 1 if behind car, 0 if side, -1 if in front
+ CarAlpha = -CarAlpha * BehindCarNess;
+ if(CarAlpha < -0.01f)
+ CarAlpha = -0.01f;
+
+ float DeltaAlpha = CarAlpha - Alpha;
+ while(DeltaAlpha >= PI) DeltaAlpha -= 2*PI;
+ while(DeltaAlpha < -PI) DeltaAlpha += 2*PI;
+ // What's this?? wouldn't it make more sense to clamp?
+ float AngleLimit = DEGTORAD(1.8f);
+ if(DeltaAlpha < -AngleLimit)
+ DeltaAlpha += AngleLimit;
+ else if(DeltaAlpha > AngleLimit)
+ DeltaAlpha -= AngleLimit;
+ else
+ DeltaAlpha = 0.0f;
+
+ // Now the collision
+
+ float TargetAlpha = 0.0f;
+ bool FoundRoofCenter = false;
+ bool FoundRoofSide1 = false;
+ bool FoundRoofSide2 = false;
+ bool FoundCamRoof = false;
+ bool FoundCamGround = false;
+ float CamRoof = 0.0f;
+ float CarBottom = TargetCoors.z - TargetHeight/2.0f;
+
+ // Check car center
+ float CarRoof = CWorld::FindRoofZFor3DCoord(TargetCoors.x, TargetCoors.y, CarBottom, &FoundRoofCenter);
+
+ // Check sides of the car
+ Forward = CamTargetEntity->GetForward(); // we actually still have that...
+ Forward.Normalise(); // shouldn't be necessary
+ float CarSideAngle = CGeneral::GetATanOfXY(Forward.x, Forward.y) + PI/2.0f;
+ float SideX = 2.5f * cos(CarSideAngle);
+ float SideY = 2.5f * sin(CarSideAngle);
+ CWorld::FindRoofZFor3DCoord(TargetCoors.x + SideX, TargetCoors.y + SideY, CarBottom, &FoundRoofSide1);
+ CWorld::FindRoofZFor3DCoord(TargetCoors.x - SideX, TargetCoors.y - SideY, CarBottom, &FoundRoofSide2);
+
+ // Now find out at what height we'd like to place the camera
+ float CamGround = CWorld::FindGroundZFor3DCoord(Source.x, Source.y, TargetCoors.z + Length*sin(Alpha + ModeAlpha) + m_fCloseInCarHeightOffset, &FoundCamGround);
+ float CamTargetZ = 0.0f;
+ if(FoundCamGround){
+ // This is the normal case
+ CamRoof = CWorld::FindRoofZFor3DCoord(Source.x, Source.y, CamGround + TargetHeight, &FoundCamRoof);
+ CamTargetZ = CamGround + TargetHeight*1.5f + 0.1f;
+ }else{
+ FoundCamRoof = false;
+ CamTargetZ = TargetCoors.z;
+ }
+
+ if(FoundRoofCenter && !FoundCamRoof && (FoundRoofSide1 || FoundRoofSide2)){
+ // Car is under something but camera isn't
+ // This seems weird...
+ TargetAlpha = CGeneral::GetATanOfXY(CA_MAX_DISTANCE, CarRoof - CamTargetZ - 1.5f);
+ CamClear = false;
+ }
+ if(FoundCamRoof){
+ // Camera is under something
+ float roof = FoundRoofCenter ? min(CamRoof, CarRoof) : CamRoof;
+ // Same weirdness again?
+ TargetAlpha = CGeneral::GetATanOfXY(CA_MAX_DISTANCE, roof - CamTargetZ - 1.5f);
+ CamClear = false;
+ }
+ while(TargetAlpha >= PI) TargetAlpha -= 2*PI;
+ while(TargetAlpha < -PI) TargetAlpha += 2*PI;
+ if(TargetAlpha < DEGTORAD(-7.0f))
+ TargetAlpha = DEGTORAD(-7.0f);
+
+ // huh?
+ if(TargetAlpha > ModeAlpha)
+ CamClear = true;
+ // Camera is contrained by collision in some way
+ PreviousNearCheckNearClipSmall = false;
+ if(!CamClear){
+ PreviousNearCheckNearClipSmall = true;
+ RwCameraSetNearClipPlane(Scene.camera, 0.9f);
+
+ DeltaAlpha = TargetAlpha - (Alpha + ModeAlpha);
+ while(DeltaAlpha >= PI) DeltaAlpha -= 2*PI;
+ while(DeltaAlpha < -PI) DeltaAlpha += 2*PI;
+
+ TopAlphaSpeed = 0.3f;
+ AlphaSpeedStep = 0.03f;
+ }
+
+ // Now do things if CamClear...but what is that anyway?
+ float CamZ = TargetCoors.z + Length*sin(Alpha + DeltaAlpha + ModeAlpha) + m_fCloseInCarHeightOffset;
+ bool FoundGround, FoundRoof;
+ float CamGround2 = CWorld::FindGroundZFor3DCoord(Source.x, Source.y, CamZ, &FoundGround);
+ if(FoundGround){
+ if(CamClear)
+ if(CamZ - CamGround2 < 1.5f){
+ PreviousNearCheckNearClipSmall = true;
+ RwCameraSetNearClipPlane(Scene.camera, 0.9f);
+
+ float a;
+ if(Length == 0.0f || CamGround2 + 1.5f - TargetCoors.z == 0.0f)
+ a = Alpha;
+ else
+ a = CGeneral::GetATanOfXY(Length, CamGround2 + 1.5f - TargetCoors.z);
+ while(a > PI) a -= 2*PI;
+ while(a < -PI) a += 2*PI;
+ DeltaAlpha = a - Alpha;
+ }
+ }else{
+ if(CamClear){
+ float CamRoof2 = CWorld::FindRoofZFor3DCoord(Source.x, Source.y, CamZ, &FoundRoof);
+ if(FoundRoof && CamZ - CamRoof2 < 1.5f){
+ PreviousNearCheckNearClipSmall = true;
+ RwCameraSetNearClipPlane(Scene.camera, 0.9f);
+
+ if(CamRoof2 > TargetCoors.z + 3.5f)
+ CamRoof2 = TargetCoors.z + 3.5f;
+
+ float a;
+ if(Length == 0.0f || CamRoof2 + 1.5f - TargetCoors.z == 0.0f)
+ a = Alpha;
+ else
+ a = CGeneral::GetATanOfXY(Length, CamRoof2 + 1.5f - TargetCoors.z);
+ while(a > PI) a -= 2*PI;
+ while(a < -PI) a += 2*PI;
+ DeltaAlpha = a - Alpha;
+ }
+ }
+ }
+
+ LastTargetAlphaWithCollisionOn = DeltaAlpha + Alpha;
+ LastTopAlphaSpeed = TopAlphaSpeed;
+ LastAlphaSpeedStep = AlphaSpeedStep;
+ }else{
+ if(PreviousNearCheckNearClipSmall)
+ RwCameraSetNearClipPlane(Scene.camera, 0.9f);
+ }
+
+ WellBufferMe(LastTargetAlphaWithCollisionOn, &Alpha, &AlphaSpeed, LastTopAlphaSpeed, LastAlphaSpeedStep, true);
+
+ Source.z = TargetCoors.z + sin(Alpha + ModeAlpha)*Length + m_fCloseInCarHeightOffset;
+}
+
+// Rotate cam behind the car when the car is moving forward
+bool
+CCam::RotCamIfInFrontCar(CVector &TargetCoors, float TargetOrientation)
+{
+ bool MovingForward = false;
+ CPhysical *phys = (CPhysical*)CamTargetEntity;
+
+ float ForwardSpeed = DotProduct(phys->GetForward(), phys->GetSpeed(CVector(0.0f, 0.0f, 0.0f)));
+ if(ForwardSpeed > 0.02f)
+ MovingForward = true;
+
+ float Dist = (Source - TargetCoors).Magnitude2D();
+
+ float DeltaBeta = TargetOrientation - Beta;
+ while(DeltaBeta >= PI) DeltaBeta -= 2*PI;
+ while(DeltaBeta < -PI) DeltaBeta += 2*PI;
+
+ if(fabs(DeltaBeta) > DEGTORAD(20.0f) && MovingForward && TheCamera.m_uiTransitionState == 0)
+ m_bFixingBeta = true;
+
+ CPad *pad = CPad::GetPad(0);
+ if(!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight()))
+ if(DirectionWasLooking != LOOKING_FORWARD)
+ TheCamera.m_bCamDirectlyBehind = true;
+
+ if(!m_bFixingBeta && !TheCamera.m_bUseTransitionBeta && !TheCamera.m_bCamDirectlyBehind && !TheCamera.m_bCamDirectlyInFront)
+ return false;
+
+ bool SetBeta = false;
+ if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront || TheCamera.m_bUseTransitionBeta)
+ if(&TheCamera.Cams[TheCamera.ActiveCam] == this)
+ SetBeta = true;
+
+ if(m_bFixingBeta || SetBeta){
+ WellBufferMe(TargetOrientation, &Beta, &BetaSpeed, 0.15f, 0.007f, true);
+
+ if(TheCamera.m_bCamDirectlyBehind && &TheCamera.Cams[TheCamera.ActiveCam] == this)
+ Beta = TargetOrientation;
+ if(TheCamera.m_bCamDirectlyInFront && &TheCamera.Cams[TheCamera.ActiveCam] == this)
+ Beta = TargetOrientation + PI;
+ if(TheCamera.m_bUseTransitionBeta && &TheCamera.Cams[TheCamera.ActiveCam] == this)
+ Beta = m_fTransitionBeta;
+
+ Source.x = TargetCoors.x - cos(Beta)*Dist;
+ Source.y = TargetCoors.y - sin(Beta)*Dist;
+
+ // Check if we're done
+ DeltaBeta = TargetOrientation - Beta;
+ while(DeltaBeta >= PI) DeltaBeta -= 2*PI;
+ while(DeltaBeta < -PI) DeltaBeta += 2*PI;
+ if(fabs(DeltaBeta) < DEGTORAD(2.0f))
+ m_bFixingBeta = false;
+ }
+ TheCamera.m_bCamDirectlyBehind = false;
+ TheCamera.m_bCamDirectlyInFront = false;
+ return true;
+}
+
+// Move the cam to avoid clipping through buildings
+bool
+CCam::FixCamIfObscured(CVector &TargetCoors, float TargetHeight, float TargetOrientation)
+{
+ CVector Target = TargetCoors;
+ bool UseEntityPos = false;
+ CVector EntityPos;
+ static CColPoint colPoint;
+ static bool LastObscured = false;
+
+ if(Mode == MODE_BEHINDCAR)
+ Target.z += TargetHeight/2.0f;
+ if(Mode == MODE_CAMONASTRING){
+ UseEntityPos = true;
+ Target.z += TargetHeight/2.0f;
+ EntityPos = CamTargetEntity->GetPosition();
+ }
+
+ CVector TempSource = Source;
+
+ bool Obscured1 = false;
+ bool Obscured2 = false;
+ bool Fix1 = false;
+ float Dist1 = 0.0f;
+ float Dist2 = 0.0f;
+ CEntity *ent;
+ if(m_bCollisionChecksOn || LastObscured){
+ Obscured1 = CWorld::ProcessLineOfSight(Target, TempSource, colPoint, ent, true, false, false, true, false, true, true);
+ if(Obscured1){
+ Dist1 = (Target - colPoint.point).Magnitude2D();
+ Fix1 = true;
+ if(UseEntityPos)
+ Obscured1 = CWorld::ProcessLineOfSight(EntityPos, TempSource, colPoint, ent, true, false, false, true, false, true, true);
+ }else if(m_bFixingBeta){
+ float d = (TempSource - Target).Magnitude();
+ TempSource.x = Target.x - d*cos(TargetOrientation);
+ TempSource.y = Target.y - d*sin(TargetOrientation);
+
+ // same check again
+ Obscured2 = CWorld::ProcessLineOfSight(Target, TempSource, colPoint, ent, true, false, false, true, false, true, true);
+ if(Obscured2){
+ Dist2 = (Target - colPoint.point).Magnitude2D();
+ if(UseEntityPos)
+ Obscured2 = CWorld::ProcessLineOfSight(EntityPos, TempSource, colPoint, ent, true, false, false, true, false, true, true);
+ }
+ }
+ LastObscured = Obscured1 || Obscured2;
+ }
+
+ // nothing to do
+ if(!LastObscured)
+ return false;
+
+ if(Fix1){
+ Source.x = Target.x - cos(Beta)*Dist1;
+ Source.y = Target.y - sin(Beta)*Dist1;
+ if(Mode == MODE_BEHINDCAR)
+ Source = colPoint.point;
+ }else{
+ WellBufferMe(Dist2, &m_fDistanceBeforeChanges, &DistanceSpeed, 0.2f, 0.025f, false);
+ Source.x = Target.x - cos(Beta)*m_fDistanceBeforeChanges;
+ Source.y = Target.y - sin(Beta)*m_fDistanceBeforeChanges;
+ }
+
+ if(ResetStatics){
+ m_fDistanceBeforeChanges = (Source - Target).Magnitude2D();
+ DistanceSpeed = 0.0f;
+ Source.x = colPoint.point.x;
+ Source.y = colPoint.point.y;
+ }
+ return true;
+}
+
+void
+CCam::Process_Cam_On_A_String(const CVector &CameraTarget, float TargetOrientation, float, float)
+{
+ if(!CamTargetEntity->IsVehicle())
+ return;
+
+ FOV = DefaultFOV;
+
+ if(ResetStatics){
+ AlphaSpeed = 0.0f;
+ if(TheCamera.m_bIdleOn)
+ TheCamera.m_uiTimeWeEnteredIdle = CTimer::GetTimeInMilliseconds();
+ }
+
+ CBaseModelInfo *mi = CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex());
+ CVector Dimensions = mi->GetColModel()->boundingBox.max - mi->GetColModel()->boundingBox.min;
+ float BaseDist = Dimensions.Magnitude2D();
+
+ CVector TargetCoors = CameraTarget;
+ TargetCoors.z += Dimensions.z - 0.1f; // final
+ Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y);
+ while(Alpha >= PI) Alpha -= 2*PI;
+ while(Alpha < -PI) Alpha += 2*PI;
+ while(Beta >= PI) Beta -= 2*PI;
+ while(Beta < -PI) Beta += 2*PI;
+
+ m_fDistanceBeforeChanges = (Source - TargetCoors).Magnitude2D();
+
+ Cam_On_A_String_Unobscured(TargetCoors, BaseDist);
+ WorkOutCamHeight(TargetCoors, TargetOrientation, Dimensions.z);
+ RotCamIfInFrontCar(TargetCoors, TargetOrientation);
+ FixCamIfObscured(TargetCoors, Dimensions.z, TargetOrientation);
+ FixCamWhenObscuredByVehicle(TargetCoors);
+
+ m_cvecTargetCoorsForFudgeInter = TargetCoors;
+ Front = TargetCoors - Source;
+ Front.Normalise();
+ GetVectorsReadyForRW();
+ ResetStatics = false;
+}
+
+// Basic Cam on a string algorithm
+void
+CCam::Cam_On_A_String_Unobscured(const CVector &TargetCoors, float BaseDist)
+{
+ CA_MAX_DISTANCE = BaseDist + 0.1f + TheCamera.CarZoomValueSmooth;
+ CA_MIN_DISTANCE = min(BaseDist*0.6f, 3.5f);
+
+ CVector Dist = Source - TargetCoors;
+
+ if(ResetStatics)
+ Source = TargetCoors + Dist*(CA_MAX_DISTANCE + 1.0f);
+
+ float Length = Dist.Magnitude2D();
+ if(Length < 0.001f){
+ // This probably shouldn't happen. reset view
+ CVector Forward = CamTargetEntity->GetForward();
+ Forward.z = 0.0f;
+ Forward.Normalise();
+ Source = TargetCoors - Forward*CA_MAX_DISTANCE;
+ Dist = Source - TargetCoors;
+ Length = Dist.Magnitude2D();
+ }
+
+ if(Length > CA_MAX_DISTANCE){
+ Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE;
+ Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE;
+ }else if(Length < CA_MIN_DISTANCE){
+ Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE;
+ Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE;
+ }
+}
+
+void
+CCam::FixCamWhenObscuredByVehicle(const CVector &TargetCoors)
+{
+ // BUG? is this never reset
+ static float HeightFixerCarsObscuring = 0.0f;
+ static float HeightFixerCarsObscuringSpeed = 0.0f;
+ CColPoint colPoint;
+ CEntity *entity;
+
+ float HeightTarget = 0.0f;
+ if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, false, true, false, false, false, false, false)){
+ CBaseModelInfo *mi = CModelInfo::GetModelInfo(entity->GetModelIndex());
+ HeightTarget = mi->GetColModel()->boundingBox.max.z + 1.0f + TargetCoors.z - Source.z;
+ if(HeightTarget < 0.0f)
+ HeightTarget = 0.0f;
+ }
+ WellBufferMe(HeightTarget, &HeightFixerCarsObscuring, &HeightFixerCarsObscuringSpeed, 0.2f, 0.025f, false);
+ Source.z += HeightFixerCarsObscuring;
+}
+
+bool
+CCam::Using3rdPersonMouseCam()
+{
+ return CCamera::m_bUseMouse3rdPerson &&
+ (Mode == MODE_FOLLOWPED ||
+ TheCamera.m_bPlayerIsInGarage &&
+ FindPlayerPed() && FindPlayerPed()->m_nPedState != PED_DRIVING &&
+ Mode != MODE_TOPDOWN1 && this->CamTargetEntity == FindPlayerPed());
+}
+
+bool
+CCam::GetWeaponFirstPersonOn()
+{
+ CEntity *target = this->CamTargetEntity;
+ if (target && target->IsPed())
+ return ((CPed*)target)->GetWeapon()->m_bAddRotOffset;
+
+ return false;
+}
+
+STARTPATCHES
+ InjectHook(0x42C760, &CCamera::IsSphereVisible, PATCH_JUMP);
+ InjectHook(0x46FD00, &CCamera::SetFadeColour, PATCH_JUMP);
+
+ InjectHook(0x46FD40, &CCamera::SetMotionBlur, PATCH_JUMP);
+ InjectHook(0x46FD80, &CCamera::SetMotionBlurAlpha, PATCH_JUMP);
+ InjectHook(0x46F940, &CCamera::RenderMotionBlur, PATCH_JUMP);
+
+ InjectHook(0x456F40, WellBufferMe, PATCH_JUMP);
+ InjectHook(0x4582F0, &CCam::GetVectorsReadyForRW, PATCH_JUMP);
+ InjectHook(0x457710, &CCam::DoAverageOnVector, PATCH_JUMP);
+ InjectHook(0x458060, &CCam::GetPedBetaAngleForClearView, PATCH_JUMP);
+ InjectHook(0x457210, &CCam::Cam_On_A_String_Unobscured, PATCH_JUMP);
+ InjectHook(0x457A80, &CCam::FixCamWhenObscuredByVehicle, PATCH_JUMP);
+ InjectHook(0x457B90, &CCam::FixCamIfObscured, PATCH_JUMP);
+ InjectHook(0x465DA0, &CCam::RotCamIfInFrontCar, PATCH_JUMP);
+ InjectHook(0x4662D0, &CCam::WorkOutCamHeightWeeCar, PATCH_JUMP);
+ InjectHook(0x466650, &CCam::WorkOutCamHeight, PATCH_JUMP);
+
+ InjectHook(0x45E3A0, &CCam::Process_FollowPed, PATCH_JUMP);
+ InjectHook(0x45BE60, &CCam::Process_BehindCar, PATCH_JUMP);
+ InjectHook(0x45C090, &CCam::Process_Cam_On_A_String, PATCH_JUMP);
+
+ InjectHook(0x473250, &CCamera::dtor, PATCH_JUMP);
+ENDPATCHES