Files
UnrealEngineUWP/Engine/Source/Runtime/PhysXCooking/Private/PhysXCooking.cpp
max whitehead f7bcb0bfa4 fix non-unity build.
#rb none


#ROBOMERGE-SOURCE: CL 11893247 via CL 11893251 via CL 11893254
#ROBOMERGE-BOT: (v656-11643781)

[CL 11893255 by max whitehead in Main branch]
2020-03-03 20:14:17 -05:00

463 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PhysXCooking.h"
#include "Core/Public/Modules/ModuleManager.h"
#if PHYSICS_INTERFACE_PHYSX
#include "Serialization/MemoryWriter.h"
#include "Modules/ModuleManager.h"
#include "IPhysXCookingModule.h"
#include "PhysicsPublicCore.h"
#include "PhysXSupportCore.h"
#include "PhysDerivedDataPublic.h" //only needed so that touching DDC will automatically re-link the cooking module
#include "PhysicsInitialization.h"
#include "Interface_CollisionDataProviderCore.h"
static FName NAME_PhysXGeneric(TEXT("PhysXGeneric"));
static FName NAME_PhysXPC(TEXT("PhysXPC"));
bool GetPhysXCooking(FName InFormatName, PxPlatform::Enum& OutFormat)
{
if ((InFormatName == NAME_PhysXPC) || (InFormatName == NAME_PhysXGeneric))
{
OutFormat = PxPlatform::ePC;
}
else
{
return false;
}
return true;
}
/**
* Validates PhysX format name.
*/
bool CheckPhysXCooking(FName InFormatName)
{
PxPlatform::Enum Format = PxPlatform::ePC;
return GetPhysXCooking(InFormatName, Format);
}
void UseBVH34IfSupported(FName Format, PxCookingParams& Params)
{
if((Format == NAME_PhysXPC))
{
//TODO: can turn this on once bug is fixed with character movement
//Params.midphaseDesc = PxMeshMidPhase::eBVH34;
}
}
FPhysXCooking::FPhysXCooking()
{
#if WITH_PHYSX
PxTolerancesScale PScale;
PScale.length = CVarToleranceScaleLength.GetValueOnAnyThread();
PScale.speed = CVarToleranceScaleSpeed.GetValueOnAnyThread();
PxCookingParams PCookingParams(PScale);
PCookingParams.meshWeldTolerance = 0.1f; // Weld to 1mm precision
PCookingParams.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eWELD_VERTICES);
// Force any cooking in PhysX or APEX to use older incremental hull method
// This is because the new 'quick hull' method can generate degenerate geometry in some cases (very thin meshes etc.)
//PCookingParams.convexMeshCookingType = PxConvexMeshCookingType::eINFLATION_INCREMENTAL_HULL;
PCookingParams.targetPlatform = PxPlatform::ePC;
PCookingParams.midphaseDesc = PxMeshMidPhase::eBVH33;
//PCookingParams.meshCookingHint = PxMeshCookingHint::eCOOKING_PERFORMANCE;
//PCookingParams.meshSizePerformanceTradeOff = 0.0f;
PhysXCooking = PxCreateCooking(PX_PHYSICS_VERSION, *GPhysXFoundation, PCookingParams);
#endif
}
physx::PxCooking* FPhysXCooking::GetCooking() const
{
return PhysXCooking;
}
bool FPhysXCooking::AllowParallelBuild() const
{
return true;
}
uint16 FPhysXCooking::GetVersion(FName Format) const
{
check(CheckPhysXCooking(Format));
return UE_PHYSX_PC_VER;
}
void FPhysXCooking::GetSupportedFormats(TArray<FName>& OutFormats) const
{
OutFormats.Add(NAME_PhysXPC);
OutFormats.Add(NAME_PhysXGeneric);
}
template <bool bUseBuffer>
EPhysXCookingResult FPhysXCooking::CookConvexImp(FName Format, EPhysXMeshCookFlags CookFlags, const TArray<FVector>& SrcBuffer, TArray<uint8>& OutBuffer, PxConvexMesh*& OutConvexMesh) const
{
EPhysXCookingResult CookResult = EPhysXCookingResult::Failed;
OutConvexMesh = nullptr;
#if WITH_PHYSX
PxPlatform::Enum PhysXFormat = PxPlatform::ePC;
bool bIsPhysXCookingValid = GetPhysXCooking(Format, PhysXFormat);
check(bIsPhysXCookingValid);
PxConvexMeshDesc PConvexMeshDesc;
PConvexMeshDesc.points.data = SrcBuffer.GetData();
PConvexMeshDesc.points.count = SrcBuffer.Num();
PConvexMeshDesc.points.stride = sizeof(FVector);
PConvexMeshDesc.flags = PxConvexFlag::eCOMPUTE_CONVEX | PxConvexFlag::eSHIFT_VERTICES;
// Set up cooking
const PxCookingParams CurrentParams = PhysXCooking->getParams();
PxCookingParams NewParams = CurrentParams;
NewParams.targetPlatform = PhysXFormat;
if (!!(CookFlags & EPhysXMeshCookFlags::SuppressFaceRemapTable))
{
NewParams.suppressTriangleMeshRemapTable = true;
}
if (!!(CookFlags & EPhysXMeshCookFlags::DeformableMesh))
{
// Meshes which can be deformed need different cooking parameters to inhibit vertex welding and add an extra skin around the collision mesh for safety.
// We need to set the meshWeldTolerance to zero, even when disabling 'clean mesh' as PhysX will attempt to perform mesh cleaning anyway according to this meshWeldTolerance
// if the convex hull is not well formed.
// Set the skin thickness as a proportion of the overall size of the mesh as PhysX's internal tolerances also use the overall size to calculate the epsilon used.
const FBox Bounds(SrcBuffer);
const float MaxExtent = (Bounds.Max - Bounds.Min).Size();
NewParams.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH);
NewParams.meshWeldTolerance = 0.0f;
}
else
{
//For meshes that don't deform we can try to use BVH34
UseBVH34IfSupported(Format, NewParams);
}
// Do we want to do a 'fast' cook on this mesh, may slow down collision performance at runtime
if (!!(CookFlags & EPhysXMeshCookFlags::FastCook))
{
NewParams.meshCookingHint = PxMeshCookingHint::eCOOKING_PERFORMANCE;
}
PhysXCooking->setParams(NewParams);
if (bUseBuffer)
{
// Cook the convex mesh to a temp buffer
TArray<uint8> CookedMeshBuffer;
FPhysXOutputStream Buffer(&CookedMeshBuffer);
if (PhysXCooking->cookConvexMesh(PConvexMeshDesc, Buffer))
{
CookResult = EPhysXCookingResult::Succeeded;
}
else
{
if (!(PConvexMeshDesc.flags & PxConvexFlag::eINFLATE_CONVEX))
{
// We failed to cook without inflating convex. Let's try again with inflation
//This is not ideal since it makes the collision less accurate. It's needed if given verts are extremely close.
PConvexMeshDesc.flags |= PxConvexFlag::eINFLATE_CONVEX;
if (PhysXCooking->cookConvexMesh(PConvexMeshDesc, Buffer))
{
CookResult = EPhysXCookingResult::SucceededWithInflation;
}
}
}
if (CookedMeshBuffer.Num() == 0)
{
CookResult = EPhysXCookingResult::Failed;
}
if (CookResult != EPhysXCookingResult::Failed)
{
// Append the cooked data into cooked buffer
OutBuffer.Append(CookedMeshBuffer);
}
}
else
{
OutConvexMesh = PhysXCooking->createConvexMesh(PConvexMeshDesc, GPhysXSDK->getPhysicsInsertionCallback()); //NOTE: getPhysicsInsertionCallback probably not thread safe!
if (OutConvexMesh)
{
CookResult = EPhysXCookingResult::Succeeded;
}
else
{
if (!(PConvexMeshDesc.flags & PxConvexFlag::eINFLATE_CONVEX))
{
// We failed to cook without inflating convex. Let's try again with inflation
//This is not ideal since it makes the collision less accurate. It's needed if given verts are extremely close.
PConvexMeshDesc.flags |= PxConvexFlag::eINFLATE_CONVEX;
OutConvexMesh = PhysXCooking->createConvexMesh(PConvexMeshDesc, GPhysXSDK->getPhysicsInsertionCallback()); //NOTE: getPhysicsInsertionCallback probably not thread safe!
if (OutConvexMesh)
{
CookResult = EPhysXCookingResult::SucceededWithInflation;
}
}
}
if (!OutConvexMesh)
{
CookResult = EPhysXCookingResult::Failed;
}
}
// Return default cooking params to normal
PhysXCooking->setParams(CurrentParams);
#endif // WITH_PHYSX
return CookResult;
}
EPhysXCookingResult FPhysXCooking::CookConvex(FName Format, EPhysXMeshCookFlags CookFlags, const TArray<FVector>& SrcBuffer, TArray<uint8>& OutBuffer) const
{
PxConvexMesh* JunkConvexMesh;
return CookConvexImp</*bUseBuffer=*/true>(Format, CookFlags, SrcBuffer, OutBuffer, JunkConvexMesh);
}
EPhysXCookingResult FPhysXCooking::CreateConvex(FName Format, EPhysXMeshCookFlags CookFlags, const TArray<FVector>& SrcBuffer, physx::PxConvexMesh*& OutConvexMesh) const
{
TArray<uint8> JunkBuffer;
return CookConvexImp</*bUseBuffer=*/false>(Format, CookFlags, SrcBuffer, JunkBuffer, OutConvexMesh);
}
template<bool bUseBuffer>
bool FPhysXCooking::CookTriMeshImp(FName Format, EPhysXMeshCookFlags CookFlags, const TArray<FVector>& SrcVertices, const TArray<FTriIndices>& SrcIndices, const TArray<uint16>& SrcMaterialIndices, const bool FlipNormals, TArray<uint8>& OutBuffer, PxTriangleMesh*& OutTriangleMesh) const
{
OutTriangleMesh = nullptr;
#if WITH_PHYSX
PxPlatform::Enum PhysXFormat = PxPlatform::ePC;
bool bIsPhysXCookingValid = GetPhysXCooking(Format, PhysXFormat);
check(bIsPhysXCookingValid);
PxTriangleMeshDesc PTriMeshDesc;
PTriMeshDesc.points.data = SrcVertices.GetData();
PTriMeshDesc.points.count = SrcVertices.Num();
PTriMeshDesc.points.stride = sizeof(FVector);
PTriMeshDesc.triangles.data = SrcIndices.GetData();
PTriMeshDesc.triangles.count = SrcIndices.Num();
PTriMeshDesc.triangles.stride = sizeof(FTriIndices);
PTriMeshDesc.materialIndices.data = SrcMaterialIndices.GetData();
PTriMeshDesc.materialIndices.stride = sizeof(PxMaterialTableIndex);
PTriMeshDesc.flags = FlipNormals ? PxMeshFlag::eFLIPNORMALS : static_cast<PxMeshFlag::Enum>(0);
// Set up cooking
const PxCookingParams CurrentParams = PhysXCooking->getParams();
PxCookingParams NewParams = CurrentParams;
NewParams.targetPlatform = PhysXFormat;
if (!!(CookFlags & EPhysXMeshCookFlags::SuppressFaceRemapTable))
{
NewParams.suppressTriangleMeshRemapTable = true;
}
if (!!(CookFlags & EPhysXMeshCookFlags::DisableActiveEdgePrecompute))
{
// Disable ActiveEdgePrecompute (This makes cooking faster, but will slow contact generation)
NewParams.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE;
}
if (!!(CookFlags & EPhysXMeshCookFlags::DeformableMesh))
{
// In the case of a deformable mesh, we have to change the cook params
NewParams.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH;
NewParams.meshPreprocessParams &= ~(PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eWELD_VERTICES));
// The default BVH34 midphase does not support refit
NewParams.midphaseDesc = PxMeshMidPhase::eBVH33;
}
else
{
//For non deformable meshes we can try to use BVH34
UseBVH34IfSupported(Format, NewParams);
}
PhysXCooking->setParams(NewParams);
bool bResult = false;
// Cook TriMesh Data
if (bUseBuffer)
{
FPhysXOutputStream Buffer(&OutBuffer);
bResult = PhysXCooking->cookTriangleMesh(PTriMeshDesc, Buffer);
}
else
{
FPhysXOutputStream Buffer(&OutBuffer);
OutTriangleMesh = PhysXCooking->createTriangleMesh(PTriMeshDesc, GPhysXSDK->getPhysicsInsertionCallback()); //NOTE: getPhysicsInsertionCallback probably not thread safe!
bResult = !!OutTriangleMesh;
}
// Restore cooking params
PhysXCooking->setParams(CurrentParams);
return bResult;
#else
return false;
#endif // WITH_PHYSX
}
bool FPhysXCooking::CookTriMesh(FName Format, EPhysXMeshCookFlags CookFlags, const TArray<FVector>& SrcVertices, const TArray<FTriIndices>& SrcIndices, const TArray<uint16>& SrcMaterialIndices, const bool FlipNormals, TArray<uint8>& OutBuffer) const
{
PxTriangleMesh* JunkTriangleMesh = nullptr;
return CookTriMeshImp</*bUseBuffer=*/true>(Format, CookFlags, SrcVertices, SrcIndices, SrcMaterialIndices, FlipNormals, OutBuffer, JunkTriangleMesh);
}
bool FPhysXCooking::CreateTriMesh(FName Format, EPhysXMeshCookFlags CookFlags, const TArray<FVector>& SrcVertices, const TArray<FTriIndices>& SrcIndices, const TArray<uint16>& SrcMaterialIndices, const bool FlipNormals, physx::PxTriangleMesh*& OutTriangleMesh) const
{
TArray<uint8> JunkBuffer;
return CookTriMeshImp</*bUseBuffer=*/false>(Format, CookFlags, SrcVertices, SrcIndices, SrcMaterialIndices, FlipNormals, JunkBuffer, OutTriangleMesh);
}
template <bool bUseBuffer>
bool FPhysXCooking::CookHeightFieldImp(FName Format, FIntPoint HFSize, const void* Samples, uint32 SamplesStride, TArray<uint8>& OutBuffer, PxHeightField*& OutHeightField) const
{
OutHeightField = nullptr;
#if WITH_PHYSX
PxPlatform::Enum PhysXFormat = PxPlatform::ePC;
bool bIsPhysXCookingValid = GetPhysXCooking(Format, PhysXFormat);
check(bIsPhysXCookingValid);
PxHeightFieldDesc HFDesc;
HFDesc.format = PxHeightFieldFormat::eS16_TM;
HFDesc.nbColumns = HFSize.X;
HFDesc.nbRows = HFSize.Y;
HFDesc.samples.data = Samples;
HFDesc.samples.stride = SamplesStride;
HFDesc.flags = PxHeightFieldFlag::eNO_BOUNDARY_EDGES;
// Set up cooking
const PxCookingParams& Params = PhysXCooking->getParams();
PxCookingParams NewParams = Params;
NewParams.targetPlatform = PhysXFormat;
UseBVH34IfSupported(Format, NewParams);
PhysXCooking->setParams(NewParams);
// Cook to a temp buffer
TArray<uint8> CookedBuffer;
FPhysXOutputStream Buffer(&CookedBuffer);
if (bUseBuffer)
{
if (PhysXCooking->cookHeightField(HFDesc, Buffer) && CookedBuffer.Num() > 0)
{
// Append the cooked data into cooked buffer
OutBuffer.Append(CookedBuffer);
return true;
}
}
else
{
OutHeightField = PhysXCooking->createHeightField(HFDesc, GPhysXSDK->getPhysicsInsertionCallback()); //NOTE: getPhysicsInsertionCallback probably not thread safe!
if (OutHeightField)
{
return true;
}
}
return false;
#else
return false;
#endif // WITH_PHYSX
}
bool FPhysXCooking::CookHeightField(FName Format, FIntPoint HFSize, const void* Samples, uint32 SamplesStride, TArray<uint8>& OutBuffer) const
{
physx::PxHeightField* JunkHeightField = nullptr;
return CookHeightFieldImp</*bUseBuffer=*/true>(Format, HFSize, Samples, SamplesStride, OutBuffer, JunkHeightField);
}
bool FPhysXCooking::CreateHeightField(FName Format, FIntPoint HFSize, const void* Samples, uint32 SamplesStride, physx::PxHeightField*& OutHeightField) const
{
TArray<uint8> JunkBuffer;
return CookHeightFieldImp</*bUseBuffer=*/false>(Format, HFSize, Samples, SamplesStride, JunkBuffer, OutHeightField);
}
FPhysXPlatformModule::FPhysXPlatformModule()
{
PhysXCookerTLS = FPlatformTLS::AllocTlsSlot();
}
FPhysXPlatformModule::~FPhysXPlatformModule()
{
//NOTE: we don't bother cleaning up TLS, this is only closed during real shutdown
}
IPhysXCooking* FPhysXPlatformModule::GetPhysXCooking()
{
FPhysXCooking* PhysXCooking = (FPhysXCooking*)FPlatformTLS::GetTlsValue(PhysXCookerTLS);
if (!PhysXCooking)
{
InitPhysXCooking();
PhysXCooking = new FPhysXCooking();
FPlatformTLS::SetTlsValue(PhysXCookerTLS, PhysXCooking);
}
return PhysXCooking;
}
physx::PxCooking* FPhysXPlatformModule::CreatePhysXCooker(uint32 version, physx::PxFoundation& foundation, const physx::PxCookingParams& params)
{
#if WITH_PHYSX
return PxCreateCooking(PX_PHYSICS_VERSION, foundation, params);
#endif
return nullptr;
}
void FPhysXPlatformModule::Terminate()
{
ShutdownPhysXCooking();
}
/**
* Load the required modules for PhysX
*/
void FPhysXPlatformModule::InitPhysXCooking()
{
if (IsInGameThread())
{
// Make sure PhysX libs are loaded
PhysDLLHelper::LoadPhysXModules(/*bLoadCookingModule=*/true);
}
}
void FPhysXPlatformModule::ShutdownPhysXCooking()
{
#if WITH_PHYSX
if(IPhysXCooking* PhysXCookingWrapper = FPhysXPlatformModule::GetPhysXCooking())
{
if (PxCooking* PhysXCooking = PhysXCookingWrapper->GetCooking())
{
PhysXCooking->release();
PhysXCooking = nullptr;
delete PhysXCookingWrapper;
FPlatformTLS::SetTlsValue(PhysXCookerTLS, nullptr);
}
}
#endif
}
IMPLEMENT_MODULE(FPhysXPlatformModule, PhysXCooking);
#else
class FPhysXPlatformModule : public IModuleInterface
{
};
IMPLEMENT_MODULE(FPhysXPlatformModule, PhysXCooking);
#endif