Description

A welded hierarchy blocks updates to collision settings on all but the root primitive. Calling SetCollisionResponseToChannel on the children won't work. The licensee noticed that IPhysicsProxyBase* Proxy on FShapeInstanceProxy's is null if a primitive is in a welded hiearchy. Because of this the CollisionData (a TShapeProperty) can never be marked dirty. There is an explicit check, to never mark dirty, if the Proxy is null.
And then finally, if the property is never marked dirty, then the data inside will never be marshaled to the physics simulation. The effect is that only the root in a hiearchy will change CollisionSettings at runtime, which includes FCollisionFilterData SimData. The SimData is correctly updated if the properties are marked dirty manually like so:

 

void MarkDirty(FBodyInstance& BodyInstance)
{
    const FPhysicsActorHandle& ActorHandle = BodyInstance.ActorHandle;
    TArray<FPhysicsShapeHandle> AllShapes;
    const int32 NumSyncShapes = FPhysicsInterface::GetAllShapes_AssumedLocked(ActorHandle, AllShapes);
    const int32 NumTotalShapes = AllShapes.Num();
    if (BodyInstance.ActorHandle->GetPhysicsThreadAPI())
    {
        const auto& Proxy = BodyInstance.ActorHandle->GetPhysicsThreadAPI()->GetProxy();
        if (Chaos::FPhysicsSolverBase* PhysicsSolverBase = Proxy->GetSolver<Chaos::FPhysicsSolverBase>())
        {
            for (int32 ShapeIdx = 0; ShapeIdx < NumTotalShapes; ++ShapeIdx)
            { 
                PhysicsSolverBase->AddDirtyProxyShape(Proxy, ShapeIdx); 
            }
        }
    }
}

 

Steps to Reproduce

To reproduce in the attached repro project:
Simulate Play In Editor session.
Expected behavior: the BP called Hierarchy, composed of three static mesh components, should go thru the bottom platform, as their collision responses to the bottom platform were set to ignore by the ChangePhysicsSettings component
Actual behavior: only the first cube (the root primitive) goes thru the bottom platform, the change does not affect the other welded primitives and they collide with the platform

To manually recreate the scene:
Create a new project.
Create a new level basic.
Create a C++ ActorComponent.
To the header, add to the public section:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components")
UPrimitiveComponent* ComponentRoot;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components")
UPrimitiveComponent* ComponentOne;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components")
UPrimitiveComponent* ComponentTwo;

To the body, add to the TickComponent:Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

 

if (bUpdateCollisionSettings)
{     
    bUpdateCollisionSettings = false;     

    if (ComponentOne)     
    { 
        ComponentOne->GetBodyInstance()->SetResponseToChannel(ECollisionChannel::ECC_PhysicsBody, ECollisionResponse::ECR_Ignore); 
    }
    if (ComponentTwo)
    {
        ComponentTwo->GetBodyInstance()->SetResponseToChannel(ECollisionChannel::ECC_PhysicsBody, ECollisionResponse::ECR_Ignore); 
    }
    if (ComponentRoot)
    { 
        ComponentRoot->GetBodyInstance()->SetResponseToChannel(ECollisionChannel::ECC_PhysicsBody, ECollisionResponse::ECR_Ignore); 
    }
}

Save and compile.
Back in the Editor, create a StaticMeshActor Blueprint called Hierarchy, set it's StaticMeshComponent to an Engine cube, add a component Cube as child of the Static Mesh Component and another as child of the second cube, arrange their positioning in a way that makes it visible to notice the collision with the bottom platform.
Confirm that all three StaticMeshComponents have their CollisionPresets to PhyhsicsActor, so they block all.
On the BPs constructor set the variables Component Root to the StaticMeshComponent, Component One to the first cube, Component Two to the second.
Back in the created level, add a cube and scale it until it looks like a platform, set it's collision preset to PhyhsicsActor, confirm that the ObjectType is PhysicsBody, and CollisionResponses blocking all, disable SimulatePhysics so it is static.
Spawn a Hierarchy BP on top of this platform so it will fall over it, make sure it is simulation physics and gravity.
Save the scene.
Simulate in Editor and check the behavior.
Expected behavior: the Hierarchy BP should fall thru the platform.
Actual behavior: only the root cube ignores collision with the platform, the other children collide with it

Callstack

> UnrealEditor-Chaos.dll!Chaos::PushToPhysicsStateImp<0>(const Chaos::FDirtyPropertiesManager & Manager, Chaos::TGeometryParticleHandleImp<double,3,1> * Handle, int DataIdx, const Chaos::FDirtyProxy & Dirty, Chaos::FShapeDirtyData * ShapesData, Chaos::FPBDRigidsSolver & Solver, bool bResimInitialized, double ExternalDt) Line 184 C++
UnrealEditor-Chaos.dll!Chaos::FSingleParticlePhysicsProxy::PushToPhysicsState(const Chaos::FDirtyPropertiesManager & Manager, int DataIdx, const Chaos::FDirtyProxy & Dirty, Chaos::FShapeDirtyData * ShapesData, double ExternalDt) Line 211 C++
UnrealEditor-Chaos.dll!$?RPEAVFSingleParticlePhysicsProxy@Chaos@@V<lambda_1>@?7?R<lambda_2>@?1??ProcessSinglePushedData_Internal@FPBDRigidsSolver@1@AEAAXAEAUFPushPhysicsData@1@@Z@QEBA@HAEAUFDirtyProxy@1@@Z@@<lambda_1>@?1??ProcessSinglePushedData_Internal@FPBDRigidsSolver@Chaos@@AEAAXAEAUFPushPhysicsData@3@@Z@QEBA?A_PAEAPEAVFSingleParticlePhysicsProxy@3@HAEAUFDirtyProxy@3@AEBV0?7???R<lambda_2>@?1??123@AEAAX0@Z@QEBA@H2@Z@@Z(Chaos::FSingleParticlePhysicsProxy * & Proxy, int DataIdx, Chaos::FDirtyProxy & Dirty, const Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal::_l2::<lambda_2>:[Image Removed])::_l8::<lambda_1> & CreateHandleFunc) Line 1596 C++
[Inline Frame] UnrealEditor-Chaos.dll!Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal::__l2::<lambda_2>::operator()(int) Line 1628 C++
UnrealEditor-Chaos.dll!??$ForEachProxy@V<lambda_2>@?1??ProcessSinglePushedData_Internal@FPBDRigidsSolver@Chaos@@AEAAXAEAUFPushPhysicsData@4@@Z@@FDirtySet@Chaos@@QEAAXAEBV<lambda_2>@?1??ProcessSinglePushedData_Internal@FPBDRigidsSolver@1@AEAAXAEAUFPushPhysicsData@1@@Z@@Z(const Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal::__l2::<lambda_2> & Func) Line 185 C++
UnrealEditor-Chaos.dll!Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal(Chaos::FPushPhysicsData & PushData) Line 1678 C++
UnrealEditor-Chaos.dll!Chaos::FPBDRigidsSolver::ProcessPushedData_Internal(Chaos::FPushPhysicsData & PushData) Line 1931 C++
UnrealEditor-Chaos.dll!Chaos::FPhysicsSolverProcessPushDataTask::ProcessPushData() Line 115 C++
[Inline Frame] UnrealEditor-Chaos.dll!Chaos::FPhysicsSolverProcessPushDataTask::DoTask(ENamedThreads::Type) Line 112 C++
UnrealEditor-Chaos.dll!TGraphTask<Chaos::FPhysicsSolverProcessPushDataTask>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1235 C++
[Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> &) Line 840 C++
[Inline Frame] UnrealEditor-Core.dll!FTaskGraphCompatibilityImplementation::QueueTask::__l5::<lambda>() Line 1970 C++
UnrealEditor-Core.dll!LowLevelTasks::FTask::Init::__l13::<lambda>(const bool bNotCanceled) Line 499 C++
[Inline Frame] UnrealEditor-Core.dll!Invoke(LowLevelTasks::FTask::Init::__l13::void <lambda>(void) &) Line 47 C++
[Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`FTaskGraphCompatibilityImplementation::QueueTask'::`5'::void <lambda>(void)>'::`13'::void <lambda>(void),0>::Call(void *) Line 162 C++
UnrealEditor-Core.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`FTaskGraphCompatibilityImplementation::QueueTask'::`5'::void <lambda>(void)>'::`13'::void <lambda>(void),0>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> & Destination, void * InlineData, unsigned int DestInlineSize, bool <Params_0>) Line 171 C++
[Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> &) Line 308 C++
UnrealEditor-Core.dll!LowLevelTasks::FTask::ExecuteTask() Line 627 C++
UnrealEditor-Core.dll!LowLevelTasks::FScheduler::ExecuteTask(LowLevelTasks::FTask * InTask) Line 155 C++
[Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::FScheduler::TryExecuteTaskFrom(LowLevelTasks::Private::FWaitEvent *) Line 362 C++
UnrealEditor-Core.dll!LowLevelTasks::FScheduler::WorkerMain(LowLevelTasks::Private::FWaitEvent * WorkerEvent, LowLevelTasks::Private::TLocalQueueRegistry<1024>::TLocalQueue * WorkerLocalQueue, unsigned int WaitCycles, bool bPermitBackgroundWork) Line 397 C++
[Inline Frame] UnrealEditor-Core.dll!LowLevelTasks::FScheduler::CreateWorker::__l2::<lambda>() Line 70 C++
[Inline Frame] UnrealEditor-Core.dll!Invoke(LowLevelTasks::FScheduler::CreateWorker::__l2::void <lambda>(void) &) Line 47 C++
UnrealEditor-Core.dll!UE::Core::Private::Function::TFunctionRefCaller<`LowLevelTasks::FScheduler::CreateWorker'::`2'::void <lambda>(void),void __cdecl(void)>::Call(void * Obj) Line 406 C++
[Inline Frame] UnrealEditor-Core.dll!UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(void)>::operator()() Line 555 C++
UnrealEditor-Core.dll!FThreadImpl::Run() Line 69 C++
UnrealEditor-Core.dll!FRunnableThreadWin::Run() Line 149 C++
UnrealEditor-Core.dll!FRunnableThreadWin::GuardedRun() Line 71 C++
[External Code]

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-231274 in the post.

2
Login to Vote

Unresolved
ComponentUE - Simulation - Physics
Affects Versions5.65.4.4
Target Fix5.6
CreatedNov 22, 2024
UpdatedNov 26, 2024
View Jira Issue