Files
UnrealEngine/Engine/Source/Runtime/PhysicsCore/Private/PhysTestSerializer.cpp
2025-05-18 13:04:45 +08:00

252 lines
7.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
// Physics engine integration utilities
#include "PhysTestSerializer.h"
#include "PhysicsCore.h"
#include "Chaos/PBDRigidsEvolution.h"
#include "Chaos/PBDRigidParticles.h"
#include "Chaos/Box.h"
#include "Chaos/Sphere.h"
#include "Chaos/Capsule.h"
#include "PhysicsPublicCore.h"
#include "PhysicsCore.h"
#include "HAL/FileManager.h"
#include "Misc/Paths.h"
#include "Serialization/MemoryWriter.h"
FPhysTestSerializer::FPhysTestSerializer()
: bDiskDataIsChaos(false)
, bChaosDataReady(false)
, Particles(UniqueIndices)
{
}
void FPhysTestSerializer::Serialize(const TCHAR* FilePrefix)
{
check(IsInGameThread());
int32 Tries = 0;
FString UseFileName;
const FString FullPathPrefix = FPaths::ProfilingDir() / FilePrefix;
do
{
UseFileName = FString::Printf(TEXT("%s_%d.bin"), *FullPathPrefix, Tries++);
} while (IFileManager::Get().FileExists(*UseFileName));
//this is not actually file safe but oh well, very unlikely someone else is trying to create this file at the same time
TUniquePtr<FArchive> File(IFileManager::Get().CreateFileWriter(*UseFileName));
if (File)
{
Chaos::FChaosArchive Ar(*File);
UE_LOG(LogPhysicsCore, Log, TEXT("PhysTestSerialize File: %s"), *UseFileName);
Serialize(Ar);
}
else
{
UE_LOG(LogPhysicsCore, Warning, TEXT("Could not create PhysTestSerialize file(%s)"), *UseFileName);
}
}
void FPhysTestSerializer::Serialize(Chaos::FChaosArchive& Ar)
{
if (!Ar.IsLoading())
{
//make sure any context we had set is restored before writing out sqcapture
Ar.SetContext(MoveTemp(ChaosContext));
}
static const FName TestSerializerName = TEXT("PhysTestSerializer");
{
Chaos::FChaosArchiveScopedMemory ScopedMemory(Ar, TestSerializerName, false);
int Version = 1;
Ar << Version;
Ar << bDiskDataIsChaos;
if (Version >= 1)
{
//use version recorded
ArchiveVersion.Serialize(Ar);
}
else
{
//no version recorded so use the latest versions in GUIDs we rely on before making serialization version change
ArchiveVersion.SetVersion(FPhysicsObjectVersion::GUID, FPhysicsObjectVersion::SerializeGTGeometryParticles, TEXT("SerializeGTGeometryParticles"));
ArchiveVersion.SetVersion(FDestructionObjectVersion::GUID, FDestructionObjectVersion::GroupAndAttributeNameRemapping, TEXT("GroupAndAttributeNameRemapping"));
ArchiveVersion.SetVersion(FExternalPhysicsCustomObjectVersion::GUID, FExternalPhysicsCustomObjectVersion::BeforeCustomVersionWasAdded, TEXT("BeforeCustomVersionWasAdded"));
}
Ar.SetCustomVersions(ArchiveVersion);
Ar << Data;
}
if (Ar.IsLoading())
{
#if 0
CreateChaosData();
#endif
Ar.SetContext(MoveTemp(ChaosContext)); //make sure any context we created during load is used for sqcapture
}
bool bHasSQCapture = !!SQCapture;
{
Chaos::FChaosArchiveScopedMemory ScopedMemory(Ar, TestSerializerName, false);
Ar << bHasSQCapture;
}
if(bHasSQCapture)
{
if (Ar.IsLoading())
{
SQCapture = TUniquePtr<FSQCapture>(new FSQCapture(*this));
}
SQCapture->Serialize(Ar);
}
ChaosContext = Ar.StealContext();
}
void FPhysTestSerializer::SetPhysicsData(Chaos::FPBDRigidsEvolution& Evolution)
{
bDiskDataIsChaos = true;
Data.Empty();
FMemoryWriter Ar(Data);
Chaos::FChaosArchive ChaosAr(Ar);
Evolution.Serialize(ChaosAr);
ChaosContext = ChaosAr.StealContext();
ArchiveVersion = Ar.GetCustomVersions();
}
#if 0
void FPhysTestSerializer::CreateChaosData()
{
if (bDiskDataIsChaos == false)
{
if (bChaosDataReady)
{
return;
}
PxScene* Scene = GetPhysXData();
check(Scene);
const uint32 NumStatic = Scene->getNbActors(PxActorTypeFlag::eRIGID_STATIC);
const uint32 NumDynamic = Scene->getNbActors(PxActorTypeFlag::eRIGID_DYNAMIC);
const uint32 NumActors = NumStatic + NumDynamic;
TArray<PxActor*> Actors;
Actors.AddUninitialized(NumActors);
if (NumStatic)
{
Scene->getActors(PxActorTypeFlag::eRIGID_STATIC, Actors.GetData(), NumStatic);
auto NewParticles = Particles.CreateStaticParticles(NumStatic); //question: do we want to distinguish query only and sim only actors?
for (uint32 Idx = 0; Idx < NumStatic; ++Idx)
{
GTParticles.Emplace(FGeometryParticle::CreateParticle());
NewParticles[Idx]->GTGeometryParticle() = GTParticles.Last().Get();
}
}
if (NumDynamic)
{
Scene->getActors(PxActorTypeFlag::eRIGID_DYNAMIC, &Actors[NumStatic], NumDynamic);
auto NewParticles = Particles.CreateDynamicParticles(NumDynamic); //question: do we want to distinguish query only and sim only actors?
for (uint32 Idx = 0; Idx < NumDynamic; ++Idx)
{
GTParticles.Emplace(FPBDRigidParticle::CreateParticle());
NewParticles[Idx]->GTGeometryParticle() = GTParticles.Last().Get();
}
}
auto& Handles = Particles.GetParticleHandles();
int32 Idx = 0;
for (PxActor* Act : Actors)
{
//transform
PxRigidActor* Actor = static_cast<PxRigidActor*>(Act);
auto& Particle = Handles.Handle(Idx);
auto& GTParticle = Particle->GTGeometryParticle();
Particle->X() = P2UVector(Actor->getGlobalPose().p);
Particle->R() = P2UQuat(Actor->getGlobalPose().q);
Particle->GTGeometryParticle()->SetX(Particle->X());
Particle->GTGeometryParticle()->SetR(Particle->R());
auto PBDRigid = Particle->CastToRigidParticle();
if(PBDRigid && PBDRigid->ObjectState() == EObjectStateType::Dynamic)
{
PBDRigid->P() = Particle->X();
PBDRigid->Q() = Particle->R();
}
PxActorToChaosHandle.Add(Act, Particle.Get());
//geometry
TArray<Chaos::FImplicitObjectPtr> Geoms;
const int32 NumShapes = Actor->getNbShapes();
TArray<PxShape*> Shapes;
Shapes.AddUninitialized(NumShapes);
Actor->getShapes(Shapes.GetData(), NumShapes);
for (PxShape* Shape : Shapes)
{
if (TUniquePtr<TImplicitObjectTransformed<float, 3>> Geom = PxShapeToChaosGeom(Shape))
{
Geoms.Add(MoveTemp(Geom));
}
}
if (Geoms.Num())
{
if (Geoms.Num() == 1)
{
auto SharedGeom = FImplicitObjectPtr(Geoms[0]);
GTParticle->SetGeometry(SharedGeom);
Particle->SetSharedGeometry(SharedGeom);
}
else
{
GTParticle->SetGeometry(MakeUnique<FImplicitObjectUnion>(MoveTemp(Geoms)));
Particle->SetGeometry(GTParticle->Geometry());
}
// Fixup bounds
auto Geom = GTParticle->Geometry();
if (Geom->HasBoundingBox())
{
auto& ShapeArray = GTParticle->ShapesArray();
for (auto& Shape : ShapeArray)
{
Shape->SetWorldSpaceInflatedShapeBounds(Geom->BoundingBox().TransformedAABB(TRigidTransform<FReal, 3>(Particle->X(), Particle->R())));
}
}
}
int32 ShapeIdx = 0;
for (PxShape* Shape : Shapes)
{
PxShapeToChaosShapes.Add(Shape, GTParticle->ShapesArray()[ShapeIdx++].Get());
}
++Idx;
}
ChaosEvolution = MakeUnique<Chaos::FPBDRigidsEvolution>(Particles, PhysicalMaterials);
}
else
{
ChaosEvolution = MakeUnique<Chaos::FPBDRigidsEvolution>(Particles, PhysicalMaterials);
FMemoryReader Ar(Data);
Chaos::FChaosArchive ChaosAr(Ar);
Ar.SetCustomVersions(ArchiveVersion);
ChaosEvolution->Serialize(ChaosAr);
ChaosContext = ChaosAr.StealContext();
}
bChaosDataReady = true;
}
#endif