Description

When a Chaos joint constraint is first created, having linear/angular plasticity enabled will unexpectedly force its linear/angular drive position target to zero on initialization, disregarding the settings made on the Editor's Details Panel.

The specific flow starts with function Chaos::FJointConstraintPhysicsProxy::InitializeOnPhysicsThread(), which calls Chaos::FPBDRigidsEvolutionGBF::CreateJointConstraint(), which in turn calls Chaos::FPBDJointConstraintHandle::SetSettings() on the joint that has just been created. SetSettings() checks if plasticity is enabled. If it is, then it saves the values for the drive position targets before applying the new constraint settings, and restores those saved values afterwards, which means that they are reset to zero on this initialization scenario. This looks like an attempt to forbid changes to the drive position targets afer plasticity is enabled, but it also effectively prevents the initial targets from being set correctly. Note that if a constraint is created with some non-zero targets and with plasticity initially disabled, enabling plasticity later does allow the initial targets to be kept and used normally.

The backup-and-restore logic was introduced on CL 18730967 from January 2022 with commit message "Reset Joint constraint based on internal plasticity values when the settings are updated from the proxy". FJointConstraintPhysicsProxy has changed substantially since then, so it is possible that the logic worked correctly at first but not anymore.

Steps to Reproduce

1. Create a new level from the Basic template.

2. Place a Cone actor (will be affected by the constraint)

2.1. Location (100, 0, 200)

2.2. Enable "simulate physics"

2.3. Disable gravity

3. Place a Sphere actor (just for visual reference, not used in the constraint)

3.1. Location (200, 0, 200)

3.2. Set "Collision Preset" to "No Collision"

4. Place a PhysicsConstraintActor

4.1. Location (100, 0, 200)

4.2. On "Constraint", set "Constraint Actor 1" to the Cone.

4.3. On "Linear Limits", set all axes to "Free".

4.4. On "Angular Limits", set all motions to "Locked".

4.5. On "Linear Motor", enable all position targets, set the target to (100, 0, 0), and set the strength to 500

5. Edit the Level Blueprint

5.1. On "Begin Play", set the Cone actor location to (500, 500, 500)

6. Simulate in editor. The cone is driven to the sphere at (200,0,200), as expected.

7. Select the PhysicsConstraintActor

7.1. On "Linear Limits", enable "Linear Plasticity" and set its threshold to 1000

8. Simulate in editor. The cone is incorrectly driven to its original location at (100, 0, 200)

8.1. A breakpoint in FPBDJointConstraintHandle::SetSettings() reveals that LinearDrivePositionTarget is being unexpectedly reset to zero

9. Defer enabling linear plasticity

9.1. On the PhysicsConstraintActor, disable "Linear Plasticity"

9.2. On the Level Blueprint, at the end of Begin Play, add a node "Delay Until Next Frame", then call node SetLinearPlasticity() on the PhysicsConstraint to enable plasticity with a threshold of 1000

9.3. Simulate in editor. The cone is correctly driven to the sphere at (200,0,200).

Callstack

Chaos::FPBDJointConstraintHandle::SetSettings(const Chaos::FPBDJointSettings & InSettings) Line 313

Chaos::FPBDRigidsEvolutionGBF::CreateJointConstraint(const Chaos::TVector<Chaos::TGeometryParticleHandleImp<double,3,1> *,2> & InConstrainedParticles, const Chaos::FPBDJointSettings & InJointSettings) Line 1461

Chaos::FJointConstraintPhysicsProxy::InitializeOnPhysicsThread(Chaos::FPBDRigidsSolver * InSolver, Chaos::FDirtyPropertiesManager & Manager, int DataIdx, Chaos::FDirtyChaosProperties & RemoteData) Line 130

Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal::__l2::<lambda_3>::operator()(int) Line 1800

Chaos::FDirtySet::ForEachProxy<`Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal'::`2'::<lambda_3>>(const Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal::__l2::<lambda_3> & Func) Line 231

Chaos::FPBDRigidsSolver::ProcessSinglePushedData_Internal(Chaos::FPushPhysicsData & PushData) Line 1864

Chaos::FPBDRigidsSolver::ProcessPushedData_Internal(Chaos::FPushPhysicsData & PushData) Line 2091

Chaos::FPhysicsSolverProcessPushDataTask::ProcessPushData() Line 144

Chaos::FPhysicsSolverProcessPushDataTask::DoTask(ENamedThreads::Type) Line 119

TGraphTask<Chaos::FPhysicsSolverProcessPushDataTask>::ExecuteTask() Line 710

UE::Tasks::Private::FTaskBase::TryExecuteTask() Line 518

UE::Tasks::Private::FTaskBase::Init::__l2::<lambda_1>::operator()() Line 180

LowLevelTasks::FTask::Init::__l15::<lambda_1>::operator()(const bool) Line 500

Invoke(LowLevelTasks::FTask::Init::__l15::<lambda_1> &) Line 47

LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1>>'::`15'::<lambda_1>,0>::Call(void *) Line 162

LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::TTaskDelegateImpl<`LowLevelTasks::FTask::Init<`UE::Tasks::Private::FTaskBase::Init'::`2'::<lambda_1>>'::`15'::<lambda_1>,0>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> & Destination, void * InlineData, unsigned int DestInlineSize, bool <Params_0>) Line 171

owLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48>::CallAndMove(LowLevelTasks::TTaskDelegate<LowLevelTasks::FTask * __cdecl(bool),48> &) Line 309

owLevelTasks::FTask::ExecuteTask() Line 628

owLevelTasks::FScheduler::ExecuteTask(LowLevelTasks::FTask * InTask) Line 400

owLevelTasks::FScheduler::TryExecuteTaskFrom(LowLevelTasks::Private::FWaitEvent *) Line 701

owLevelTasks::FScheduler::WorkerLoop(LowLevelTasks::Private::FWaitEvent * WorkerEvent, LowLevelTasks::Private::TLocalQueueRegistry<1024,1024>::TLocalQueue * WorkerLocalQueue, unsigned int WaitCycles, bool bPermitBackgroundWork) Line 799

owLevelTasks::FScheduler::WorkerMain(LowLevelTasks::Private::FWaitEvent * WorkerEvent, LowLevelTasks::Private::TLocalQueueRegistry<1024,1024>::TLocalQueue * WorkerLocalQueue, unsigned int WaitCycles, bool) Line 858

owLevelTasks::FScheduler::CreateWorker::__l2::<lambda_1>::operator()() Line 220

E::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::TFunctionStorage<1>,void __cdecl(void)>::operator()() Line 414

ThreadImpl::Run() Line 69

RunnableThreadWin::Run() Line 167

RunnableThreadWin::GuardedRun() Line 79

Have Comments or More Details?

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

0
Login to Vote

Fixed
ComponentUE - Simulation - Core
Affects Versions5.75.8
Target Fix5.8
Fix Commit51556398
CreatedMar 5, 2026
ResolvedMar 9, 2026
UpdatedMar 17, 2026
View Jira Issue