UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
PBDCollisionSolver.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2#pragma once
3
4#include "Chaos/Core.h"
6#include "Chaos/Defines.h"
8
9namespace Chaos
10{
11 class FManifoldPoint;
12 class FPBDCollisionConstraint;
13
14 namespace Private
15 {
16 class FPBDCollisionSolver;
17 }
18
19 namespace CVars
20 {
25 }
26
27 namespace Private
28 {
34 {
35 public:
36 // @todo(chaos): the contact point is only needed for SolveVelocityAverage - try to remove it
37 FSolverVec3 RelativeContactPoints[2]; // R (World-space) Contact point relative to each particle's center of mass
38
39 FSolverVec3 ContactNormal; // N (World Space)
40 FSolverVec3 ContactTangentU; // U (World Space)
41 FSolverVec3 ContactTangentV; // V (World Space)
42
43 FSolverReal ContactDeltaNormal; // Initial separation along N
44 FSolverReal ContactDeltaTangentU; // Initial separation along U
45 FSolverReal ContactDeltaTangentV; // Initial separation along V
46
47 FSolverReal ContactTargetVelocityNormal; // Normal velocity target
48
55
62
63 FSolverReal ContactMassNormal; // 1/[NxR0.InvI0.R0xN + InvM0 + NxR1.InvI1.R1xN + InvM1]
64 FSolverReal ContactMassTangentU; // 1/[UxR0.InvI0.R0xU + InvM0 + UxR1.InvI1.R1xU + InvM1]
65 FSolverReal ContactMassTangentV; // 1/[VxR0.InvI0.R0xV + InvM0 + VxR1.InvI1.R1xV + InvM1]
66
74
75 // A measure of how much we exceeded the static friction threshold.
76 // Equal to (NormalPushOut / TangentialPushOut) before clamping to the friction cone.
77 // Used to move the static friction anchors to the edge of the cone in Scatter.
79
80 // Transient - whether to apply friction on the current iteration
82 };
83
91 {
92 public:
93 static const int32 MaxConstrainedBodies = 2;
94
95 // Create a solver that is initialized to safe defaults
97 {
99 Solver.State.Init();
100 return Solver;
101 }
102
103 // Create a solver with no initialization
108
109 // NOTE: Does not initialize any properties. See MakeInitialized
111
114 {
115 State.SolverBodies[0].Reset();
116 State.SolverBodies[1].Reset();
117 State.ManifoldPoints = InManifoldPoints;
118 State.NumManifoldPoints = 0;
119 State.MaxManifoldPoints = InMaxManifoldPoints;
120 }
121
123 {
124 State.NumManifoldPoints = 0;
125 }
126
127 FSolverReal GetStaticFriction() const { return State.StaticFriction; }
128 FSolverReal GetDynamicFriction() const { return State.DynamicFriction; }
129 FSolverReal GetVelocityFriction() const { return State.VelocityFriction; }
130
132 {
133 State.StaticFriction = InStaticFriction;
134 State.DynamicFriction = InDynamicFriction;
135 State.VelocityFriction = InVelocityFriction;
136 State.MinMaxFrictionPushout = InMinMaxFrictionPushOut;
137 }
138
140 {
141 State.Stiffness = InStiffness;
142 }
143
145 {
146 State.SoftPhi = 0;
147 }
148
149 void SetSoftContact(const FSolverReal SoftPhi)
150 {
151 State.SoftPhi = SoftPhi;
152 }
153
155 {
156 State.SolverBodies[0].SetSolverBody(SolverBody0);
157 State.SolverBodies[1].SetSolverBody(SolverBody1);
158 }
159
161 {
162 return State.NumManifoldPoints;
163 }
164
166 {
167 return State.MaxManifoldPoints;
168 }
169
171 {
172 if (State.NumManifoldPoints < State.MaxManifoldPoints)
173 {
174 return State.NumManifoldPoints++;
175 }
176 return INDEX_NONE;
177 }
178
179 const FPBDCollisionSolverManifoldPoint& GetManifoldPoint(const int32 ManifoldPointIndex) const
180 {
181 check(State.ManifoldPoints != nullptr);
182 check(ManifoldPointIndex < NumManifoldPoints());
183
184 return State.ManifoldPoints[ManifoldPointIndex];
185 }
186
191 const int32 PointIndex,
192 const FSolverReal Dt,
202
206 void FinalizeManifold();
207
212 FConstraintSolverBody& SolverBody0() { return State.SolverBodies[0]; }
213 const FConstraintSolverBody& SolverBody0() const { return State.SolverBodies[0]; }
214
215
220 FConstraintSolverBody& SolverBody1() { return State.SolverBodies[1]; }
221 const FConstraintSolverBody& SolverBody1() const { return State.SolverBodies[1]; }
222
223
229 const FSolverReal Dt,
230 const FSolverReal MaxPushOut);
231
233 const FSolverReal Dt,
234 const FSolverReal MaxPushOut);
235
240 void SolveVelocity(
241 const FSolverReal Dt,
242 const bool bApplyDynamicFriction);
243
244 void UpdateMass();
245
246 void UpdateMassNormal();
247
248 private:
249 // Get the mass properties for the bodies if they are dynamic
250 FORCEINLINE_DEBUGGABLE void GetDynamicMassProperties(
255 {
256 OutInvM0 = 0;
257 OutInvM1 = 0;
258
260 if (Body0.IsDynamic())
261 {
262 OutInvM0 = Body0.InvM();
263 OutInvI0 = Body0.InvI();
264 }
265
267 if (Body1.IsDynamic())
268 {
269 OutInvM1 = Body1.InvM();
270 OutInvI1 = Body1.InvI();
271 }
272 }
273
274 bool IsDynamic(const int32 BodyIndex) const
275 {
276 return (State.InvMs[BodyIndex] > FSolverBody::ZeroMassThreshold());
277 }
278
279 void CalculateContactPositionCorrectionNormal(
280 const FPBDCollisionSolverManifoldPoint& ManifoldPoint,
283
284 void CalculateContactPositionErrorTangential(
285 const FPBDCollisionSolverManifoldPoint& ManifoldPoint,
288
289 void CalculateContactVelocityError(
290 const FPBDCollisionSolverManifoldPoint& ManifoldPoint,
291 const FSolverReal DynamicFriction,
292 const FSolverReal Dt,
296
297 void CalculateContactVelocityErrorNormal(
298 const FPBDCollisionSolverManifoldPoint& ManifoldPoint,
300
301 bool ShouldSolveVelocity(
302 const FPBDCollisionSolverManifoldPoint& ManifoldPoint) const;
303
304 void CalculatePositionCorrectionNormal(
306 const FSolverReal ContactDeltaNormal,
307 const FSolverReal ContactMassNormal,
308 const FSolverReal NetPushOutNormal,
310
311 void CalculatePositionCorrectionTangent(
317
318 void ApplyFrictionCone(
319 const FSolverReal StaticFriction,
320 const FSolverReal DynamicFriction,
327
328 void ApplyPositionCorrectionTangential(
330 const FSolverReal StaticFriction,
331 const FSolverReal DynamicFriction,
333 const FSolverReal ContactDeltaTangentU,
334 const FSolverReal ContactDeltaTangentV,
335 FPBDCollisionSolverManifoldPoint& ManifoldPoint);
336
337 void ApplyPositionCorrectionNormal(
339 const FSolverReal ContactDeltaNormal,
340 FPBDCollisionSolverManifoldPoint& ManifoldPoint);
341
342 void ApplySoftPositionCorrectionNormal(
344 const FSolverReal ContactDeltaNormal,
346 FPBDCollisionSolverManifoldPoint& ManifoldPoint);
347
348 void ApplyVelocityCorrection(
350 const FSolverReal Dt,
351 const FSolverReal DynamicFriction,
356 FPBDCollisionSolverManifoldPoint& ManifoldPoint);
357
358 void ApplyVelocityCorrectionNormal(
362 FPBDCollisionSolverManifoldPoint& ManifoldPoint);
363
370 void SolveVelocityAverage(const FSolverReal Dt);
371
372 struct FState
373 {
374 FState()
375 {
376 }
377
378 void Init()
379 {
380 StaticFriction = 0;
381 DynamicFriction = 0;
382 VelocityFriction = 0;
383 Stiffness = 1;
384 SolverBodies[0].Reset();
385 SolverBodies[1].Reset();
386 ManifoldPoints = nullptr;
387 NumManifoldPoints = 0;
388 MaxManifoldPoints = 0;
389 SoftPhi = 0;
390 MinMaxFrictionPushout = 0;
391 }
392
393 // Static Friction in the position-solve phase
394 FSolverReal StaticFriction;
395
396 // Dynamic Friction in the position-solve phase
397 FSolverReal DynamicFriction;
398
399 // Dynamic Friction in the velocity-solve phase
400 FSolverReal VelocityFriction;
401
402 // A min clamp on the max friction pushout - essentially the minimum
403 // friction position impulse that is always available to this contact
404 // to counteract lateral motion
405 FSolverReal MinMaxFrictionPushout;
406
407 // Solver stiffness (scales all pushout and impulses)
409
410 // Soft contact penetration
411 FSolverReal SoftPhi;
412
413 // Bodies
414 FConstraintSolverBody SolverBodies[MaxConstrainedBodies];
415 FSolverReal InvMs[2];
416
417 // Manifold Points
418 FPBDCollisionSolverManifoldPoint* ManifoldPoints;
419 int32 NumManifoldPoints;
420 int32 MaxManifoldPoints;
421 };
422
423 FState State;
424 };
425
426
431
432 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::CalculatePositionCorrectionNormal(
434 const FSolverReal ContactDeltaNormal,
435 const FSolverReal ContactMassNormal,
436 const FSolverReal NetPushOutNormal,
438 {
439 const FSolverReal PushOutNormal = -Stiffness * ContactDeltaNormal * ContactMassNormal;
440
441 // The total pushout so far this sub-step
442 // Unilateral constraint: Net-negative impulses not allowed (negative incremental impulses are allowed as long as the net is positive)
443 if ((NetPushOutNormal + PushOutNormal) > FSolverReal(0))
444 {
446 }
447 else
448 {
449 OutPushOutNormal = -NetPushOutNormal;
450 }
451 }
452
453 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::CalculatePositionCorrectionTangent(
459 {
460 // Bilateral constraint - negative values allowed (unlike the normal correction)
462 }
463
464 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::ApplyFrictionCone(
465 const FSolverReal StaticFriction,
466 const FSolverReal DynamicFriction,
473 {
479
481 {
487 }
488 else
489 {
490 // If we exceed the static friction cone, clip to the dynamic friction cone
494 {
496 const FSolverReal FrictionMultiplier = MaxDynamicPushOutTangent * FMath::InvSqrt(NetPushOutTangentSq);
501 ClampedStaticFrictionRatio = FrictionMultiplier;
502 }
503 }
504
510 }
511
512 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::ApplyPositionCorrectionTangential(
514 const FSolverReal StaticFriction,
515 const FSolverReal DynamicFriction,
517 const FSolverReal ContactDeltaTangentU,
518 const FSolverReal ContactDeltaTangentV,
519 FPBDCollisionSolverManifoldPoint& ManifoldPoint)
520 {
523
524 CalculatePositionCorrectionTangent(
525 Stiffness,
526 ContactDeltaTangentU,
527 ManifoldPoint.ContactMassTangentU,
528 ManifoldPoint.NetPushOutTangentU,
529 PushOutTangentU); // Out
530
531 CalculatePositionCorrectionTangent(
532 Stiffness,
533 ContactDeltaTangentV,
534 ManifoldPoint.ContactMassTangentV,
535 ManifoldPoint.NetPushOutTangentV,
536 PushOutTangentV); // Out
537
538 // NOTE: This function modifies NetPushOutTangentU and V
539 ApplyFrictionCone(
540 StaticFriction,
541 DynamicFriction,
543 PushOutTangentU, // InOut
544 PushOutTangentV, // InOut
545 ManifoldPoint.NetPushOutTangentU, // InOut
546 ManifoldPoint.NetPushOutTangentV, // InOut
547 ManifoldPoint.StaticFrictionRatio); // Out
548
549 // Update the particle state based on the pushout
550 const FVec3 PushOut = PushOutTangentU * ManifoldPoint.ContactTangentU + PushOutTangentV * ManifoldPoint.ContactTangentV;
551 if (IsDynamic(0))
552 {
553 const FSolverVec3 DX0 = State.InvMs[0] * PushOut;
554 const FSolverVec3 DR0 = ManifoldPoint.ContactTangentUAngular0 * PushOutTangentU + ManifoldPoint.ContactTangentVAngular0 * PushOutTangentV;
555 FConstraintSolverBody& Body0 = SolverBody0();
556 Body0.ApplyPositionDelta(DX0);
557 Body0.ApplyRotationDelta(DR0);
558 }
559 if (IsDynamic(1))
560 {
561 const FSolverVec3 DX1 = -State.InvMs[1] * PushOut;
562 const FSolverVec3 DR1 = ManifoldPoint.ContactTangentUAngular1 * -PushOutTangentU + ManifoldPoint.ContactTangentVAngular1 * -PushOutTangentV;
563 FConstraintSolverBody& Body1 = SolverBody1();
564 Body1.ApplyPositionDelta(DX1);
565 Body1.ApplyRotationDelta(DR1);
566 }
567 }
568
569 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::ApplyPositionCorrectionNormal(
571 const FSolverReal ContactDeltaNormal,
572 FPBDCollisionSolverManifoldPoint& ManifoldPoint)
573 {
575
576 CalculatePositionCorrectionNormal(
577 Stiffness,
578 ContactDeltaNormal,
579 ManifoldPoint.ContactMassNormal,
580 ManifoldPoint.NetPushOutNormal,
581 PushOutNormal); // Out
582
583 ManifoldPoint.NetPushOutNormal += PushOutNormal;
584
585 // Update the particle state based on the pushout
586 if (IsDynamic(0))
587 {
588 const FSolverVec3 DX0 = (State.InvMs[0] * PushOutNormal) * ManifoldPoint.ContactNormal;
589 const FSolverVec3 DR0 = ManifoldPoint.ContactNormalAngular0 * PushOutNormal;
590 FConstraintSolverBody& Body0 = SolverBody0();
591 Body0.ApplyPositionDelta(DX0);
592 Body0.ApplyRotationDelta(DR0);
593 }
594 if (IsDynamic(1))
595 {
596 const FSolverVec3 DX1 = (State.InvMs[1] * -PushOutNormal) * ManifoldPoint.ContactNormal;
597 const FSolverVec3 DR1 = ManifoldPoint.ContactNormalAngular1 * -PushOutNormal;
598 FConstraintSolverBody& Body1 = SolverBody1();
599 Body1.ApplyPositionDelta(DX1);
600 Body1.ApplyRotationDelta(DR1);
601 }
602 }
603
604 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::ApplySoftPositionCorrectionNormal(
606 const FSolverReal ContactDeltaNormal,
608 FPBDCollisionSolverManifoldPoint& ManifoldPoint)
609 {
610 // TODO: Apply soft pushout correction
611 }
612
613 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::ApplyVelocityCorrection(
615 const FSolverReal Dt,
616 const FSolverReal DynamicFriction,
621 FPBDCollisionSolverManifoldPoint& ManifoldPoint)
622 {
624
625 // Clamp the total impulse to be positive along the normal. We can apply a net negative impulse,
626 // but only to correct the velocity that was added by pushout (in which case MinImpulseNormal will be negative).
627 if ((ManifoldPoint.NetImpulseNormal + ImpulseNormal) < MinImpulseNormal)
628 {
629 // We are trying to apply a net negative impulse larger than one to counteract the effective pushout impulse
630 // so clamp the net impulse to be equal to minus the pushout impulse along the normal.
631 ImpulseNormal = MinImpulseNormal - ManifoldPoint.NetImpulseNormal;
632 }
633
634 ManifoldPoint.NetImpulseNormal += ImpulseNormal;
635
637
638 // Clamp the tangential impulses to the friction cone
641 if ((DynamicFriction > 0) && (Dt > 0))
642 {
646
648 = ManifoldPoint.NetImpulseNormal
649 + (ManifoldPoint.NetSoftPushOutNormal
650 + ManifoldPoint.NetPushOutNormal) / Dt;
651
652 // Clamp the total tangential impulse including positional contribution to the friction cone
653 const FSolverReal MinMaxFrictionImpulse = State.MinMaxFrictionPushout / Dt;
654 const FSolverReal MaxImpulseTangent = DynamicFriction * FMath::Max(MinMaxFrictionImpulse, TotalImpulseNormal);
656 const FSolverReal NetImpulseTangentU = ManifoldPoint.NetImpulseTangentU + ManifoldPoint.NetPushOutTangentU / Dt;
657 const FSolverReal NetImpulseTangentV = ManifoldPoint.NetImpulseTangentV + ManifoldPoint.NetPushOutTangentV / Dt;
658 const FSolverReal NetImpulseTangentSq = FMath::Square(NetImpulseTangentU + ImpulseTangentU) + FMath::Square(NetImpulseTangentV + ImpulseTangentV);
660 {
662 ImpulseTangentU = (NetImpulseTangentU + ImpulseTangentU) * ImpulseTangentScale - NetImpulseTangentU;
663 ImpulseTangentV = (NetImpulseTangentV + ImpulseTangentV) * ImpulseTangentScale - NetImpulseTangentV;
664 // TODO: Should we update the StaticFrictionRatio if we clamp here? This is used to move the
665 // static friction anchors to the edge of the friction cone
666 }
667
668 ManifoldPoint.NetImpulseTangentU += ImpulseTangentU;
669 ManifoldPoint.NetImpulseTangentV += ImpulseTangentV;
670
671 Impulse += ImpulseTangentU * ManifoldPoint.ContactTangentU + ImpulseTangentV * ManifoldPoint.ContactTangentV;
672 }
673
674 // Apply the velocity deltas from the impulse
675 if (IsDynamic(0))
676 {
677 const FSolverVec3 DV0 = State.InvMs[0] * Impulse;
678 const FSolverVec3 DW0 = ManifoldPoint.ContactNormalAngular0 * ImpulseNormal + ManifoldPoint.ContactTangentUAngular0 * ImpulseTangentU + ManifoldPoint.ContactTangentVAngular0 * ImpulseTangentV;
679 FConstraintSolverBody& Body0 = SolverBody0();
680 Body0.ApplyVelocityDelta(DV0, DW0);
681 }
682 if (IsDynamic(1))
683 {
684 const FSolverVec3 DV1 = -State.InvMs[1] * Impulse;
685 const FSolverVec3 DW1 = ManifoldPoint.ContactNormalAngular1 * -ImpulseNormal + ManifoldPoint.ContactTangentUAngular1 * -ImpulseTangentU + ManifoldPoint.ContactTangentVAngular1 * -ImpulseTangentV;
686 FConstraintSolverBody& Body1 = SolverBody1();
687 Body1.ApplyVelocityDelta(DV1, DW1);
688 }
689 }
690
691 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::ApplyVelocityCorrectionNormal(
695 FPBDCollisionSolverManifoldPoint& ManifoldPoint)
696 {
698
699 // See comments in ApplyVelocityCorrection
700 if (ManifoldPoint.NetImpulseNormal + ImpulseNormal < MinImpulseNormal)
701 {
702 ImpulseNormal = MinImpulseNormal - ManifoldPoint.NetImpulseNormal;
703 }
704
705 ManifoldPoint.NetImpulseNormal += ImpulseNormal;
706
707 // Calculate the velocity deltas from the impulse
709 if (IsDynamic(0))
710 {
711 const FSolverVec3 DV0 = State.InvMs[0] * Impulse;
712 const FSolverVec3 DW0 = ManifoldPoint.ContactNormalAngular0 * ImpulseNormal;
713 FConstraintSolverBody& Body0 = SolverBody0();
714 Body0.ApplyVelocityDelta(DV0, DW0);
715 }
716 if (IsDynamic(1))
717 {
718 const FSolverVec3 DV1 = -State.InvMs[1] * Impulse;
719 const FSolverVec3 DW1 = ManifoldPoint.ContactNormalAngular1 * -ImpulseNormal;
720 FConstraintSolverBody& Body1 = SolverBody1();
721 Body1.ApplyVelocityDelta(DV1, DW1);
722 }
723 }
724
726 {
727 const FConstraintSolverBody& Body0 = SolverBody0();
728 const FConstraintSolverBody& Body1 = SolverBody1();
729
732 GetDynamicMassProperties(InvM0, InvI0, InvM1, InvI1);
733
734 State.InvMs[0] = InvM0;
735 State.InvMs[1] = InvM1;
736
738 {
740
744
745 // These are not used if not initialized below so no need to clear
746 //ContactNormalAngular0 = FSolverVec3(0);
747 //ContactTangentUAngular0 = FSolverVec3(0);
748 //ContactTangentVAngular0 = FSolverVec3(0);
749 //ContactNormalAngular1 = FSolverVec3(0);
750 //ContactTangentUAngular1 = FSolverVec3(0);
751 //ContactTangentVAngular1 = FSolverVec3(0);
752
753 if (IsDynamic(0))
754 {
755 const FSolverVec3& R0xN = ManifoldPoint.ContactRxNormal0;
756 const FSolverVec3& R0xU = ManifoldPoint.ContactRxTangentU0;
757 const FSolverVec3& R0xV = ManifoldPoint.ContactRxTangentV0;
758
759 ManifoldPoint.ContactNormalAngular0 = InvI0 * R0xN;
760 ManifoldPoint.ContactTangentUAngular0 = InvI0 * R0xU;
761 ManifoldPoint.ContactTangentVAngular0 = InvI0 * R0xV;
762
763 ContactMassInvNormal += FSolverVec3::DotProduct(R0xN, ManifoldPoint.ContactNormalAngular0) + InvM0;
764 ContactMassInvTangentU += FSolverVec3::DotProduct(R0xU, ManifoldPoint.ContactTangentUAngular0) + InvM0;
765 ContactMassInvTangentV += FSolverVec3::DotProduct(R0xV, ManifoldPoint.ContactTangentVAngular0) + InvM0;
766 }
767 if (IsDynamic(1))
768 {
769 const FSolverVec3& R1xN = ManifoldPoint.ContactRxNormal1;
770 const FSolverVec3& R1xU = ManifoldPoint.ContactRxTangentU1;
771 const FSolverVec3& R1xV = ManifoldPoint.ContactRxTangentV1;
772
773 ManifoldPoint.ContactNormalAngular1 = InvI1 * R1xN;
774 ManifoldPoint.ContactTangentUAngular1 = InvI1 * R1xU;
775 ManifoldPoint.ContactTangentVAngular1 = InvI1 * R1xV;
776
777 ContactMassInvNormal += FSolverVec3::DotProduct(R1xN, ManifoldPoint.ContactNormalAngular1) + InvM1;
778 ContactMassInvTangentU += FSolverVec3::DotProduct(R1xU, ManifoldPoint.ContactTangentUAngular1) + InvM1;
779 ContactMassInvTangentV += FSolverVec3::DotProduct(R1xV, ManifoldPoint.ContactTangentVAngular1) + InvM1;
780 }
781
785 }
786 }
787
789 {
790 const FConstraintSolverBody& Body0 = SolverBody0();
791 const FConstraintSolverBody& Body1 = SolverBody1();
792
795 GetDynamicMassProperties(InvM0, InvI0, InvM1, InvI1);
796
797 State.InvMs[0] = InvM0;
798 State.InvMs[1] = InvM1;
799
801 {
803
805 if (IsDynamic(0))
806 {
807 const FSolverVec3& R0xN = ManifoldPoint.ContactRxNormal0;
808 ManifoldPoint.ContactNormalAngular0 = InvI0 * R0xN;
809 ContactMassInvNormal += FSolverVec3::DotProduct(R0xN, ManifoldPoint.ContactNormalAngular0) + InvM0;
810 }
811 if (IsDynamic(1))
812 {
813 const FSolverVec3& R1xN = ManifoldPoint.ContactRxNormal1;
814 ManifoldPoint.ContactNormalAngular1 = InvI1 * R1xN;
815 ContactMassInvNormal += FSolverVec3::DotProduct(R1xN, ManifoldPoint.ContactNormalAngular1) + InvM1;
816 }
818 }
819 }
820
821 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::CalculateContactPositionCorrectionNormal(
825 {
826 const FConstraintSolverBody& Body0 = SolverBody0();
827 const FConstraintSolverBody& Body1 = SolverBody1();
828
829 // Linear version: calculate the contact delta assuming linear motion after applying a positional impulse at the contact point. There will be an error that depends on the size of the rotation.
831 if (IsDynamic(0) && IsDynamic(1))
832 {
833 ContactDelta += FSolverVec3::DotProduct(Body0.DP() - Body1.DP(), ManifoldPoint.ContactNormal);
834 ContactDelta += FSolverVec3::DotProduct(Body0.DQ(), ManifoldPoint.ContactRxNormal0);
835 ContactDelta -= FSolverVec3::DotProduct(Body1.DQ(), ManifoldPoint.ContactRxNormal1);
836 }
837 else if (IsDynamic(0))
838 {
839 ContactDelta += FSolverVec3::DotProduct(Body0.DQ(), ManifoldPoint.ContactRxNormal0);
840 ContactDelta += FSolverVec3::DotProduct(Body0.DP(), ManifoldPoint.ContactNormal);
841 }
842 else if (IsDynamic(1))
843 {
844 ContactDelta -= FSolverVec3::DotProduct(Body1.DQ(), ManifoldPoint.ContactRxNormal1);
845 ContactDelta -= FSolverVec3::DotProduct(Body1.DP(), ManifoldPoint.ContactNormal);
846 }
847
848 // NOTE: ContactDelta is negative for penetration
849 // NOTE: MaxPushOut == 0 disables the pushout limits
850 if ((MaxPushOut > 0) && (ContactDelta < -MaxPushOut))
851 {
853 }
854
856 }
857
858 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::CalculateContactPositionErrorTangential(
859 const FPBDCollisionSolverManifoldPoint& ManifoldPoint,
862 {
863 const FConstraintSolverBody& Body0 = SolverBody0();
864 const FConstraintSolverBody& Body1 = SolverBody1();
865
866 // Linear version: calculate the contact delta assuming linear motion after applying a positional impulse at the contact point. There will be an error that depends on the size of the rotation.
867 FSolverReal ContactDeltaU = ManifoldPoint.ContactDeltaTangentU;
868 FSolverReal ContactDeltaV = ManifoldPoint.ContactDeltaTangentV;
869 if (IsDynamic(0) && IsDynamic(1))
870 {
871 ContactDeltaU += FSolverVec3::DotProduct(Body0.DP() - Body1.DP(), ManifoldPoint.ContactTangentU);
872 ContactDeltaU += FSolverVec3::DotProduct(Body0.DQ(), ManifoldPoint.ContactRxTangentU0);
873 ContactDeltaU -= FSolverVec3::DotProduct(Body1.DQ(), ManifoldPoint.ContactRxTangentU1);
874
875 ContactDeltaV += FSolverVec3::DotProduct(Body0.DP() - Body1.DP(), ManifoldPoint.ContactTangentV);
876 ContactDeltaV += FSolverVec3::DotProduct(Body0.DQ(), ManifoldPoint.ContactRxTangentV0);
877 ContactDeltaV -= FSolverVec3::DotProduct(Body1.DQ(), ManifoldPoint.ContactRxTangentV1);
878 }
879 else if (IsDynamic(0))
880 {
881 ContactDeltaU += FSolverVec3::DotProduct(Body0.DP(), ManifoldPoint.ContactTangentU);
882 ContactDeltaU += FSolverVec3::DotProduct(Body0.DQ(), ManifoldPoint.ContactRxTangentU0);
883
884 ContactDeltaV += FSolverVec3::DotProduct(Body0.DP(), ManifoldPoint.ContactTangentV);
885 ContactDeltaV += FSolverVec3::DotProduct(Body0.DQ(), ManifoldPoint.ContactRxTangentV0);
886 }
887 else if (IsDynamic(1))
888 {
889 ContactDeltaU -= FSolverVec3::DotProduct(Body1.DP(), ManifoldPoint.ContactTangentU);
890 ContactDeltaU -= FSolverVec3::DotProduct(Body1.DQ(), ManifoldPoint.ContactRxTangentU1);
891
892 ContactDeltaV -= FSolverVec3::DotProduct(Body1.DP(), ManifoldPoint.ContactTangentV);
893 ContactDeltaV -= FSolverVec3::DotProduct(Body1.DQ(), ManifoldPoint.ContactRxTangentV1);
894 }
895
898 }
899
900 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::CalculateContactVelocityError(
901 const FPBDCollisionSolverManifoldPoint& ManifoldPoint,
902 const FSolverReal DynamicFriction,
903 const FSolverReal Dt,
907 {
908 const FConstraintSolverBody& Body0 = SolverBody0();
909 const FConstraintSolverBody& Body1 = SolverBody1();
910
911 FSolverReal ContactVelN = -ManifoldPoint.ContactTargetVelocityNormal;
914
915 // @todo(chaos): check for static or stationary kinematics?
916 ContactVelN += FSolverVec3::DotProduct(Body0.V() - Body1.V(), ManifoldPoint.ContactNormal);
917 ContactVelN += FSolverVec3::DotProduct(Body0.W(), ManifoldPoint.ContactRxNormal0);
918 ContactVelN -= FSolverVec3::DotProduct(Body1.W(), ManifoldPoint.ContactRxNormal1);
919
920 ContactVelU += FSolverVec3::DotProduct(Body0.V() - Body1.V(), ManifoldPoint.ContactTangentU);
921 ContactVelU += FSolverVec3::DotProduct(Body0.W(), ManifoldPoint.ContactRxTangentU0);
922 ContactVelU -= FSolverVec3::DotProduct(Body1.W(), ManifoldPoint.ContactRxTangentU1);
923
924 ContactVelV += FSolverVec3::DotProduct(Body0.V() - Body1.V(), ManifoldPoint.ContactTangentV);
925 ContactVelV += FSolverVec3::DotProduct(Body0.W(), ManifoldPoint.ContactRxTangentV0);
926 ContactVelV -= FSolverVec3::DotProduct(Body1.W(), ManifoldPoint.ContactRxTangentV1);
927
931 }
932
933 FORCEINLINE_DEBUGGABLE void FPBDCollisionSolver::CalculateContactVelocityErrorNormal(
934 const FPBDCollisionSolverManifoldPoint& ManifoldPoint,
936 {
937 const FConstraintSolverBody& Body0 = SolverBody0();
938 const FConstraintSolverBody& Body1 = SolverBody1();
939
940 FSolverReal ContactVelN = -ManifoldPoint.ContactTargetVelocityNormal;
941 ContactVelN += FSolverVec3::DotProduct(Body0.V() - Body1.V(), ManifoldPoint.ContactNormal);
942 ContactVelN += FSolverVec3::DotProduct(Body0.W(), ManifoldPoint.ContactRxNormal0);
943 ContactVelN -= FSolverVec3::DotProduct(Body1.W(), ManifoldPoint.ContactRxNormal1);
944
946 }
947
948 FORCEINLINE_DEBUGGABLE bool FPBDCollisionSolver::ShouldSolveVelocity(
949 const FPBDCollisionSolverManifoldPoint& ManifoldPoint) const
950 {
951 // We ensure positive separating velocity for close contacts even if they didn't receive a pushout
952 return (ManifoldPoint.NetPushOutNormal > FSolverReal(0)) || (ManifoldPoint.NetSoftPushOutNormal > FSolverReal(0)) || (ManifoldPoint.ContactDeltaNormal < State.SoftPhi);
953 }
954
956 const int32 PointIndex,
957 const FSolverReal Dt,
967 {
968 check(State.ManifoldPoints != nullptr);
969 check(PointIndex < State.NumManifoldPoints);
971
972 ManifoldPoint.RelativeContactPoints[0] = InRelativeContactPosition0;
973 ManifoldPoint.RelativeContactPoints[1] = InRelativeContactPosition1;
974
975 ManifoldPoint.ContactNormal = InWorldContactNormal;
976 ManifoldPoint.ContactTangentU = InWorldContactTangentU;
977 ManifoldPoint.ContactTangentV = InWorldContactTangentV;
978 ManifoldPoint.ContactDeltaNormal = InWorldContactDeltaNormal;
979 ManifoldPoint.ContactDeltaTangentU = InWorldContactDeltaTangentU;
980 ManifoldPoint.ContactDeltaTangentV = InWorldContactDeltaTangentV;
981 ManifoldPoint.ContactTargetVelocityNormal = InWorldContactVelocityTargetNormal;
982
983 ManifoldPoint.ContactRxNormal0 = FSolverVec3::CrossProduct(InRelativeContactPosition0, InWorldContactNormal);
984 ManifoldPoint.ContactRxTangentU0 = FSolverVec3::CrossProduct(InRelativeContactPosition0, InWorldContactTangentU);
985 ManifoldPoint.ContactRxTangentV0 = FSolverVec3::CrossProduct(InRelativeContactPosition0, InWorldContactTangentV);
986 ManifoldPoint.ContactRxNormal1 = FSolverVec3::CrossProduct(InRelativeContactPosition1, InWorldContactNormal);
987 ManifoldPoint.ContactRxTangentU1 = FSolverVec3::CrossProduct(InRelativeContactPosition1, InWorldContactTangentU);
988 ManifoldPoint.ContactRxTangentV1 = FSolverVec3::CrossProduct(InRelativeContactPosition1, InWorldContactTangentV);
989
990 ManifoldPoint.NetPushOutNormal = FSolverReal(0);
991 ManifoldPoint.NetPushOutTangentU = FSolverReal(0);
992 ManifoldPoint.NetPushOutTangentV = FSolverReal(0);
993 ManifoldPoint.NetImpulseNormal = FSolverReal(0);
994 ManifoldPoint.NetImpulseTangentU = FSolverReal(0);
995 ManifoldPoint.NetImpulseTangentV = FSolverReal(0);
996 ManifoldPoint.StaticFrictionRatio = FSolverReal(0);
997 }
998
1003
1005 const FSolverReal Dt,
1006 const FSolverReal MaxPushOut)
1007 {
1008
1009 // Apply the position correction along the normal and determine if we want to run friction on each point
1011
1012 // Apply the tangential position correction if required
1014 if (FrictionStiffness > 0)
1015 {
1017 {
1019
1021 = SolverManifoldPoint.NetPushOutNormal
1022 + SolverManifoldPoint.NetSoftPushOutNormal;
1023
1024 const FSolverReal FrictionMaxPushOut = FMath::Max(State.MinMaxFrictionPushout, TotalPushOutNormal);
1025
1026 const bool bApplyFriction = (FrictionMaxPushOut > 0) || (SolverManifoldPoint.NetPushOutTangentU != 0) || (SolverManifoldPoint.NetPushOutTangentV != 0);
1027
1028 if (bApplyFriction)
1029 {
1030 FSolverReal ContactDeltaTangentU, ContactDeltaTangentV;
1031 CalculateContactPositionErrorTangential(SolverManifoldPoint, ContactDeltaTangentU, ContactDeltaTangentV);
1032
1033 ApplyPositionCorrectionTangential(
1035 State.StaticFriction,
1036 State.DynamicFriction,
1038 ContactDeltaTangentU,
1039 ContactDeltaTangentV,
1041 }
1042 }
1043 }
1044 }
1045
1047 const FSolverReal Dt,
1048 const FSolverReal MaxPushOut)
1049 {
1050 // Apply the position correction so that all contacts have zero separation
1052 {
1054
1056 CalculateContactPositionCorrectionNormal(SolverManifoldPoint, MaxPushOut, ContactCorrectionNormal);
1057
1058 // Shift the contact to the hard shell under the soft layer
1059 const FSolverReal HardContactErrorNormal = SolverManifoldPoint.ContactDeltaNormal + ContactCorrectionNormal - State.SoftPhi;
1060
1063 {
1064 ApplyPositionCorrectionNormal(
1065 State.Stiffness,
1068 }
1069
1070 //
1071 // TODO: when State.SoftPhi < 0, apply soft position correction
1072 //
1073 }
1074 }
1075
1081 const FSolverReal Dt,
1082 const bool bApplyDynamicFriction)
1083 {
1084 // Apply restitution at the average contact point
1085 // This means we don't need to run as many iterations to get stable bouncing
1086 // It also helps with zero restitution to counter any velocioty added by the PBD solve
1089 {
1090 SolveVelocityAverage(Dt);
1091 }
1092
1093 const FSolverReal DynamicFriction = (bApplyDynamicFriction && (Dt > 0) && CVars::bChaos_PBDCollisionSolver_Velocity_FrictionEnabled) ? State.VelocityFriction : FSolverReal(0);
1094
1096 {
1098
1099 const bool bShouldSolveNormalVelocity = (SolverManifoldPoint.NetPushOutNormal > FSolverReal(0));
1100 const bool bShouldSolveTangentVelocity = (DynamicFriction > 0) && (bShouldSolveNormalVelocity || (SolverManifoldPoint.ContactDeltaNormal < State.SoftPhi) || (SolverManifoldPoint.NetSoftPushOutNormal > FSolverReal(0)));
1101
1103 {
1104 const FSolverReal MinImpulseNormal = FMath::Min(FSolverReal(0), -(SolverManifoldPoint.NetPushOutNormal + SolverManifoldPoint.NetSoftPushOutNormal) / Dt);
1105
1106 // @todo(chaos): clean this up - friction and no-friction versions should share solve-normal code
1108 {
1111
1113 {
1115 }
1116
1117 ApplyVelocityCorrection(
1118 State.Stiffness,
1119 Dt,
1120 DynamicFriction,
1126 }
1128 {
1130 CalculateContactVelocityErrorNormal(SolverManifoldPoint, ContactVelocityDeltaNormal);
1131
1132 ApplyVelocityCorrectionNormal(
1133 State.Stiffness,
1137 }
1138 }
1139 }
1140 }
1141
1142 } // namespace Private
1143} // namespace Chaos
1144
#define check(expr)
Definition AssertionMacros.h:314
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define FORCEINLINE_DEBUGGABLE
Definition CoreMiscDefines.h:74
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
#define UE_SMALL_NUMBER
Definition UnrealMathUtility.h:130
#define UE_KINDA_SMALL_NUMBER
Definition UnrealMathUtility.h:131
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition SolverBody.h:543
bool IsDynamic() const
Whether the body is dynamic (i.e., has a finite mass) after scaling is applied.
Definition SolverBody.h:622
const FSolverVec3 & DP() const
Definition SolverBody.h:641
FSolverReal InvM() const
The net scaled inverse mass.
Definition SolverBody.h:607
FSolverMatrix33 InvI() const
The net scaled inverse inertia.
Definition SolverBody.h:612
const FSolverVec3 & DQ() const
Definition SolverBody.h:642
Definition SolverBody.h:99
static constexpr FSolverReal ZeroMassThreshold()
Definition SolverBody.h:101
Definition Matrix.h:21
A single contact point in a FPBDCollisionSolver.
Definition PBDCollisionSolver.h:34
FSolverVec3 ContactRxTangentU1
Definition PBDCollisionSolver.h:53
FSolverReal ContactMassTangentV
Definition PBDCollisionSolver.h:65
FSolverVec3 ContactTangentU
Definition PBDCollisionSolver.h:40
FSolverReal NetImpulseNormal
Definition PBDCollisionSolver.h:70
FSolverVec3 ContactTangentUAngular1
Definition PBDCollisionSolver.h:60
FSolverVec3 ContactTangentVAngular0
Definition PBDCollisionSolver.h:58
FSolverVec3 ContactRxTangentV0
Definition PBDCollisionSolver.h:52
uint32 bApplyFriction
Definition PBDCollisionSolver.h:81
FSolverVec3 ContactNormalAngular1
Definition PBDCollisionSolver.h:59
FSolverReal NetSoftPushOutNormal
Definition PBDCollisionSolver.h:73
FSolverReal NetPushOutTangentU
Definition PBDCollisionSolver.h:68
FSolverVec3 ContactNormalAngular0
Definition PBDCollisionSolver.h:56
FSolverVec3 RelativeContactPoints[2]
Definition PBDCollisionSolver.h:37
FSolverReal ContactMassTangentU
Definition PBDCollisionSolver.h:64
FSolverReal NetImpulseTangentU
Definition PBDCollisionSolver.h:71
FSolverReal NetPushOutNormal
Definition PBDCollisionSolver.h:67
FSolverReal ContactMassNormal
Definition PBDCollisionSolver.h:63
FSolverVec3 ContactRxTangentU0
Definition PBDCollisionSolver.h:51
FSolverVec3 ContactTangentV
Definition PBDCollisionSolver.h:41
FSolverReal NetImpulseTangentV
Definition PBDCollisionSolver.h:72
FSolverReal StaticFrictionRatio
Definition PBDCollisionSolver.h:78
FSolverVec3 ContactRxNormal0
Definition PBDCollisionSolver.h:49
FSolverReal ContactDeltaNormal
Definition PBDCollisionSolver.h:43
FSolverReal ContactDeltaTangentV
Definition PBDCollisionSolver.h:45
FSolverReal NetPushOutTangentV
Definition PBDCollisionSolver.h:69
FSolverVec3 ContactNormal
Definition PBDCollisionSolver.h:39
FSolverVec3 ContactTangentVAngular1
Definition PBDCollisionSolver.h:61
FSolverVec3 ContactRxTangentV1
Definition PBDCollisionSolver.h:54
FSolverVec3 ContactRxNormal1
Definition PBDCollisionSolver.h:50
FSolverReal ContactDeltaTangentU
Definition PBDCollisionSolver.h:44
FSolverVec3 ContactTangentUAngular0
Definition PBDCollisionSolver.h:57
FSolverReal ContactTargetVelocityNormal
Definition PBDCollisionSolver.h:47
Definition PBDCollisionSolver.h:91
FSolverReal GetVelocityFriction() const
Definition PBDCollisionSolver.h:129
void SolveVelocity(const FSolverReal Dt, const bool bApplyDynamicFriction)
Calculate and apply the velocity correction for this iteration.
Definition PBDCollisionSolver.h:1080
static const int32 MaxConstrainedBodies
Definition PBDCollisionSolver.h:93
static FPBDCollisionSolver MakeUninitialized()
Definition PBDCollisionSolver.h:104
const FPBDCollisionSolverManifoldPoint & GetManifoldPoint(const int32 ManifoldPointIndex) const
Definition PBDCollisionSolver.h:179
void SetStiffness(const FSolverReal InStiffness)
Definition PBDCollisionSolver.h:139
void SolvePositionNoFriction(const FSolverReal Dt, const FSolverReal MaxPushOut)
Calculate and apply the position correction for this iteration.
Definition PBDCollisionSolver.h:1046
const FConstraintSolverBody & SolverBody1() const
Definition PBDCollisionSolver.h:221
void UpdateMassNormal()
Definition PBDCollisionSolver.h:788
void FinalizeManifold()
Definition PBDCollisionSolver.h:999
void SetSoftContact(const FSolverReal SoftPhi)
Definition PBDCollisionSolver.h:149
void ResetManifold()
Definition PBDCollisionSolver.h:122
void InitManifoldPoint(const int32 PointIndex, const FSolverReal Dt, const FSolverVec3 &InRelativeContactPosition0, const FSolverVec3 &InRelativeContactPosition1, const FSolverVec3 &InWorldContactNormal, const FSolverVec3 &InWorldContactTangentU, const FSolverVec3 &InWorldContactTangentV, const FSolverReal InWorldContactDeltaNormal, const FSolverReal InWorldContactDeltaTangentU, const FSolverReal InWorldContactDeltaTangentV, const FSolverReal InWorldContactVelocityTargetNormal)
Definition PBDCollisionSolver.h:955
FSolverReal GetStaticFriction() const
Definition PBDCollisionSolver.h:127
void UpdateMass()
Definition PBDCollisionSolver.h:725
FConstraintSolverBody & SolverBody0()
Get the first solver body NOTE: This will not include any shock propagation mass scaling.
Definition PBDCollisionSolver.h:212
void SetSolverBodies(FSolverBody &SolverBody0, FSolverBody &SolverBody1)
Definition PBDCollisionSolver.h:154
int32 AddManifoldPoint()
Definition PBDCollisionSolver.h:170
void SetHardContact()
Definition PBDCollisionSolver.h:144
FSolverReal GetDynamicFriction() const
Definition PBDCollisionSolver.h:128
int32 NumManifoldPoints() const
Definition PBDCollisionSolver.h:160
const FConstraintSolverBody & SolverBody0() const
Definition PBDCollisionSolver.h:213
void SetFriction(const FSolverReal InStaticFriction, const FSolverReal InDynamicFriction, const FSolverReal InVelocityFriction, const FSolverReal InMinMaxFrictionPushOut)
Definition PBDCollisionSolver.h:131
FPBDCollisionSolver()
Definition PBDCollisionSolver.h:110
void Reset(FPBDCollisionSolverManifoldPoint *InManifoldPoints, const int32 InMaxManifoldPoints)
Definition PBDCollisionSolver.h:113
static FPBDCollisionSolver MakeInitialized()
Definition PBDCollisionSolver.h:96
int32 MaxManifoldPoints() const
Definition PBDCollisionSolver.h:165
void SolvePositionWithFriction(const FSolverReal Dt, const FSolverReal MaxPushOut)
Definition PBDCollisionSolver.h:1004
FConstraintSolverBody & SolverBody1()
Get the second solver body NOTE: This will not include any shock propagation mass scaling.
Definition PBDCollisionSolver.h:220
Definition Vector.h:1000
bool bChaos_PBDCollisionSolver_Velocity_FrictionEnabled
Definition PBDCollisionSolver.cpp:47
float Chaos_PBDCollisionSolver_Position_StaticFrictionStiffness
Definition PBDCollisionSolver.cpp:34
bool bChaos_PBDCollisionSolver_Velocity_AveragePointEnabled
Definition PBDCollisionSolver.cpp:49
float Chaos_PBDCollisionSolver_Velocity_StaticFrictionStiffness
Definition PBDCollisionSolver.cpp:48
Definition SkeletalMeshComponent.h:307
TVec3< FSolverReal > FSolverVec3
Definition SolverBody.h:44
FRealSingle FSolverReal
Definition SolverBody.h:38
TVector< FReal, 3 > FVec3
Definition Core.h:17
void Init()
Definition LaunchIOS.cpp:473
Definition OverriddenPropertySet.cpp:45
FAutoConsoleVariableRef CVars[]
Definition MassProcessingPhaseManager.cpp:29
static constexpr UE_FORCEINLINE_HINT T Square(const T A)
Definition UnrealMathUtility.h:578