| | | 1 | | //======================================================================= |
| | | 2 | | // ExactContactLever2D.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2026-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using FixedMathSharp; |
| | | 9 | | using FixedMathSharp.Geometry; |
| | | 10 | | |
| | | 11 | | namespace Gravitas.CollisionHandling; |
| | | 12 | | |
| | | 13 | | /// <summary> |
| | | 14 | | /// Adapts planar body mobility and signed yaw to the shared exact 3D response |
| | | 15 | | /// kernel. |
| | | 16 | | /// </summary> |
| | | 17 | | internal static class ExactContactLever2D |
| | | 18 | | { |
| | | 19 | | internal static bool CanUseCompactResponse( |
| | | 20 | | SolidBody2D? bodyA, |
| | | 21 | | Vector2d linearVelocityA, |
| | | 22 | | Fixed64 angularVelocityA, |
| | | 23 | | Vector2d relativeContactPointA, |
| | | 24 | | SolidBody2D? bodyB, |
| | | 25 | | Vector2d linearVelocityB, |
| | | 26 | | Fixed64 angularVelocityB, |
| | | 27 | | Vector2d relativeContactPointB, |
| | | 28 | | Vector2d axis) |
| | | 29 | | { |
| | 2011 | 30 | | Vector3d spatialAxis = ToSpatial(axis); |
| | 2011 | 31 | | Vector3d leverA = ToSpatial(relativeContactPointA); |
| | 2011 | 32 | | Vector3d leverB = ToSpatial(relativeContactPointB); |
| | 2011 | 33 | | return ContactResponseArithmetic3D.CanUseFastPointVelocity( |
| | 2011 | 34 | | ToSpatial(linearVelocityA), |
| | 2011 | 35 | | new Vector3d( |
| | 2011 | 36 | | Fixed64.Zero, |
| | 2011 | 37 | | -angularVelocityA, |
| | 2011 | 38 | | Fixed64.Zero), |
| | 2011 | 39 | | leverA, |
| | 2011 | 40 | | ToSpatial(linearVelocityB), |
| | 2011 | 41 | | new Vector3d( |
| | 2011 | 42 | | Fixed64.Zero, |
| | 2011 | 43 | | -angularVelocityB, |
| | 2011 | 44 | | Fixed64.Zero), |
| | 2011 | 45 | | leverB, |
| | 2011 | 46 | | spatialAxis) |
| | 2011 | 47 | | && ContactResponseArithmetic3D.CanUseFastAngularResponse( |
| | 2011 | 48 | | leverA, |
| | 2011 | 49 | | spatialAxis, |
| | 2011 | 50 | | CreateInverseInertia(bodyA)) |
| | 2011 | 51 | | && ContactResponseArithmetic3D.CanUseFastAngularResponse( |
| | 2011 | 52 | | leverB, |
| | 2011 | 53 | | spatialAxis, |
| | 2011 | 54 | | CreateInverseInertia(bodyB)); |
| | | 55 | | } |
| | | 56 | | |
| | | 57 | | internal static ExactContactResponseOperand3D CreateResponseOperand( |
| | | 58 | | SolidBody2D? body, |
| | | 59 | | Vector2d linearVelocity, |
| | | 60 | | Fixed64 angularVelocity, |
| | | 61 | | in ExactLever3D lever, |
| | | 62 | | Vector3d signedAxis) => |
| | 460 | 63 | | new( |
| | 460 | 64 | | lever, |
| | 460 | 65 | | ToSpatial(linearVelocity), |
| | 460 | 66 | | new Vector3d( |
| | 460 | 67 | | Fixed64.Zero, |
| | 460 | 68 | | -angularVelocity, |
| | 460 | 69 | | Fixed64.Zero), |
| | 460 | 70 | | body == null |
| | 460 | 71 | | ? Vector3d.Zero |
| | 460 | 72 | | : ToSpatial(body.ProjectLinearMotion(ToPlanar(signedAxis))), |
| | 460 | 73 | | body?.EffectiveInverseMass ?? Fixed64.Zero, |
| | 460 | 74 | | CreateInverseInertia(body)); |
| | | 75 | | |
| | | 76 | | internal static bool TryGetNormalResponse( |
| | | 77 | | SolidBody2D? bodyA, |
| | | 78 | | Vector2d linearVelocityA, |
| | | 79 | | Fixed64 angularVelocityA, |
| | | 80 | | in ExactLever3D relativeContactPointA, |
| | | 81 | | SolidBody2D? bodyB, |
| | | 82 | | Vector2d linearVelocityB, |
| | | 83 | | Fixed64 angularVelocityB, |
| | | 84 | | in ExactLever3D relativeContactPointB, |
| | | 85 | | Vector2d normal, |
| | | 86 | | Fixed64 restitution, |
| | | 87 | | Fixed64 restitutionVelocityThreshold, |
| | | 88 | | out ExactNormalResponse3D response) |
| | | 89 | | { |
| | 26 | 90 | | Vector3d spatialNormal = ToSpatial(normal); |
| | 26 | 91 | | ExactContactResponseOperand3D first = CreateResponseOperand( |
| | 26 | 92 | | bodyA, |
| | 26 | 93 | | linearVelocityA, |
| | 26 | 94 | | angularVelocityA, |
| | 26 | 95 | | relativeContactPointA, |
| | 26 | 96 | | -spatialNormal); |
| | 26 | 97 | | ExactContactResponseOperand3D second = CreateResponseOperand( |
| | 26 | 98 | | bodyB, |
| | 26 | 99 | | linearVelocityB, |
| | 26 | 100 | | angularVelocityB, |
| | 26 | 101 | | relativeContactPointB, |
| | 26 | 102 | | spatialNormal); |
| | 26 | 103 | | return ExactContactResponseKernel.TryGetNormalResponse( |
| | 26 | 104 | | first, |
| | 26 | 105 | | second, |
| | 26 | 106 | | spatialNormal, |
| | 26 | 107 | | restitution, |
| | 26 | 108 | | restitutionVelocityThreshold, |
| | 26 | 109 | | out response); |
| | | 110 | | } |
| | | 111 | | |
| | | 112 | | internal static bool TryGetAccumulatedNormalResponse( |
| | | 113 | | SolidBody2D? bodyA, |
| | | 114 | | Vector2d linearVelocityA, |
| | | 115 | | Fixed64 angularVelocityA, |
| | | 116 | | in ExactLever3D relativeContactPointA, |
| | | 117 | | SolidBody2D? bodyB, |
| | | 118 | | Vector2d linearVelocityB, |
| | | 119 | | Fixed64 angularVelocityB, |
| | | 120 | | in ExactLever3D relativeContactPointB, |
| | | 121 | | Vector2d normal, |
| | | 122 | | Fixed64 restitution, |
| | | 123 | | Fixed64 restitutionVelocityThreshold, |
| | | 124 | | Fixed64 accumulatedImpulse, |
| | | 125 | | Fixed64 positiveImpulseScale, |
| | | 126 | | Fixed64 negativeImpulseScale, |
| | | 127 | | out ExactNormalResponse3D response) |
| | | 128 | | { |
| | 38 | 129 | | Vector3d spatialNormal = ToSpatial(normal); |
| | 38 | 130 | | ExactContactResponseOperand3D first = CreateResponseOperand( |
| | 38 | 131 | | bodyA, |
| | 38 | 132 | | linearVelocityA, |
| | 38 | 133 | | angularVelocityA, |
| | 38 | 134 | | relativeContactPointA, |
| | 38 | 135 | | -spatialNormal); |
| | 38 | 136 | | ExactContactResponseOperand3D second = CreateResponseOperand( |
| | 38 | 137 | | bodyB, |
| | 38 | 138 | | linearVelocityB, |
| | 38 | 139 | | angularVelocityB, |
| | 38 | 140 | | relativeContactPointB, |
| | 38 | 141 | | spatialNormal); |
| | 38 | 142 | | return ExactContactResponseKernel.TryGetAccumulatedNormalResponse( |
| | 38 | 143 | | first, |
| | 38 | 144 | | second, |
| | 38 | 145 | | spatialNormal, |
| | 38 | 146 | | restitution, |
| | 38 | 147 | | restitutionVelocityThreshold, |
| | 38 | 148 | | accumulatedImpulse, |
| | 38 | 149 | | positiveImpulseScale, |
| | 38 | 150 | | negativeImpulseScale, |
| | 38 | 151 | | out response); |
| | | 152 | | } |
| | | 153 | | |
| | | 154 | | internal static Vector3d ToSpatial(Vector2d vector) => |
| | 21145 | 155 | | new(vector.X, Fixed64.Zero, vector.Y); |
| | | 156 | | |
| | | 157 | | internal static Vector2d ToPlanar(Vector3d vector) => |
| | 700 | 158 | | new(vector.X, vector.Z); |
| | | 159 | | |
| | | 160 | | internal static Fixed64 ToPlanarAngular(Vector3d vector) => |
| | 306 | 161 | | -vector.Y; |
| | | 162 | | |
| | | 163 | | internal static bool TryGetImpulseVelocityDeltas( |
| | | 164 | | SolidBody2D? bodyA, |
| | | 165 | | in ExactLever3D relativeContactPointA, |
| | | 166 | | SolidBody2D? bodyB, |
| | | 167 | | in ExactLever3D relativeContactPointB, |
| | | 168 | | Vector2d firstAxis, |
| | | 169 | | Fixed64 firstScale, |
| | | 170 | | Vector2d secondAxis, |
| | | 171 | | Fixed64 secondScale, |
| | | 172 | | out Vector2d linearVelocityDeltaA, |
| | | 173 | | out Fixed64 angularVelocityDeltaA, |
| | | 174 | | out Vector2d linearVelocityDeltaB, |
| | | 175 | | out Fixed64 angularVelocityDeltaB) |
| | | 176 | | { |
| | 3 | 177 | | Vector3d first = ToSpatial(firstAxis); |
| | 3 | 178 | | Vector3d second = ToSpatial(secondAxis); |
| | 3 | 179 | | bool firstResolved = TryGetParticipantVelocityDeltas( |
| | 3 | 180 | | bodyA, |
| | 3 | 181 | | relativeContactPointA, |
| | 3 | 182 | | -first, |
| | 3 | 183 | | firstScale, |
| | 3 | 184 | | -second, |
| | 3 | 185 | | secondScale, |
| | 3 | 186 | | out linearVelocityDeltaA, |
| | 3 | 187 | | out angularVelocityDeltaA); |
| | 3 | 188 | | bool secondResolved = TryGetParticipantVelocityDeltas( |
| | 3 | 189 | | bodyB, |
| | 3 | 190 | | relativeContactPointB, |
| | 3 | 191 | | first, |
| | 3 | 192 | | firstScale, |
| | 3 | 193 | | second, |
| | 3 | 194 | | secondScale, |
| | 3 | 195 | | out linearVelocityDeltaB, |
| | 3 | 196 | | out angularVelocityDeltaB); |
| | 3 | 197 | | return firstResolved & secondResolved; |
| | | 198 | | } |
| | | 199 | | |
| | | 200 | | internal static bool TryGetParticipantVelocityDeltas( |
| | | 201 | | SolidBody2D? body, |
| | | 202 | | in ExactLever3D lever, |
| | | 203 | | Vector3d firstAxis, |
| | | 204 | | Fixed64 firstScale, |
| | | 205 | | Vector3d secondAxis, |
| | | 206 | | Fixed64 secondScale, |
| | | 207 | | out Vector2d linearVelocityDelta, |
| | | 208 | | out Fixed64 angularVelocityDelta) |
| | | 209 | | { |
| | 10 | 210 | | linearVelocityDelta = Vector2d.Zero; |
| | 10 | 211 | | angularVelocityDelta = Fixed64.Zero; |
| | 10 | 212 | | if (body?.HasSolverMobility != true) |
| | 5 | 213 | | return true; |
| | | 214 | | |
| | 5 | 215 | | Vector3d spatialLinear = Vector3d.Zero; |
| | 5 | 216 | | bool linearResolved = !body.CanTranslate |
| | 5 | 217 | | || Vector3d.TryScaledLinearCombination( |
| | 5 | 218 | | ToSpatial(body.ProjectLinearMotion(ToPlanar(firstAxis))), |
| | 5 | 219 | | firstScale, |
| | 5 | 220 | | ToSpatial(body.ProjectLinearMotion(ToPlanar(secondAxis))), |
| | 5 | 221 | | secondScale, |
| | 5 | 222 | | Vector3d.Zero, |
| | 5 | 223 | | Fixed64.Zero, |
| | 5 | 224 | | body.EffectiveInverseMass, |
| | 5 | 225 | | out spatialLinear); |
| | 5 | 226 | | Vector3d spatialAngular = Vector3d.Zero; |
| | 5 | 227 | | bool angularResolved = !body.CanRotate |
| | 5 | 228 | | || ExactLever3D.TryGetTransformedWeightedCrossProduct( |
| | 5 | 229 | | lever, |
| | 5 | 230 | | firstAxis, |
| | 5 | 231 | | firstScale, |
| | 5 | 232 | | secondAxis, |
| | 5 | 233 | | secondScale, |
| | 5 | 234 | | Vector3d.Zero, |
| | 5 | 235 | | Fixed64.Zero, |
| | 5 | 236 | | CreateInverseInertia(body), |
| | 5 | 237 | | out spatialAngular); |
| | 5 | 238 | | linearVelocityDelta = ToPlanar(spatialLinear); |
| | 5 | 239 | | angularVelocityDelta = ToPlanarAngular(spatialAngular); |
| | 5 | 240 | | return linearResolved & angularResolved; |
| | | 241 | | } |
| | | 242 | | |
| | | 243 | | internal static Fixed3x3 CreateInverseInertia(SolidBody2D? body) => |
| | 4805 | 244 | | body?.CanRotate == true |
| | 4805 | 245 | | ? new Fixed3x3( |
| | 4805 | 246 | | Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, |
| | 4805 | 247 | | Fixed64.Zero, body.EffectiveInverseMomentOfInertia, Fixed64.Zero, |
| | 4805 | 248 | | Fixed64.Zero, Fixed64.Zero, Fixed64.Zero) |
| | 4805 | 249 | | : Fixed3x3.Zero; |
| | | 250 | | } |