471 lines
12 KiB
C++
471 lines
12 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "LyraCameraMode.h"
|
|
|
|
#include "Components/CapsuleComponent.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "Engine/Canvas.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "GameFramework/Character.h"
|
|
#include "GameFramework/Pawn.h"
|
|
#include "LyraCameraComponent.h"
|
|
#include "LyraPlayerCameraManager.h"
|
|
#include "Math/Color.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Templates/Casts.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/UnrealNames.h"
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FLyraCameraModeView
|
|
//////////////////////////////////////////////////////////////////////////
|
|
FLyraCameraModeView::FLyraCameraModeView()
|
|
: Location(ForceInit)
|
|
, Rotation(ForceInit)
|
|
, ControlRotation(ForceInit)
|
|
, FieldOfView(LYRA_CAMERA_DEFAULT_FOV)
|
|
{
|
|
}
|
|
|
|
void FLyraCameraModeView::Blend(const FLyraCameraModeView& Other, float OtherWeight)
|
|
{
|
|
if (OtherWeight <= 0.0f)
|
|
{
|
|
return;
|
|
}
|
|
else if (OtherWeight >= 1.0f)
|
|
{
|
|
*this = Other;
|
|
return;
|
|
}
|
|
|
|
Location = FMath::Lerp(Location, Other.Location, OtherWeight);
|
|
|
|
const FRotator DeltaRotation = (Other.Rotation - Rotation).GetNormalized();
|
|
Rotation = Rotation + (OtherWeight * DeltaRotation);
|
|
|
|
const FRotator DeltaControlRotation = (Other.ControlRotation - ControlRotation).GetNormalized();
|
|
ControlRotation = ControlRotation + (OtherWeight * DeltaControlRotation);
|
|
|
|
FieldOfView = FMath::Lerp(FieldOfView, Other.FieldOfView, OtherWeight);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// ULyraCameraMode
|
|
//////////////////////////////////////////////////////////////////////////
|
|
ULyraCameraMode::ULyraCameraMode()
|
|
{
|
|
FieldOfView = LYRA_CAMERA_DEFAULT_FOV;
|
|
ViewPitchMin = LYRA_CAMERA_DEFAULT_PITCH_MIN;
|
|
ViewPitchMax = LYRA_CAMERA_DEFAULT_PITCH_MAX;
|
|
|
|
BlendTime = 0.5f;
|
|
BlendFunction = ELyraCameraModeBlendFunction::EaseOut;
|
|
BlendExponent = 4.0f;
|
|
BlendAlpha = 1.0f;
|
|
BlendWeight = 1.0f;
|
|
}
|
|
|
|
ULyraCameraComponent* ULyraCameraMode::GetLyraCameraComponent() const
|
|
{
|
|
return CastChecked<ULyraCameraComponent>(GetOuter());
|
|
}
|
|
|
|
UWorld* ULyraCameraMode::GetWorld() const
|
|
{
|
|
return HasAnyFlags(RF_ClassDefaultObject) ? nullptr : GetOuter()->GetWorld();
|
|
}
|
|
|
|
AActor* ULyraCameraMode::GetTargetActor() const
|
|
{
|
|
const ULyraCameraComponent* LyraCameraComponent = GetLyraCameraComponent();
|
|
|
|
return LyraCameraComponent->GetTargetActor();
|
|
}
|
|
|
|
FVector ULyraCameraMode::GetPivotLocation() const
|
|
{
|
|
const AActor* TargetActor = GetTargetActor();
|
|
check(TargetActor);
|
|
|
|
if (const APawn* TargetPawn = Cast<APawn>(TargetActor))
|
|
{
|
|
// Height adjustments for characters to account for crouching.
|
|
if (const ACharacter* TargetCharacter = Cast<ACharacter>(TargetPawn))
|
|
{
|
|
const ACharacter* TargetCharacterCDO = TargetCharacter->GetClass()->GetDefaultObject<ACharacter>();
|
|
check(TargetCharacterCDO);
|
|
|
|
const UCapsuleComponent* CapsuleComp = TargetCharacter->GetCapsuleComponent();
|
|
check(CapsuleComp);
|
|
|
|
const UCapsuleComponent* CapsuleCompCDO = TargetCharacterCDO->GetCapsuleComponent();
|
|
check(CapsuleCompCDO);
|
|
|
|
const float DefaultHalfHeight = CapsuleCompCDO->GetUnscaledCapsuleHalfHeight();
|
|
const float ActualHalfHeight = CapsuleComp->GetUnscaledCapsuleHalfHeight();
|
|
const float HeightAdjustment = (DefaultHalfHeight - ActualHalfHeight) + TargetCharacterCDO->BaseEyeHeight;
|
|
|
|
return TargetCharacter->GetActorLocation() + (FVector::UpVector * HeightAdjustment);
|
|
}
|
|
|
|
return TargetPawn->GetPawnViewLocation();
|
|
}
|
|
|
|
return TargetActor->GetActorLocation();
|
|
}
|
|
|
|
FRotator ULyraCameraMode::GetPivotRotation() const
|
|
{
|
|
const AActor* TargetActor = GetTargetActor();
|
|
check(TargetActor);
|
|
|
|
if (const APawn* TargetPawn = Cast<APawn>(TargetActor))
|
|
{
|
|
return TargetPawn->GetViewRotation();
|
|
}
|
|
|
|
return TargetActor->GetActorRotation();
|
|
}
|
|
|
|
void ULyraCameraMode::UpdateCameraMode(float DeltaTime)
|
|
{
|
|
UpdateView(DeltaTime);
|
|
UpdateBlending(DeltaTime);
|
|
}
|
|
|
|
void ULyraCameraMode::UpdateView(float DeltaTime)
|
|
{
|
|
FVector PivotLocation = GetPivotLocation();
|
|
FRotator PivotRotation = GetPivotRotation();
|
|
|
|
PivotRotation.Pitch = FMath::ClampAngle(PivotRotation.Pitch, ViewPitchMin, ViewPitchMax);
|
|
|
|
View.Location = PivotLocation;
|
|
View.Rotation = PivotRotation;
|
|
View.ControlRotation = View.Rotation;
|
|
View.FieldOfView = FieldOfView;
|
|
}
|
|
|
|
void ULyraCameraMode::SetBlendWeight(float Weight)
|
|
{
|
|
BlendWeight = FMath::Clamp(Weight, 0.0f, 1.0f);
|
|
|
|
// Since we're setting the blend weight directly, we need to calculate the blend alpha to account for the blend function.
|
|
const float InvExponent = (BlendExponent > 0.0f) ? (1.0f / BlendExponent) : 1.0f;
|
|
|
|
switch (BlendFunction)
|
|
{
|
|
case ELyraCameraModeBlendFunction::Linear:
|
|
BlendAlpha = BlendWeight;
|
|
break;
|
|
|
|
case ELyraCameraModeBlendFunction::EaseIn:
|
|
BlendAlpha = FMath::InterpEaseIn(0.0f, 1.0f, BlendWeight, InvExponent);
|
|
break;
|
|
|
|
case ELyraCameraModeBlendFunction::EaseOut:
|
|
BlendAlpha = FMath::InterpEaseOut(0.0f, 1.0f, BlendWeight, InvExponent);
|
|
break;
|
|
|
|
case ELyraCameraModeBlendFunction::EaseInOut:
|
|
BlendAlpha = FMath::InterpEaseInOut(0.0f, 1.0f, BlendWeight, InvExponent);
|
|
break;
|
|
|
|
default:
|
|
checkf(false, TEXT("SetBlendWeight: Invalid BlendFunction [%d]\n"), (uint8)BlendFunction);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ULyraCameraMode::UpdateBlending(float DeltaTime)
|
|
{
|
|
if (BlendTime > 0.0f)
|
|
{
|
|
BlendAlpha += (DeltaTime / BlendTime);
|
|
BlendAlpha = FMath::Min(BlendAlpha, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
BlendAlpha = 1.0f;
|
|
}
|
|
|
|
const float Exponent = (BlendExponent > 0.0f) ? BlendExponent : 1.0f;
|
|
|
|
switch (BlendFunction)
|
|
{
|
|
case ELyraCameraModeBlendFunction::Linear:
|
|
BlendWeight = BlendAlpha;
|
|
break;
|
|
|
|
case ELyraCameraModeBlendFunction::EaseIn:
|
|
BlendWeight = FMath::InterpEaseIn(0.0f, 1.0f, BlendAlpha, Exponent);
|
|
break;
|
|
|
|
case ELyraCameraModeBlendFunction::EaseOut:
|
|
BlendWeight = FMath::InterpEaseOut(0.0f, 1.0f, BlendAlpha, Exponent);
|
|
break;
|
|
|
|
case ELyraCameraModeBlendFunction::EaseInOut:
|
|
BlendWeight = FMath::InterpEaseInOut(0.0f, 1.0f, BlendAlpha, Exponent);
|
|
break;
|
|
|
|
default:
|
|
checkf(false, TEXT("UpdateBlending: Invalid BlendFunction [%d]\n"), (uint8)BlendFunction);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ULyraCameraMode::DrawDebug(UCanvas* Canvas) const
|
|
{
|
|
check(Canvas);
|
|
|
|
FDisplayDebugManager& DisplayDebugManager = Canvas->DisplayDebugManager;
|
|
|
|
DisplayDebugManager.SetDrawColor(FColor::White);
|
|
DisplayDebugManager.DrawString(FString::Printf(TEXT(" LyraCameraMode: %s (%f)"), *GetName(), BlendWeight));
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// ULyraCameraModeStack
|
|
//////////////////////////////////////////////////////////////////////////
|
|
ULyraCameraModeStack::ULyraCameraModeStack()
|
|
{
|
|
bIsActive = true;
|
|
}
|
|
|
|
void ULyraCameraModeStack::ActivateStack()
|
|
{
|
|
if (!bIsActive)
|
|
{
|
|
bIsActive = true;
|
|
|
|
// Notify camera modes that they are being activated.
|
|
for (ULyraCameraMode* CameraMode : CameraModeStack)
|
|
{
|
|
check(CameraMode);
|
|
CameraMode->OnActivation();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ULyraCameraModeStack::DeactivateStack()
|
|
{
|
|
if (bIsActive)
|
|
{
|
|
bIsActive = false;
|
|
|
|
// Notify camera modes that they are being deactivated.
|
|
for (ULyraCameraMode* CameraMode : CameraModeStack)
|
|
{
|
|
check(CameraMode);
|
|
CameraMode->OnDeactivation();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ULyraCameraModeStack::PushCameraMode(TSubclassOf<ULyraCameraMode> CameraModeClass)
|
|
{
|
|
if (!CameraModeClass)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ULyraCameraMode* CameraMode = GetCameraModeInstance(CameraModeClass);
|
|
check(CameraMode);
|
|
|
|
int32 StackSize = CameraModeStack.Num();
|
|
|
|
if ((StackSize > 0) && (CameraModeStack[0] == CameraMode))
|
|
{
|
|
// Already top of stack.
|
|
return;
|
|
}
|
|
|
|
// See if it's already in the stack and remove it.
|
|
// Figure out how much it was contributing to the stack.
|
|
int32 ExistingStackIndex = INDEX_NONE;
|
|
float ExistingStackContribution = 1.0f;
|
|
|
|
for (int32 StackIndex = 0; StackIndex < StackSize; ++StackIndex)
|
|
{
|
|
if (CameraModeStack[StackIndex] == CameraMode)
|
|
{
|
|
ExistingStackIndex = StackIndex;
|
|
ExistingStackContribution *= CameraMode->GetBlendWeight();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ExistingStackContribution *= (1.0f - CameraModeStack[StackIndex]->GetBlendWeight());
|
|
}
|
|
}
|
|
|
|
if (ExistingStackIndex != INDEX_NONE)
|
|
{
|
|
CameraModeStack.RemoveAt(ExistingStackIndex);
|
|
StackSize--;
|
|
}
|
|
else
|
|
{
|
|
ExistingStackContribution = 0.0f;
|
|
}
|
|
|
|
// Decide what initial weight to start with.
|
|
const bool bShouldBlend = ((CameraMode->GetBlendTime() > 0.0f) && (StackSize > 0));
|
|
const float BlendWeight = (bShouldBlend ? ExistingStackContribution : 1.0f);
|
|
|
|
CameraMode->SetBlendWeight(BlendWeight);
|
|
|
|
// Add new entry to top of stack.
|
|
CameraModeStack.Insert(CameraMode, 0);
|
|
|
|
// Make sure stack bottom is always weighted 100%.
|
|
CameraModeStack.Last()->SetBlendWeight(1.0f);
|
|
|
|
// Let the camera mode know if it's being added to the stack.
|
|
if (ExistingStackIndex == INDEX_NONE)
|
|
{
|
|
CameraMode->OnActivation();
|
|
}
|
|
}
|
|
|
|
bool ULyraCameraModeStack::EvaluateStack(float DeltaTime, FLyraCameraModeView& OutCameraModeView)
|
|
{
|
|
if (!bIsActive)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
UpdateStack(DeltaTime);
|
|
BlendStack(OutCameraModeView);
|
|
|
|
return true;
|
|
}
|
|
|
|
ULyraCameraMode* ULyraCameraModeStack::GetCameraModeInstance(TSubclassOf<ULyraCameraMode> CameraModeClass)
|
|
{
|
|
check(CameraModeClass);
|
|
|
|
// First see if we already created one.
|
|
for (ULyraCameraMode* CameraMode : CameraModeInstances)
|
|
{
|
|
if ((CameraMode != nullptr) && (CameraMode->GetClass() == CameraModeClass))
|
|
{
|
|
return CameraMode;
|
|
}
|
|
}
|
|
|
|
// Not found, so we need to create it.
|
|
ULyraCameraMode* NewCameraMode = NewObject<ULyraCameraMode>(GetOuter(), CameraModeClass, NAME_None, RF_NoFlags);
|
|
check(NewCameraMode);
|
|
|
|
CameraModeInstances.Add(NewCameraMode);
|
|
|
|
return NewCameraMode;
|
|
}
|
|
|
|
void ULyraCameraModeStack::UpdateStack(float DeltaTime)
|
|
{
|
|
const int32 StackSize = CameraModeStack.Num();
|
|
if (StackSize <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int32 RemoveCount = 0;
|
|
int32 RemoveIndex = INDEX_NONE;
|
|
|
|
for (int32 StackIndex = 0; StackIndex < StackSize; ++StackIndex)
|
|
{
|
|
ULyraCameraMode* CameraMode = CameraModeStack[StackIndex];
|
|
check(CameraMode);
|
|
|
|
CameraMode->UpdateCameraMode(DeltaTime);
|
|
|
|
if (CameraMode->GetBlendWeight() >= 1.0f)
|
|
{
|
|
// Everything below this mode is now irrelevant and can be removed.
|
|
RemoveIndex = (StackIndex + 1);
|
|
RemoveCount = (StackSize - RemoveIndex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (RemoveCount > 0)
|
|
{
|
|
// Let the camera modes know they being removed from the stack.
|
|
for (int32 StackIndex = RemoveIndex; StackIndex < StackSize; ++StackIndex)
|
|
{
|
|
ULyraCameraMode* CameraMode = CameraModeStack[StackIndex];
|
|
check(CameraMode);
|
|
|
|
CameraMode->OnDeactivation();
|
|
}
|
|
|
|
CameraModeStack.RemoveAt(RemoveIndex, RemoveCount);
|
|
}
|
|
}
|
|
|
|
void ULyraCameraModeStack::BlendStack(FLyraCameraModeView& OutCameraModeView) const
|
|
{
|
|
const int32 StackSize = CameraModeStack.Num();
|
|
if (StackSize <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Start at the bottom and blend up the stack
|
|
const ULyraCameraMode* CameraMode = CameraModeStack[StackSize - 1];
|
|
check(CameraMode);
|
|
|
|
OutCameraModeView = CameraMode->GetCameraModeView();
|
|
|
|
for (int32 StackIndex = (StackSize - 2); StackIndex >= 0; --StackIndex)
|
|
{
|
|
CameraMode = CameraModeStack[StackIndex];
|
|
check(CameraMode);
|
|
|
|
OutCameraModeView.Blend(CameraMode->GetCameraModeView(), CameraMode->GetBlendWeight());
|
|
}
|
|
}
|
|
|
|
void ULyraCameraModeStack::DrawDebug(UCanvas* Canvas) const
|
|
{
|
|
check(Canvas);
|
|
|
|
FDisplayDebugManager& DisplayDebugManager = Canvas->DisplayDebugManager;
|
|
|
|
DisplayDebugManager.SetDrawColor(FColor::Green);
|
|
DisplayDebugManager.DrawString(FString(TEXT(" --- Camera Modes (Begin) ---")));
|
|
|
|
for (const ULyraCameraMode* CameraMode : CameraModeStack)
|
|
{
|
|
check(CameraMode);
|
|
CameraMode->DrawDebug(Canvas);
|
|
}
|
|
|
|
DisplayDebugManager.SetDrawColor(FColor::Green);
|
|
DisplayDebugManager.DrawString(FString::Printf(TEXT(" --- Camera Modes (End) ---")));
|
|
}
|
|
|
|
void ULyraCameraModeStack::GetBlendInfo(float& OutWeightOfTopLayer, FGameplayTag& OutTagOfTopLayer) const
|
|
{
|
|
if (CameraModeStack.Num() == 0)
|
|
{
|
|
OutWeightOfTopLayer = 1.0f;
|
|
OutTagOfTopLayer = FGameplayTag();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ULyraCameraMode* TopEntry = CameraModeStack.Last();
|
|
check(TopEntry);
|
|
OutWeightOfTopLayer = TopEntry->GetBlendWeight();
|
|
OutTagOfTopLayer = TopEntry->GetCameraTypeTag();
|
|
}
|
|
}
|