2019-12-26 15:33:43 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
|
CameraController.cpp: Implements controls for a camera with pseudo-physics
|
|
|
|
|
=============================================================================*/
|
|
|
|
|
|
|
|
|
|
#include "CameraController.h"
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3209340 on 2016/11/23 by Ben.Marsh
Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h.
Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms.
* Every header now includes everything it needs to compile.
* There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first.
* There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h.
* Every .cpp file includes its matching .h file first.
* This helps validate that each header is including everything it needs to compile.
* No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more.
* You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there.
* There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible.
* No engine code explicitly includes a precompiled header any more.
* We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies.
* PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files.
Tool used to generate this transform is at Engine\Source\Programs\IncludeTool.
[CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
|
|
|
#include "EditorModeManager.h"
|
|
|
|
|
#include "EditorModes.h"
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Constructor */
|
|
|
|
|
FEditorCameraController::FEditorCameraController()
|
|
|
|
|
: Config(),
|
|
|
|
|
MovementVelocity( 0.0f, 0.0f, 0.0f ),
|
|
|
|
|
FOVVelocity( 0.0f ),
|
|
|
|
|
RotationVelocityEuler( 0.0f, 0.0f, 0.0f ),
|
|
|
|
|
OriginalFOVForRecoil( -1.0f ) // -1.0f here means 'initialize me on demand'
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the position and orientation of the camera as well as other state (like velocity.) Should be
|
|
|
|
|
* called every frame.
|
|
|
|
|
*
|
|
|
|
|
* @param UserImpulseData Input data from the user this frame
|
|
|
|
|
* @param DeltaTime Time interval since last update
|
|
|
|
|
* @param bAllowRecoilIfNoImpulse True if we should recoil FOV if needed
|
|
|
|
|
* @param MovementSpeedScale Scales the speed of movement
|
|
|
|
|
* @param InOutCameraPosition [in, out] Camera position
|
|
|
|
|
* @param InOutCameraEuler [in, out] Camera orientation
|
|
|
|
|
* @param InOutCameraFOV [in, out] Camera field of view
|
|
|
|
|
*/
|
|
|
|
|
void FEditorCameraController::UpdateSimulation(
|
|
|
|
|
const FCameraControllerUserImpulseData& UserImpulseData,
|
|
|
|
|
const float DeltaTime,
|
|
|
|
|
const bool bAllowRecoilIfNoImpulse,
|
|
|
|
|
const float MovementSpeedScale,
|
|
|
|
|
FVector& InOutCameraPosition,
|
|
|
|
|
FVector& InOutCameraEuler,
|
|
|
|
|
float& InOutCameraFOV )
|
|
|
|
|
{
|
|
|
|
|
bool bAnyUserImpulse = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Apply dead zone test to user impulse data
|
|
|
|
|
//ApplyImpulseDeadZone( UserImpulseData, FinalUserImpulse, bAnyUserImpulse );
|
|
|
|
|
if( UserImpulseData.RotateYawVelocityModifier != 0.0f ||
|
|
|
|
|
UserImpulseData.RotatePitchVelocityModifier != 0.0f ||
|
|
|
|
|
UserImpulseData.RotateRollVelocityModifier != 0.0f ||
|
|
|
|
|
UserImpulseData.MoveForwardBackwardImpulse != 0.0f ||
|
|
|
|
|
UserImpulseData.MoveRightLeftImpulse!= 0.0f ||
|
|
|
|
|
UserImpulseData.MoveUpDownImpulse != 0.0f ||
|
|
|
|
|
UserImpulseData.ZoomOutInImpulse != 0.0f ||
|
|
|
|
|
UserImpulseData.RotateYawImpulse != 0.0f ||
|
|
|
|
|
UserImpulseData.RotatePitchImpulse != 0.0f ||
|
|
|
|
|
UserImpulseData.RotateRollImpulse != 0.0f
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
bAnyUserImpulse = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FVector TranslationCameraEuler = InOutCameraEuler;
|
|
|
|
|
if (Config.bPlanarCamera)
|
|
|
|
|
{
|
|
|
|
|
//remove roll
|
|
|
|
|
TranslationCameraEuler.X = 0;
|
|
|
|
|
//remove pitch
|
|
|
|
|
TranslationCameraEuler.Y = 0;
|
|
|
|
|
}
|
|
|
|
|
// Movement
|
|
|
|
|
UpdatePosition( UserImpulseData, DeltaTime, MovementSpeedScale, TranslationCameraEuler, InOutCameraPosition );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Rotation
|
|
|
|
|
UpdateRotation( UserImpulseData, DeltaTime, InOutCameraEuler );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// FOV
|
|
|
|
|
UpdateFOV( UserImpulseData, DeltaTime, InOutCameraFOV );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Recoil camera FOV if we need to
|
|
|
|
|
ApplyRecoil( DeltaTime, bAllowRecoilIfNoImpulse, bAnyUserImpulse, InOutCameraFOV );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-11-07 23:43:01 -05:00
|
|
|
void FEditorCameraController::ResetVelocity()
|
|
|
|
|
{
|
|
|
|
|
MovementVelocity = FVector::ZeroVector;
|
|
|
|
|
FOVVelocity = 0.f;
|
|
|
|
|
RotationVelocityEuler = FVector::ZeroVector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
/**true if this camera currently has rotational velocity*/
|
|
|
|
|
bool FEditorCameraController::IsRotating (void) const
|
|
|
|
|
{
|
|
|
|
|
if ((RotationVelocityEuler.X != 0.0f) || (RotationVelocityEuler.Y != 0.0f) || (RotationVelocityEuler.Z != 0.0f))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies the dead zone setting to the incoming user impulse data
|
|
|
|
|
*
|
|
|
|
|
* @param InUserImpulse Input user impulse data
|
|
|
|
|
* @param OutUserImpulse [out] Output user impulse data with dead zone applied
|
|
|
|
|
* @param bOutAnyImpulseData [out] True if there was any user impulse this frame
|
|
|
|
|
*/
|
|
|
|
|
void FEditorCameraController::ApplyImpulseDeadZone( const FCameraControllerUserImpulseData& InUserImpulse,
|
|
|
|
|
FCameraControllerUserImpulseData& OutUserImpulse,
|
|
|
|
|
bool& bOutAnyImpulseData )
|
|
|
|
|
{
|
|
|
|
|
FMemory::Memzero( &OutUserImpulse, sizeof( OutUserImpulse ) );
|
|
|
|
|
|
|
|
|
|
// Keep track if there is any actual user input. This is so that when all of the flight controls
|
|
|
|
|
// are released, we can take action (such as resetting the camera FOV back to what it was.)
|
|
|
|
|
bOutAnyImpulseData = false;
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( InUserImpulse.MoveRightLeftImpulse ) >= Config.ImpulseDeadZoneAmount )
|
|
|
|
|
{
|
|
|
|
|
OutUserImpulse.MoveRightLeftImpulse = InUserImpulse.MoveRightLeftImpulse;
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( InUserImpulse.MoveForwardBackwardImpulse ) >= Config.ImpulseDeadZoneAmount )
|
|
|
|
|
{
|
|
|
|
|
OutUserImpulse.MoveForwardBackwardImpulse = InUserImpulse.MoveForwardBackwardImpulse;
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( InUserImpulse.MoveUpDownImpulse ) >= Config.ImpulseDeadZoneAmount )
|
|
|
|
|
{
|
|
|
|
|
OutUserImpulse.MoveUpDownImpulse = InUserImpulse.MoveUpDownImpulse;
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( InUserImpulse.RotateYawImpulse ) >= Config.ImpulseDeadZoneAmount )
|
|
|
|
|
{
|
|
|
|
|
OutUserImpulse.RotateYawImpulse = InUserImpulse.RotateYawImpulse;
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( InUserImpulse.RotatePitchImpulse ) >= Config.ImpulseDeadZoneAmount )
|
|
|
|
|
{
|
|
|
|
|
OutUserImpulse.RotatePitchImpulse = InUserImpulse.RotatePitchImpulse;
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( InUserImpulse.RotateRollImpulse ) >= Config.ImpulseDeadZoneAmount )
|
|
|
|
|
{
|
|
|
|
|
OutUserImpulse.RotateRollImpulse = InUserImpulse.RotateRollImpulse;
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( InUserImpulse.ZoomOutInImpulse ) >= Config.ImpulseDeadZoneAmount )
|
|
|
|
|
{
|
|
|
|
|
OutUserImpulse.ZoomOutInImpulse = InUserImpulse.ZoomOutInImpulse;
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No dead zone for these
|
|
|
|
|
OutUserImpulse.RotateYawVelocityModifier = InUserImpulse.RotateYawVelocityModifier;
|
|
|
|
|
OutUserImpulse.RotatePitchVelocityModifier = InUserImpulse.RotatePitchVelocityModifier;
|
|
|
|
|
OutUserImpulse.RotateRollVelocityModifier = InUserImpulse.RotateRollVelocityModifier;
|
|
|
|
|
if( OutUserImpulse.RotateYawVelocityModifier != 0.0f ||
|
|
|
|
|
OutUserImpulse.RotatePitchVelocityModifier != 0.0f ||
|
|
|
|
|
OutUserImpulse.RotateRollVelocityModifier != 0.0f )
|
|
|
|
|
{
|
|
|
|
|
bOutAnyImpulseData = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the camera position. Called every frame by UpdateSimulation.
|
|
|
|
|
*
|
|
|
|
|
* @param UserImpulse User impulse data for the current frame
|
|
|
|
|
* @param DeltaTime Time interval
|
|
|
|
|
* @param MovementSpeedScale Additional movement accel/speed scale
|
|
|
|
|
* @param CameraEuler Current camera rotation
|
|
|
|
|
* @param InOutCameraPosition [in, out] Camera position
|
|
|
|
|
*/
|
|
|
|
|
void FEditorCameraController::UpdatePosition( const FCameraControllerUserImpulseData& UserImpulse, const float DeltaTime, const float MovementSpeedScale, const FVector& CameraEuler, FVector& InOutCameraPosition )
|
|
|
|
|
{
|
|
|
|
|
// Compute local impulse
|
|
|
|
|
FVector LocalSpaceImpulse;
|
|
|
|
|
{
|
|
|
|
|
// NOTE: Forward/back and right/left impulse are applied in local space, but up/down impulse is
|
|
|
|
|
// applied in world space. This is because it feels more intuitive to always move straight
|
|
|
|
|
// up or down with those controls.
|
|
|
|
|
LocalSpaceImpulse =
|
|
|
|
|
FVector( UserImpulse.MoveForwardBackwardImpulse, // Local space forward/back
|
|
|
|
|
UserImpulse.MoveRightLeftImpulse, // Local space right/left
|
|
|
|
|
0.0f ); // Local space up/down
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Compute world space acceleration
|
|
|
|
|
FVector WorldSpaceAcceleration;
|
|
|
|
|
{
|
|
|
|
|
// Compute camera orientation, then rotate our local space impulse to world space
|
|
|
|
|
const FQuat CameraOrientation = FQuat::MakeFromEuler( CameraEuler );
|
|
|
|
|
FVector WorldSpaceImpulse = CameraOrientation.RotateVector( LocalSpaceImpulse );
|
|
|
|
|
|
|
|
|
|
// Accumulate any world space impulse we may have
|
|
|
|
|
// NOTE: Up/down impulse is applied in world space. See above comments for more info.
|
|
|
|
|
WorldSpaceImpulse +=
|
|
|
|
|
FVector( 0.0f, // World space forward/back
|
|
|
|
|
0.0f, // World space right/left
|
|
|
|
|
UserImpulse.MoveUpDownImpulse ); // World space up/down
|
|
|
|
|
|
|
|
|
|
// Cap impulse by normalizing, but only if our magnitude is greater than 1.0
|
|
|
|
|
//if( WorldSpaceImpulse.SizeSquared() > 1.0f )
|
|
|
|
|
{
|
|
|
|
|
//WorldSpaceImpulse = WorldSpaceImpulse.UnsafeNormal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute world space acceleration
|
|
|
|
|
WorldSpaceAcceleration = WorldSpaceImpulse * Config.MovementAccelerationRate * MovementSpeedScale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( Config.bUsePhysicsBasedMovement )
|
|
|
|
|
{
|
|
|
|
|
// Accelerate the movement velocity
|
|
|
|
|
MovementVelocity += WorldSpaceAcceleration * DeltaTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Apply damping
|
|
|
|
|
{
|
|
|
|
|
const float DampingFactor = FMath::Clamp( Config.MovementVelocityDampingAmount * DeltaTime, 0.0f, 0.75f );
|
|
|
|
|
|
|
|
|
|
// Decelerate
|
|
|
|
|
MovementVelocity += -MovementVelocity * DampingFactor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No physics, so just use the acceleration as our velocity
|
|
|
|
|
MovementVelocity = WorldSpaceAcceleration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Constrain maximum movement speed
|
2015-09-06 12:42:18 -04:00
|
|
|
if( MovementVelocity.SizeSquared() > FMath::Square(Config.MaximumMovementSpeed * MovementSpeedScale) )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-11-26 10:01:12 -05:00
|
|
|
MovementVelocity = MovementVelocity.GetUnsafeNormal() * Config.MaximumMovementSpeed * MovementSpeedScale;
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Clamp velocity to a reasonably small number
|
2015-09-06 12:42:18 -04:00
|
|
|
if( MovementVelocity.SizeSquared() < FMath::Square(KINDA_SMALL_NUMBER) )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
MovementVelocity = FVector::ZeroVector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update camera position
|
|
|
|
|
InOutCameraPosition += MovementVelocity * DeltaTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the camera rotation. Called every frame by UpdateSimulation.
|
|
|
|
|
*
|
|
|
|
|
* @param UserImpulse User impulse data for this frame
|
|
|
|
|
* @param DeltaTime Time interval
|
|
|
|
|
* @param InOutCameraEuler [in, out] Camera rotation
|
|
|
|
|
*/
|
|
|
|
|
void FEditorCameraController::UpdateRotation( const FCameraControllerUserImpulseData& UserImpulse, const float DeltaTime, FVector &InOutCameraEuler )
|
|
|
|
|
{
|
|
|
|
|
FVector RotateImpulseEuler =
|
|
|
|
|
FVector( UserImpulse.RotateRollImpulse,
|
|
|
|
|
UserImpulse.RotatePitchImpulse,
|
|
|
|
|
UserImpulse.RotateYawImpulse );
|
|
|
|
|
|
|
|
|
|
FVector RotateVelocityModifierEuler =
|
|
|
|
|
FVector( UserImpulse.RotateRollVelocityModifier,
|
|
|
|
|
UserImpulse.RotatePitchVelocityModifier,
|
|
|
|
|
UserImpulse.RotateYawVelocityModifier );
|
|
|
|
|
|
|
|
|
|
// Iterate for each euler axis - yaw, pitch and roll
|
|
|
|
|
for( int32 CurRotationAxis = 0; CurRotationAxis < 3; ++CurRotationAxis )
|
|
|
|
|
{
|
|
|
|
|
// This will serve as both our source and destination rotation value
|
2021-05-05 15:07:25 -04:00
|
|
|
FVector::FReal& RotationVelocity = RotationVelocityEuler[ CurRotationAxis ];
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
const float RotationImpulse = RotateImpulseEuler[ CurRotationAxis ];
|
|
|
|
|
const float RotationVelocityModifier = RotateVelocityModifierEuler[ CurRotationAxis ];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Compute acceleration
|
|
|
|
|
float RotationAcceleration = RotationImpulse * Config.RotationAccelerationRate;
|
|
|
|
|
|
|
|
|
|
if( Config.bUsePhysicsBasedRotation || Config.bForceRotationalPhysics)
|
|
|
|
|
{
|
|
|
|
|
// Accelerate the rotation velocity
|
|
|
|
|
RotationVelocity += RotationAcceleration * DeltaTime;
|
|
|
|
|
|
|
|
|
|
// Apply velocity modifier. This is used for mouse-look based camera rotation, where
|
|
|
|
|
// we don't need to account for DeltaTime, since the value is based on an explicit number
|
|
|
|
|
// of degrees per cursor pixel moved.
|
|
|
|
|
RotationVelocity += RotationVelocityModifier;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Apply damping
|
|
|
|
|
{
|
|
|
|
|
const float DampingFactor = FMath::Clamp( Config.RotationVelocityDampingAmount * DeltaTime, 0.0f, 0.75f );
|
|
|
|
|
|
|
|
|
|
// Decelerate
|
|
|
|
|
RotationVelocity += -RotationVelocity * DampingFactor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No physics, so just use the acceleration as our velocity
|
|
|
|
|
RotationVelocity = RotationAcceleration;
|
|
|
|
|
|
|
|
|
|
// Apply velocity modifier. This is used for mouse-look based camera rotation, where
|
|
|
|
|
// we don't need to account for DeltaTime, since the value is based on an explicit number
|
|
|
|
|
// of degrees per cursor pixel moved.
|
|
|
|
|
RotationVelocity += RotationVelocityModifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Constrain maximum rotation speed
|
|
|
|
|
RotationVelocity = FMath::Clamp<float>(RotationVelocity, -Config.MaximumRotationSpeed, Config.MaximumRotationSpeed);
|
|
|
|
|
|
|
|
|
|
// Clamp velocity to a reasonably small number
|
|
|
|
|
if( FMath::Abs( RotationVelocity ) < KINDA_SMALL_NUMBER )
|
|
|
|
|
{
|
|
|
|
|
RotationVelocity = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update rotation
|
|
|
|
|
InOutCameraEuler[ CurRotationAxis ] += RotationVelocity * DeltaTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Constrain final pitch rotation value to configured range
|
|
|
|
|
if( CurRotationAxis == 1 ) // 1 == pitch
|
|
|
|
|
{
|
|
|
|
|
// Normalize the angle to -180 to 180.
|
2021-11-18 14:37:34 -05:00
|
|
|
FVector::FReal Angle = FMath::Fmod(InOutCameraEuler[ CurRotationAxis ], 360.0);
|
|
|
|
|
if (Angle > 180.)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2021-11-18 14:37:34 -05:00
|
|
|
Angle -= 360.;
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
2021-11-18 14:37:34 -05:00
|
|
|
else if (Angle < -180.)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2021-11-18 14:37:34 -05:00
|
|
|
Angle += 360.;
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2021-06-17 14:53:36 -04:00
|
|
|
if (Config.bLockedPitch)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
// Clamp the angle.
|
|
|
|
|
InOutCameraEuler[ CurRotationAxis ] =
|
|
|
|
|
FMath::Clamp( Angle,
|
2021-11-18 14:37:34 -05:00
|
|
|
(FVector::FReal)Config.MinimumAllowedPitchRotation,
|
|
|
|
|
(FVector::FReal)Config.MaximumAllowedPitchRotation );
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update the field of view. Called every frame by UpdateSimulation.
|
|
|
|
|
*
|
|
|
|
|
* @param UserImpulse User impulse data for this frame
|
|
|
|
|
* @param DeltaTime Time interval
|
|
|
|
|
* @param InOutCameraFOV [in, out] Camera field of view
|
|
|
|
|
*/
|
|
|
|
|
void FEditorCameraController::UpdateFOV( const FCameraControllerUserImpulseData& UserImpulse, const float DeltaTime, float& InOutCameraFOV )
|
|
|
|
|
{
|
|
|
|
|
// Compute acceleration
|
|
|
|
|
float FOVAcceleration = UserImpulse.ZoomOutInImpulse * Config.FOVAccelerationRate;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Is the user actively changing the FOV?
|
|
|
|
|
if( FMath::Abs( FOVAcceleration ) > KINDA_SMALL_NUMBER )
|
|
|
|
|
{
|
|
|
|
|
// If we've never cached a FOV, then go ahead and do that now
|
|
|
|
|
if( OriginalFOVForRecoil < 0.0f )
|
|
|
|
|
{
|
|
|
|
|
OriginalFOVForRecoil = InOutCameraFOV;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( Config.bUsePhysicsBasedFOV )
|
|
|
|
|
{
|
|
|
|
|
// Accelerate the FOV velocity
|
|
|
|
|
FOVVelocity += FOVAcceleration * DeltaTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Apply damping
|
|
|
|
|
{
|
|
|
|
|
const float DampingFactor = FMath::Clamp( Config.FOVVelocityDampingAmount * DeltaTime, 0.0f, 0.75f );
|
|
|
|
|
|
|
|
|
|
// Decelerate
|
|
|
|
|
FOVVelocity += -FOVVelocity * DampingFactor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No physics, so just use the acceleration as our velocity
|
|
|
|
|
FOVVelocity = FOVAcceleration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Constrain maximum FOV speed
|
|
|
|
|
FOVVelocity = FMath::Clamp<float>(FOVVelocity, -Config.MaximumFOVSpeed, Config.MaximumFOVSpeed );
|
|
|
|
|
|
|
|
|
|
// Clamp velocity to a reasonably small number
|
|
|
|
|
if( FMath::Abs( FOVVelocity ) < KINDA_SMALL_NUMBER )
|
|
|
|
|
{
|
|
|
|
|
FOVVelocity = 0.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update camera FOV
|
|
|
|
|
InOutCameraFOV += FOVVelocity * DeltaTime;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Constrain final FOV to configured range
|
|
|
|
|
InOutCameraFOV = FMath::Clamp( InOutCameraFOV, Config.MinimumAllowedFOV, Config.MaximumAllowedFOV );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies FOV recoil (if appropriate). Called every frame by UpdateSimulation.
|
|
|
|
|
*
|
|
|
|
|
* @param DeltaTime Time interval
|
|
|
|
|
* @param bAllowRecoilIfNoImpulse Whether recoil should be allowed if there wasn't any user impulse
|
|
|
|
|
* @param bAnyUserImpulse True if there was user impulse data this iteration
|
|
|
|
|
* @param InOutCameraFOV [in, out] Camera field of view
|
|
|
|
|
*/
|
|
|
|
|
void FEditorCameraController::ApplyRecoil( const float DeltaTime, const bool bAllowRecoilIfNoImpulse, bool bAnyUserImpulse, float& InOutCameraFOV )
|
|
|
|
|
{
|
|
|
|
|
bool bIsRecoilingFOV = false;
|
|
|
|
|
|
|
|
|
|
// Is the FOV 'recoil' feature enabled? If so, we'll smoothly snap the FOV angle back to what
|
|
|
|
|
// it was before the user started interacting with the camera.
|
|
|
|
|
if( Config.bEnableFOVRecoil )
|
|
|
|
|
{
|
|
|
|
|
// We don't need to recoil if the user hasn't started changing the FOV yet
|
|
|
|
|
if( OriginalFOVForRecoil >= 0.0f )
|
|
|
|
|
{
|
|
|
|
|
// If there isn't any user impulse, then go ahead and recoil the camera FOV
|
|
|
|
|
if( !bAnyUserImpulse && bAllowRecoilIfNoImpulse )
|
|
|
|
|
{
|
|
|
|
|
// Kill any physics-based FOV velocity
|
|
|
|
|
FOVVelocity = 0.0f;
|
|
|
|
|
|
|
|
|
|
const float FOVDistance = FMath::Abs( InOutCameraFOV - OriginalFOVForRecoil );
|
|
|
|
|
if( FOVDistance > 0.1f )
|
|
|
|
|
{
|
|
|
|
|
// Recoil speed in 'distances' per second
|
|
|
|
|
const float CameraFOVRecoilSpeedScale = 10.0f;
|
|
|
|
|
|
|
|
|
|
if( InOutCameraFOV < OriginalFOVForRecoil )
|
|
|
|
|
{
|
|
|
|
|
InOutCameraFOV += FOVDistance * DeltaTime * CameraFOVRecoilSpeedScale;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
InOutCameraFOV -= FOVDistance * DeltaTime * CameraFOVRecoilSpeedScale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We've tinkered with the FOV, so make sure we don't cache these changes
|
|
|
|
|
bIsRecoilingFOV = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Close enough, so snap it!
|
|
|
|
|
InOutCameraFOV = OriginalFOVForRecoil;
|
|
|
|
|
|
|
|
|
|
// We're done done manipulating the FOV for now
|
|
|
|
|
OriginalFOVForRecoil = -1.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|