Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

331 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ChaosOutfitAsset/OutfitAsset.h"
#include "ChaosClothAsset/ClothSimulationModel.h"
#include "ChaosClothAsset/CollectionClothFacade.h"
#include "ChaosOutfitAsset/CollectionOutfitFacade.h"
#include "ChaosOutfitAsset/OutfitAssetPrivate.h"
#include "Dataflow/DataflowContextAssetStore.h"
#include "Engine/SkeletalMesh.h"
#if WITH_EDITOR
#include "DerivedDataCacheInterface.h"
#endif
#include "Rendering/SkeletalMeshRenderData.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OutfitAsset)
// If Chaos outfit asset derived data needs to be rebuilt (new format, serialization differences, etc.) replace the version GUID below with a new one.
// In case of merge conflicts with DDC versions, you MUST generate a new GUID and set this new GUID as the version.
#define UE_CHAOS_OUTFIT_ASSET_DERIVED_DATA_VERSION TEXT("DD1C25C90FDE4287881A8759CD3646A6")
UChaosOutfitAsset::UChaosOutfitAsset(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
DataflowInstance.SetDataflowTerminal(TEXT("OutfitAssetTerminal"));
PRAGMA_ENABLE_DEPRECATION_WARNINGS
// Init an empty asset with a root bone and empty but initialized render data
Build(nullptr);
}
UChaosOutfitAsset::UChaosOutfitAsset(FVTableHelper& Helper)
: Super(Helper)
{
}
UChaosOutfitAsset::~UChaosOutfitAsset() = default;
bool UChaosOutfitAsset::HasValidClothSimulationModels() const
{
for (const FChaosOutfitPiece& Piece : Pieces)
{
if (Piece.ClothSimulationModel->GetNumLods())
{
return true;
}
}
return false;
}
void UChaosOutfitAsset::Build(const TObjectPtr<const UChaosOutfit> InOutfit, UE::Dataflow::IContextAssetStoreInterface* ContextAssetStore)
{
using namespace UE::Chaos::OutfitAsset;
// Unregister dependent components, the context will reregister them at the end of the scope
const FMultiComponentReregisterContext MultiComponentReregisterContext(GetDependentComponents());
// Stop the rendering
ReleaseResources();
// Copy the outfit to this asset
TUniquePtr<FSkeletalMeshRenderData> TempSkeletalMeshRenderData = MakeUnique<FSkeletalMeshRenderData>();
FReferenceSkeleton TempReferenceSkeleton;
if (InOutfit)
{
InOutfit->CopyTo(
Pieces,
TempReferenceSkeleton,
TempSkeletalMeshRenderData,
Materials,
OutfitCollection);
#if WITH_EDITORONLY_DATA
if (Outfit != InOutfit)
{
const FName UniqueOutfitName = MakeUniqueObjectName(this, UChaosOutfit::StaticClass());
Outfit = DuplicateObject<UChaosOutfit>(InOutfit, this, UniqueOutfitName);
}
#endif
if (ContextAssetStore)
{
// Fix up resized material paths
TMap<FSoftObjectPath, FSoftObjectPath> MaterialPathsToFixUp;
MaterialPathsToFixUp.Reserve(Materials.Num());
for (int32 MaterialIndex = 0; MaterialIndex < Materials.Num(); ++MaterialIndex)
{
FSkeletalMaterial& Material = Materials[MaterialIndex];
if (Material.MaterialInterface && Material.MaterialInterface->GetOuter() == GetTransientPackage())
{
const FString TransientPathName = Material.MaterialInterface->GetPathName();
Material.MaterialInterface = Cast<UMaterialInterface>(ContextAssetStore->CommitAsset(TransientPathName));
#if WITH_EDITORONLY_DATA
if (Outfit)
{
Outfit->GetMaterials()[MaterialIndex].MaterialInterface = Material.MaterialInterface;
}
#endif
MaterialPathsToFixUp.Emplace(TransientPathName, Material.MaterialInterface ? Material.MaterialInterface->GetPathName() : FString());
}
}
// Fix up cloth collections
if (MaterialPathsToFixUp.Num())
{
auto FixUpPiecesMaterials = [&MaterialPathsToFixUp](TArrayView<FChaosOutfitPiece> PiecesToFixUp)
{
using namespace UE::Chaos::ClothAsset;
for (FChaosOutfitPiece& Piece : PiecesToFixUp)
{
for (TSharedRef<const FManagedArrayCollection>& Collection : Piece.Collections)
{
TOptional<TSharedRef<FManagedArrayCollection>> FixedUpCollection;
TOptional<FCollectionClothFacade> FixedUpClothFacade;
FCollectionClothConstFacade ClothFacade(Collection);
const TConstArrayView<FSoftObjectPath> RenderMaterialPathNames = ClothFacade.GetRenderMaterialSoftObjectPathName();
for (int32 PathIndex = 0; PathIndex < RenderMaterialPathNames.Num(); ++PathIndex)
{
const FSoftObjectPath& RenderMaterialPathName = RenderMaterialPathNames[PathIndex];
if (const FSoftObjectPath* MaterialPathToFixUp = MaterialPathsToFixUp.Find(RenderMaterialPathName))
{
if (!FixedUpCollection.IsSet())
{
FixedUpCollection.Emplace(MakeShared<FManagedArrayCollection>(*Collection));
FixedUpClothFacade.Emplace(FixedUpCollection.GetValue());
}
TArrayView<FSoftObjectPath> FixedUpRenderMaterialPathNames = FixedUpClothFacade->GetRenderMaterialSoftObjectPathName();
check(FixedUpRenderMaterialPathNames[PathIndex] == RenderMaterialPathName);
FixedUpRenderMaterialPathNames[PathIndex] = *MaterialPathToFixUp;
}
}
if (FixedUpCollection.IsSet())
{
Collection = MoveTemp(FixedUpCollection.GetValue());
}
}
}
};
// Fix up this outfit's pieces
FixUpPiecesMaterials(Pieces);
#if WITH_EDITORONLY_DATA
// Fix up this outfit construction's pieces
if (Outfit)
{
FixUpPiecesMaterials(Outfit->GetPieces());
}
#endif
}
}
}
else
{
UChaosOutfit::Init(
Pieces,
TempReferenceSkeleton,
TempSkeletalMeshRenderData,
Materials,
OutfitCollection);
#if WITH_EDITORONLY_DATA
Outfit = nullptr;
#endif
}
// Populate the body sizes
const FCollectionOutfitConstFacade OutfitFacade(OutfitCollection);
const TArray<FSoftObjectPath> OutfitBodyPartsSkeletalMeshes = OutfitFacade.GetOutfitBodyPartsSkeletalMeshPaths();
Bodies.Reset(OutfitBodyPartsSkeletalMeshes.Num());
for (const FSoftObjectPath& OutfitBodyPartsSkeletalMesh : OutfitBodyPartsSkeletalMeshes)
{
if (USkeletalMesh* const Body = Cast<USkeletalMesh>(OutfitBodyPartsSkeletalMesh.TryLoad()))
{
Bodies.Emplace(Body);
}
}
// Set the new reference skeleton
SetReferenceSkeleton(&TempReferenceSkeleton);
CalculateInvRefMatrices();
// Create the render data
if (!TempSkeletalMeshRenderData->LODRenderData.Num())
{
TempSkeletalMeshRenderData->LODRenderData.Add(new FSkeletalMeshLODRenderData()); // Always needs at least one LOD when rendering
TempSkeletalMeshRenderData->LODRenderData[0].StaticVertexBuffers.PositionVertexBuffer.Init(0); // Required for serialization
TempSkeletalMeshRenderData->LODRenderData[0].StaticVertexBuffers.StaticMeshVertexBuffer.Init(0, 0); // Required for serialization
}
ReleaseResourcesFence.Wait(); // Make sure the release resources fence has completed before deleting/replacing the render data
SetResourceForRendering(MoveTemp(TempSkeletalMeshRenderData));
CalculateBounds();
const int32 NumLODs = GetResourceForRendering()->LODRenderData.Num(); // The render data will always look for at least one default LOD 0
LODInfo.Reset(NumLODs);
LODInfo.AddDefaulted(NumLODs);
if (FApp::CanEverRender())
{
InitResources();
}
// Update any components using this asset
constexpr bool bReregisterComponents = false; // Do not reregister twice, this is already done at the function scope
OnAssetChanged(bReregisterComponents);
}
void UChaosOutfitAsset::Serialize(FArchive& Ar)
{
LLM_SCOPE_BYNAME(TEXT("Physics/Cloth"));
Super::Serialize(Ar);
#if WITH_EDITORONLY_DATA
if (Outfit)
#endif
{
bool bCooked = Ar.IsCooking();
Ar << bCooked;
if (bCooked && !IsTemplate() && !Ar.IsCountingMemory()) // Counting of these resources are done in GetResourceSizeEx, so skip these when counting memory
{
LLM_SCOPE_BYNAME(TEXT("Physics/ClothRendering"));
if (Ar.IsLoading())
{
SetResourceForRendering(MakeUnique<FSkeletalMeshRenderData>());
}
GetResourceForRendering()->Serialize(Ar, this);
}
}
if (Ar.IsLoading())
{
UE::Chaos::OutfitAsset::FCollectionOutfitFacade OutfitFacade(OutfitCollection);
OutfitFacade.PostSerialize(Ar);
}
}
void UChaosOutfitAsset::PostLoad()
{
LLM_SCOPE_BYNAME(TEXT("Physics/Cloth"));
Super::PostLoad();
#if WITH_EDITORONLY_DATA
if (Outfit)
{
// Rebuild the outfit
Build(Outfit);
}
else
{
// Re-evaluate the Dataflow (legacy PostLoad behavior from before the Outfit object was saved)
GetDataflowInstance().UpdateOwnerAsset();
if (UPackage* const Package = GetOutermost())
{
Package->SetDirtyFlag(true);
}
UE_LOG(LogChaosOutfitAsset, Warning, TEXT("Outfit Asset [%s] needs to be re-saved."), *GetName());
}
#endif
if (FApp::CanEverRender())
{
InitResources();
}
else
{
UpdateUVChannelData(false); // Update any missing data when cooking
}
CalculateInvRefMatrices();
CalculateBounds();
}
FName UChaosOutfitAsset::GetClothSimulationModelName(int32 ModelIndex) const
{
return Pieces[ModelIndex].Name;
}
TSharedPtr<const FChaosClothSimulationModel> UChaosOutfitAsset::GetClothSimulationModel(int32 ModelIndex) const
{
return Pieces[ModelIndex].ClothSimulationModel;
}
const TArray<TSharedRef<const FManagedArrayCollection>>& UChaosOutfitAsset::GetCollections(int32 ModelIndex) const
{
return Pieces[ModelIndex].Collections;
}
const UPhysicsAsset* UChaosOutfitAsset::GetPhysicsAssetForModel(int32 ModelIndex) const
{
return Pieces[ModelIndex].PhysicsAsset;
}
FGuid UChaosOutfitAsset::GetAssetGuid(int32 ModelIndex) const
{
return Pieces[ModelIndex].AssetGuid;
}
#if WITH_EDITOR
FString UChaosOutfitAsset::BuildDerivedDataKey(const ITargetPlatform* TargetPlatform)
{
const FString KeySuffix;
return FDerivedDataCacheInterface::BuildCacheKey(
TEXT("CHAOSOUTFIT"),
UE_CHAOS_OUTFIT_ASSET_DERIVED_DATA_VERSION,
*KeySuffix
);
}
#endif // #if WITH_EDITOR
void UChaosOutfitAsset::CalculateBounds()
{
FBox BoundingBox(ForceInit);
for (const FSkeletalMeshLODRenderData& LODRenderDatum : GetResourceForRendering()->LODRenderData)
{
const FPositionVertexBuffer& PositionVertexBuffer = LODRenderDatum.StaticVertexBuffers.PositionVertexBuffer;
for (uint32 VertexIndex = 0; VertexIndex < PositionVertexBuffer.GetNumVertices(); ++VertexIndex)
{
const FVector3f& VertexPosition = PositionVertexBuffer.VertexPosition(VertexIndex);
BoundingBox += (FVector)VertexPosition;
}
}
Bounds = FBoxSphereBounds(BoundingBox);
}