Files
UnrealEngine/Engine/Plugins/Experimental/Water/Source/Runtime/Private/OceanCollisionComponent.cpp
2025-05-18 13:04:45 +08:00

234 lines
8.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "OceanCollisionComponent.h"
#include "BodySetupEnums.h"
#include "SceneView.h"
#include "WaterBodyActor.h"
#include "Chaos/CollisionConvexMesh.h"
#include "PrimitiveSceneProxy.h"
#include "Physics/PhysicsInterfaceTypes.h"
#include "PhysicsEngine/BodySetup.h"
#include "PrimitiveViewRelevance.h"
#include "SceneManagement.h"
#include "ShaderCore.h"
#include "AI/NavigationSystemHelpers.h"
#include "WaterUtils.h"
// for working around Chaos issue
#include "Chaos/Convex.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(OceanCollisionComponent)
UOceanCollisionComponent::UOceanCollisionComponent(const FObjectInitializer& ObjectInitializer)
: UPrimitiveComponent(ObjectInitializer)
{
bHiddenInGame = true;
bCastDynamicShadow = false;
bIgnoreStreamingManagerUpdate = true;
bUseEditorCompositing = true;
}
void UOceanCollisionComponent::InitializeFromConvexElements(const TArray<FKConvexElem>& ConvexElements)
{
bool bNeedsUpdatedBody = true;
UpdateBodySetup(ConvexElements);
if (bPhysicsStateCreated)
{
// Update physics engine collision shapes
BodyInstance.UpdateBodyScale(GetComponentTransform().GetScale3D(), true);
}
}
FBoxSphereBounds UOceanCollisionComponent::CalcBounds(const FTransform& LocalToWorld) const
{
return FBoxSphereBounds(BoundingBox).TransformBy(LocalToWorld);
}
void UOceanCollisionComponent::CreateOceanBodySetupIfNeeded()
{
if (!IsValid(CachedBodySetup))
{
CachedBodySetup = NewObject<UBodySetup>(this, TEXT("BodySetup")); // a name needs to be provided to ensure determinism
CachedBodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
// HACK [jonathan.bard] : to avoid non-determinitic cook issues which can occur as new collision components are created on construction, which generates a random GUID for UBodySetup,
// we use a GUID based on the (deterministic) full name of the component, tweaked so as not to collide with standard GUIDs. BodySetupGuid should be removed altogether and UBodySetup's DDC
// key should be based only on its actual content but for now, this is one way around the determinism issue :
CachedBodySetup->BodySetupGuid = FWaterUtils::StringToGuid(GetFullName(nullptr, EObjectFullNameFlags::IncludeClassPackage));
}
}
void UOceanCollisionComponent::UpdateBodySetup(const TArray<FKConvexElem>& ConvexElements)
{
CreateOceanBodySetupIfNeeded();
FGuid PreviousBodySetupGuid = CachedBodySetup->BodySetupGuid;
CachedBodySetup->RemoveSimpleCollision();
FMemMark Mark(FMemStack::Get());
CachedBodySetup->AggGeom.ConvexElems = ConvexElements;
CachedBodySetup->bHasCookedCollisionData = true; // not clear what this flag is for, and true is the default in UBodySetup constructor?
// This currently doesn't appear to work at Runtime!!!
// Chaos paths in CreatePhysicsMeshes() will only try to get the data from the DDC.
CachedBodySetup->InvalidatePhysicsData();
// Removing the collision will needlessly generate a new Guid : restore the old one if valid to avoid invalidating the DDC / making the cook non-deterministic :
if (PreviousBodySetupGuid.IsValid())
{
CachedBodySetup->BodySetupGuid = PreviousBodySetupGuid;
}
CachedBodySetup->CreatePhysicsMeshes();
//
// GROSS HACK to work around the issue above that CreatePhysicsMeshes() doesn't currently work at Runtime.
// The "cook" for a FKConvexElem just creates and caches a Chaos::FConvex instance, and the restore from cooked
// data just restores that and pases it to the FKConvexElem. So we're just going to do that ourselves.
// Code below is taken from FChaosDerivedDataCooker::BuildConvexMeshes()
//
for (FKConvexElem& Elem : CachedBodySetup->AggGeom.ConvexElems)
{
int32 NumHullVerts = Elem.VertexData.Num();
TArray<Chaos::FConvex::FVec3Type> ConvexVertices;
ConvexVertices.SetNum(NumHullVerts);
for (int32 VertIndex = 0; VertIndex < NumHullVerts; ++VertIndex)
{
ConvexVertices[VertIndex] = Elem.VertexData[VertIndex];
}
Chaos::FConvexPtr ChaosConvex( new Chaos::FConvex(ConvexVertices, 0.0f));
Elem.SetConvexMeshObject(MoveTemp(ChaosConvex));
}
RecreatePhysicsState();
MarkRenderStateDirty();
BoundingBox = CachedBodySetup->AggGeom.CalcAABB(FTransform::Identity);
UpdateBounds();
}
UBodySetup* UOceanCollisionComponent::GetBodySetup()
{
return CachedBodySetup;
}
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
FPrimitiveSceneProxy* UOceanCollisionComponent::CreateSceneProxy()
{
/** Represents a UBoxComponent to the scene manager. */
class FOceanCollisionSceneProxy final : public FPrimitiveSceneProxy
{
public:
SIZE_T GetTypeHash() const override
{
static size_t UniquePointer;
return reinterpret_cast<size_t>(&UniquePointer);
}
FOceanCollisionSceneProxy(const UOceanCollisionComponent* InComponent)
: FPrimitiveSceneProxy(InComponent)
{
bWillEverBeLit = false;
if (InComponent->CachedBodySetup)
{
// copy the geometry for being able to access it on the render thread :
AggregateGeom = InComponent->CachedBodySetup->AggGeom;
}
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
const FMatrix& LocalToWorld = GetLocalToWorld();
const FTransform LocalToWorldTransform(LocalToWorld);
const bool bDrawCollision = ViewFamily.EngineShowFlags.Collision && IsCollisionEnabled();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
if (bDrawCollision && AllowDebugViewmodes())
{
FColor CollisionColor(157, 149, 223, 255);
const bool bPerHullColor = false;
const bool bDrawSolid = false;
AggregateGeom.GetAggGeom(LocalToWorldTransform, GetSelectionColor(CollisionColor, IsSelected(), IsHovered()).ToFColor(true), nullptr, bPerHullColor, bDrawSolid, AlwaysHasVelocity(), ViewIndex, Collector);
}
RenderBounds(Collector.GetPDI(ViewIndex), View->Family->EngineShowFlags, GetBounds(), IsSelected());
}
}
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
// Should we draw this because collision drawing is enabled, and we have collision
const bool bShowForCollision = View->Family->EngineShowFlags.Collision && IsCollisionEnabled();
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View) || bShowForCollision;
Result.bDynamicRelevance = true;
Result.bShadowRelevance = false;
Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
return Result;
}
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
uint32 GetAllocatedSize(void) const { return FPrimitiveSceneProxy::GetAllocatedSize() + AggregateGeom.GetAllocatedSize(); }
private:
FKAggregateGeom AggregateGeom;
};
return new FOceanCollisionSceneProxy(this);
}
#endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
bool UOceanCollisionComponent::DoCustomNavigableGeometryExport(FNavigableGeometryExport& GeomExport) const
{
const AWaterBody* OwningBody = GetTypedOuter<AWaterBody>();
if (CachedBodySetup && OwningBody && OwningBody->GetWaterBodyComponent())
{
FTransform GeomTransform(GetComponentTransform());
GeomTransform.AddToTranslation(OwningBody->GetWaterBodyComponent()->GetWaterNavCollisionOffset());
GeomExport.ExportRigidBodySetup(*CachedBodySetup, GeomTransform);
return false;
}
return true;
}
// ----------------------------------------------------------------------------------
UOceanBoxCollisionComponent::UOceanBoxCollisionComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
bool UOceanBoxCollisionComponent::DoCustomNavigableGeometryExport(FNavigableGeometryExport& GeomExport) const
{
const AWaterBody* OwningBody = GetTypedOuter<AWaterBody>();
if (ShapeBodySetup && OwningBody && OwningBody->GetWaterBodyComponent())
{
FTransform GeomTransform(GetComponentTransform());
GeomTransform.AddToTranslation(OwningBody->GetWaterBodyComponent()->GetWaterNavCollisionOffset());
GeomExport.ExportRigidBodySetup(*ShapeBodySetup, GeomTransform);
return false;
}
return true;
}