RealtimeStyleTransferRuntime/Source/LyraGame/Cosmetics/LyraPawnComponent_Character...

357 lines
9.8 KiB
C++
Raw Permalink Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
2022-09-13 07:18:28 +00:00
#include "Cosmetics/LyraPawnComponent_CharacterParts.h"
2022-05-23 18:41:30 +00:00
#include "Components/ChildActorComponent.h"
2022-09-13 07:18:28 +00:00
#include "Components/SceneComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Cosmetics/LyraCharacterPartTypes.h"
#include "Engine/EngineBaseTypes.h"
#include "GameFramework/Actor.h"
#include "GameFramework/Character.h"
2022-05-23 18:41:30 +00:00
#include "GameplayTagAssetInterface.h"
2022-09-13 07:18:28 +00:00
#include "HAL/Platform.h"
#include "Math/Transform.h"
#include "Misc/AssertionMacros.h"
#include "Net/UnrealNetwork.h"
#include "Templates/Casts.h"
#include "Templates/SubclassOf.h"
#include "UObject/NameTypes.h"
#include "UObject/UObjectBaseUtility.h"
class FLifetimeProperty;
class UPhysicsAsset;
class USkeletalMesh;
class UWorld;
2022-05-23 18:41:30 +00:00
//////////////////////////////////////////////////////////////////////
FString FLyraAppliedCharacterPartEntry::GetDebugString() const
{
return FString::Printf(TEXT("(PartClass: %s, Socket: %s, Instance: %s)"), *GetPathNameSafe(Part.PartClass), *Part.SocketName.ToString(), *GetPathNameSafe(SpawnedComponent));
}
//////////////////////////////////////////////////////////////////////
void FLyraCharacterPartList::PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize)
{
bool bDestroyedAnyActors = false;
for (int32 Index : RemovedIndices)
{
FLyraAppliedCharacterPartEntry& Entry = Entries[Index];
bDestroyedAnyActors |= DestroyActorForEntry(Entry);
}
if (bDestroyedAnyActors)
{
OwnerComponent->BroadcastChanged();
}
}
void FLyraCharacterPartList::PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize)
{
bool bCreatedAnyActors = false;
for (int32 Index : AddedIndices)
{
FLyraAppliedCharacterPartEntry& Entry = Entries[Index];
bCreatedAnyActors |= SpawnActorForEntry(Entry);
}
if (bCreatedAnyActors)
{
OwnerComponent->BroadcastChanged();
}
}
void FLyraCharacterPartList::PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize)
{
bool bChangedAnyActors = false;
// We don't support dealing with propagating changes, just destroy and recreate
for (int32 Index : ChangedIndices)
{
FLyraAppliedCharacterPartEntry& Entry = Entries[Index];
bChangedAnyActors |= DestroyActorForEntry(Entry);
bChangedAnyActors |= SpawnActorForEntry(Entry);
}
if (bChangedAnyActors)
{
OwnerComponent->BroadcastChanged();
}
}
FLyraCharacterPartHandle FLyraCharacterPartList::AddEntry(FLyraCharacterPart NewPart)
{
FLyraCharacterPartHandle Result;
Result.PartHandle = PartHandleCounter++;
if (ensure(OwnerComponent && OwnerComponent->GetOwner() && OwnerComponent->GetOwner()->HasAuthority()))
{
FLyraAppliedCharacterPartEntry& NewEntry = Entries.AddDefaulted_GetRef();
NewEntry.Part = NewPart;
NewEntry.PartHandle = Result.PartHandle;
if (SpawnActorForEntry(NewEntry))
{
OwnerComponent->BroadcastChanged();
}
MarkItemDirty(NewEntry);
}
return Result;
}
void FLyraCharacterPartList::RemoveEntry(FLyraCharacterPartHandle Handle)
{
for (auto EntryIt = Entries.CreateIterator(); EntryIt; ++EntryIt)
{
FLyraAppliedCharacterPartEntry& Entry = *EntryIt;
if (Entry.PartHandle == Handle.PartHandle)
{
const bool bDestroyedActor = DestroyActorForEntry(Entry);
EntryIt.RemoveCurrent();
MarkArrayDirty();
if (bDestroyedActor)
{
OwnerComponent->BroadcastChanged();
}
break;
}
}
}
void FLyraCharacterPartList::ClearAllEntries(bool bBroadcastChangeDelegate)
{
bool bDestroyedAnyActors = false;
for (FLyraAppliedCharacterPartEntry& Entry : Entries)
{
bDestroyedAnyActors |= DestroyActorForEntry(Entry);
}
Entries.Reset();
MarkArrayDirty();
if (bDestroyedAnyActors && bBroadcastChangeDelegate)
{
OwnerComponent->BroadcastChanged();
}
}
FGameplayTagContainer FLyraCharacterPartList::CollectCombinedTags() const
{
FGameplayTagContainer Result;
for (const FLyraAppliedCharacterPartEntry& Entry : Entries)
{
if (Entry.SpawnedComponent != nullptr)
{
if (IGameplayTagAssetInterface* TagInterface = Cast<IGameplayTagAssetInterface>(Entry.SpawnedComponent->GetChildActor()))
{
TagInterface->GetOwnedGameplayTags(/*inout*/ Result);
}
}
}
return Result;
}
bool FLyraCharacterPartList::SpawnActorForEntry(FLyraAppliedCharacterPartEntry& Entry)
{
bool bCreatedAnyActors = false;
if (!OwnerComponent->IsNetMode(NM_DedicatedServer))
{
if (Entry.Part.PartClass != nullptr)
{
UWorld* World = OwnerComponent->GetWorld();
if (USceneComponent* ComponentToAttachTo = OwnerComponent->GetSceneComponentToAttachTo())
{
const FTransform SpawnTransform = ComponentToAttachTo->GetSocketTransform(Entry.Part.SocketName);
UChildActorComponent* PartComponent = NewObject<UChildActorComponent>(OwnerComponent->GetOwner());
PartComponent->SetupAttachment(ComponentToAttachTo, Entry.Part.SocketName);
PartComponent->SetChildActorClass(Entry.Part.PartClass);
PartComponent->RegisterComponent();
if (AActor* SpawnedActor = PartComponent->GetChildActor())
{
switch (Entry.Part.CollisionMode)
{
case ECharacterCustomizationCollisionMode::UseCollisionFromCharacterPart:
// Do nothing
break;
case ECharacterCustomizationCollisionMode::NoCollision:
SpawnedActor->SetActorEnableCollision(false);
break;
}
// Set up a direct tick dependency to work around the child actor component not providing one
if (USceneComponent* SpawnedRootComponent = SpawnedActor->GetRootComponent())
{
SpawnedRootComponent->AddTickPrerequisiteComponent(ComponentToAttachTo);
}
}
Entry.SpawnedComponent = PartComponent;
bCreatedAnyActors = true;
}
}
}
return bCreatedAnyActors;
}
bool FLyraCharacterPartList::DestroyActorForEntry(FLyraAppliedCharacterPartEntry& Entry)
{
bool bDestroyedAnyActors = false;
if (Entry.SpawnedComponent != nullptr)
{
Entry.SpawnedComponent->DestroyComponent();
Entry.SpawnedComponent = nullptr;
bDestroyedAnyActors = true;
}
return bDestroyedAnyActors;
}
//////////////////////////////////////////////////////////////////////
ULyraPawnComponent_CharacterParts::ULyraPawnComponent_CharacterParts(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, CharacterPartList(this)
{
SetIsReplicatedByDefault(true);
}
void ULyraPawnComponent_CharacterParts::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ThisClass, CharacterPartList);
}
FLyraCharacterPartHandle ULyraPawnComponent_CharacterParts::AddCharacterPart(const FLyraCharacterPart& NewPart)
{
return CharacterPartList.AddEntry(NewPart);
}
void ULyraPawnComponent_CharacterParts::RemoveCharacterPart(FLyraCharacterPartHandle Handle)
{
CharacterPartList.RemoveEntry(Handle);
}
void ULyraPawnComponent_CharacterParts::RemoveAllCharacterParts()
{
CharacterPartList.ClearAllEntries(/*bBroadcastChangeDelegate=*/ true);
}
void ULyraPawnComponent_CharacterParts::BeginPlay()
{
Super::BeginPlay();
}
void ULyraPawnComponent_CharacterParts::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
CharacterPartList.ClearAllEntries(/*bBroadcastChangeDelegate=*/ false);
Super::EndPlay(EndPlayReason);
}
TArray<AActor*> ULyraPawnComponent_CharacterParts::GetCharacterPartActors() const
{
TArray<AActor*> Result;
Result.Reserve(CharacterPartList.Entries.Num());
for (const FLyraAppliedCharacterPartEntry& Entry : CharacterPartList.Entries)
{
if (UChildActorComponent* PartComponent = Entry.SpawnedComponent)
{
if (AActor* SpawnedActor = PartComponent->GetChildActor())
{
Result.Add(SpawnedActor);
}
}
}
return Result;
}
USkeletalMeshComponent* ULyraPawnComponent_CharacterParts::GetParentMeshComponent() const
{
if (AActor* OwnerActor = GetOwner())
{
if (ACharacter* OwningCharacter = Cast<ACharacter>(OwnerActor))
{
if (USkeletalMeshComponent* MeshComponent = OwningCharacter->GetMesh())
{
return MeshComponent;
}
}
}
return nullptr;
}
USceneComponent* ULyraPawnComponent_CharacterParts::GetSceneComponentToAttachTo() const
{
if (USkeletalMeshComponent* MeshComponent = GetParentMeshComponent())
{
return MeshComponent;
}
else if (AActor* OwnerActor = GetOwner())
{
return OwnerActor->GetRootComponent();
}
else
{
return nullptr;
}
}
FGameplayTagContainer ULyraPawnComponent_CharacterParts::GetCombinedTags(FGameplayTag RequiredPrefix) const
{
FGameplayTagContainer Result = CharacterPartList.CollectCombinedTags();
if (RequiredPrefix.IsValid())
{
return Result.Filter(FGameplayTagContainer(RequiredPrefix));
}
else
{
return Result;
}
}
void ULyraPawnComponent_CharacterParts::BroadcastChanged()
{
const bool bReinitPose = true;
// Check to see if the body type has changed
if (USkeletalMeshComponent* MeshComponent = GetParentMeshComponent())
{
// Determine the mesh to use based on cosmetic part tags
const FGameplayTagContainer MergedTags = GetCombinedTags(FGameplayTag());
USkeletalMesh* DesiredMesh = BodyMeshes.SelectBestBodyStyle(MergedTags);
// Apply the desired mesh (this call is a no-op if the mesh hasn't changed)
MeshComponent->SetSkeletalMesh(DesiredMesh, /*bReinitPose=*/ bReinitPose);
// Apply the desired physics asset if there's a forced override independent of the one from the mesh
if (UPhysicsAsset* PhysicsAsset = BodyMeshes.ForcedPhysicsAsset)
{
MeshComponent->SetPhysicsAsset(PhysicsAsset, /*bForceReInit=*/ bReinitPose);
}
}
// Let observers know, e.g., if they need to apply team coloring or similar
OnCharacterPartsChanged.Broadcast(this);
}