| | | 1 | | //======================================================================= |
| | | 2 | | // GravitasConstraint2DService.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 Chronicler; |
| | | 9 | | using FixedMathSharp; |
| | | 10 | | using Gravitas.Colliders; |
| | | 11 | | using Gravitas.Constraints; |
| | | 12 | | using SwiftCollections; |
| | | 13 | | using System; |
| | | 14 | | |
| | | 15 | | namespace Gravitas; |
| | | 16 | | |
| | | 17 | | /// <summary> |
| | | 18 | | /// Owns deterministic pure 2D joints and ragdoll articulation state for one world context. |
| | | 19 | | /// </summary> |
| | | 20 | | public sealed class GravitasConstraint2DService |
| | | 21 | | { |
| | | 22 | | private const int DefaultJointCapacity = 64; |
| | | 23 | | |
| | | 24 | | private readonly GravitasWorldContext _context; |
| | 5083 | 25 | | private readonly ConstraintEndpointJointIndex<SolidBody2D> _jointIdsByBody = new(); |
| | 5083 | 26 | | private readonly SwiftDictionary<SolidBody2D, RagdollRuntime2D> _ragdollByBody = new(); |
| | 5083 | 27 | | private readonly SwiftDictionary<int, RagdollRuntime2D> _ragdollsById = new(); |
| | 5083 | 28 | | private readonly SwiftDictionary<ulong, int> _suppressedColliderPairs = new(); |
| | 5083 | 29 | | private Joint2D?[] _joints = new Joint2D?[DefaultJointCapacity]; |
| | | 30 | | private RagdollRuntime2D? _firstRagdoll; |
| | | 31 | | private RagdollRuntime2D? _lastRagdoll; |
| | | 32 | | private int _nextRagdollId; |
| | | 33 | | private int _registeredRagdollCount; |
| | | 34 | | private int _enabledJointCount; |
| | | 35 | | |
| | 5083 | 36 | | internal GravitasConstraint2DService(GravitasWorldContext context) |
| | | 37 | | { |
| | 5083 | 38 | | SwiftThrowHelper.ThrowIfNull(context, nameof(context)); |
| | 5083 | 39 | | _context = context; |
| | 5083 | 40 | | } |
| | | 41 | | |
| | | 42 | | /// <summary> |
| | | 43 | | /// Gets the owning world context. |
| | | 44 | | /// </summary> |
| | 195 | 45 | | public GravitasWorldContext Context => _context; |
| | | 46 | | |
| | | 47 | | /// <summary> |
| | | 48 | | /// Gets the highest joint ID allocated in this context. |
| | | 49 | | /// </summary> |
| | | 50 | | public int PeakJointCount { get; private set; } |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// Gets the number of registered active joints. |
| | | 54 | | /// </summary> |
| | | 55 | | public int RegisteredJointCount { get; private set; } |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// Gets the number of registered ragdoll runtimes. |
| | | 59 | | /// </summary> |
| | 11 | 60 | | public int RegisteredRagdollCount => _registeredRagdollCount; |
| | | 61 | | |
| | 21 | 62 | | internal int EnabledJointCount => _enabledJointCount; |
| | | 63 | | |
| | 1003 | 64 | | internal bool HasActiveJoints => _enabledJointCount > 0; |
| | | 65 | | |
| | | 66 | | internal void ClearSolverCachesForBody(SolidBody2D body) |
| | | 67 | | { |
| | 78 | 68 | | if (!_jointIdsByBody.TryGetLast(body, out int jointId)) |
| | 63 | 69 | | return; |
| | | 70 | | |
| | 1 | 71 | | while (true) |
| | | 72 | | { |
| | 16 | 73 | | _joints[jointId]!.ClearSolverCache(); |
| | 16 | 74 | | if (!_jointIdsByBody.TryGetPrevious(body, jointId, out int previousJointId)) |
| | 15 | 75 | | return; |
| | | 76 | | |
| | 1 | 77 | | jointId = previousJointId; |
| | | 78 | | } |
| | | 79 | | } |
| | | 80 | | |
| | | 81 | | /// <summary> |
| | | 82 | | /// Registers a context-owned deterministic pure 2D joint. |
| | | 83 | | /// </summary> |
| | | 84 | | public Joint2D RegisterJoint(in JointDefinition2D definition) |
| | | 85 | | { |
| | 203 | 86 | | _context.ThrowIfDisposed(); |
| | 202 | 87 | | ValidateDefinition(definition); |
| | | 88 | | |
| | 195 | 89 | | int id = ++PeakJointCount; |
| | 195 | 90 | | EnsureJointCapacity(id + 1); |
| | 195 | 91 | | var joint = new Joint2D(this, id, definition); |
| | 195 | 92 | | _joints[id] = joint; |
| | 195 | 93 | | _jointIdsByBody.Add(joint.BodyA, joint.BodyB, id); |
| | 195 | 94 | | RegisteredJointCount++; |
| | 195 | 95 | | _enabledJointCount++; |
| | | 96 | | |
| | 195 | 97 | | if (joint.CollisionPolicy == JointCollisionPolicy.SuppressLinked) |
| | 169 | 98 | | AddSuppressedColliderPair(joint.BodyA.Collider, joint.BodyB.Collider); |
| | | 99 | | |
| | 195 | 100 | | _context.Diagnostics.EmitJointRegistered(joint); |
| | 195 | 101 | | return joint; |
| | | 102 | | } |
| | | 103 | | |
| | | 104 | | /// <summary> |
| | | 105 | | /// Removes a context-owned pure 2D joint and releases its filter state. |
| | | 106 | | /// </summary> |
| | | 107 | | public bool RemoveJoint(int jointId) |
| | | 108 | | { |
| | 13 | 109 | | _context.ThrowIfDisposed(); |
| | 12 | 110 | | if (!TryGetJoint(jointId, out Joint2D? joint)) |
| | 1 | 111 | | return false; |
| | | 112 | | |
| | 11 | 113 | | SwiftThrowHelper.ThrowIfTrue( |
| | 11 | 114 | | joint!.OwningRagdoll?.IsRegistered == true, |
| | 11 | 115 | | nameof(jointId), |
| | 11 | 116 | | "Ragdoll-owned joints cannot be removed independently. Remove the owning ragdoll instead."); |
| | 10 | 117 | | return RemoveJointCore(joint); |
| | | 118 | | } |
| | | 119 | | |
| | | 120 | | private bool RemoveJointCore(Joint2D joint) |
| | | 121 | | { |
| | 28 | 122 | | int jointId = joint.Id; |
| | 28 | 123 | | _joints[jointId] = null; |
| | 28 | 124 | | _jointIdsByBody.Remove(jointId); |
| | 28 | 125 | | RegisteredJointCount--; |
| | 28 | 126 | | if (joint.IsEnabled) |
| | 27 | 127 | | _enabledJointCount--; |
| | 28 | 128 | | joint.IsActive = false; |
| | 28 | 129 | | joint.ClearSolverCache(); |
| | 28 | 130 | | if (joint.CollisionPolicy == JointCollisionPolicy.SuppressLinked) |
| | 27 | 131 | | RemoveSuppressedColliderPair(joint.BodyA.Collider, joint.BodyB.Collider); |
| | | 132 | | |
| | 28 | 133 | | _context.Diagnostics.EmitJointRemoved(joint); |
| | 28 | 134 | | return true; |
| | | 135 | | } |
| | | 136 | | |
| | | 137 | | /// <summary> |
| | | 138 | | /// Gets a registered pure 2D joint by context-local ID. |
| | | 139 | | /// </summary> |
| | | 140 | | public Joint2D GetJoint(int jointId) |
| | | 141 | | { |
| | 21 | 142 | | if (!TryGetJoint(jointId, out Joint2D? joint)) |
| | 1 | 143 | | throw new ArgumentOutOfRangeException(nameof(jointId), "No active 2D joint exists with the supplied ID."); |
| | | 144 | | |
| | 20 | 145 | | return joint!; |
| | | 146 | | } |
| | | 147 | | |
| | | 148 | | /// <summary> |
| | | 149 | | /// Tries to get a registered pure 2D joint by context-local ID. |
| | | 150 | | /// </summary> |
| | | 151 | | public bool TryGetJoint(int jointId, out Joint2D? joint) |
| | | 152 | | { |
| | 657 | 153 | | if ((uint)jointId >= (uint)_joints.Length) |
| | | 154 | | { |
| | 4 | 155 | | joint = null; |
| | 4 | 156 | | return false; |
| | | 157 | | } |
| | | 158 | | |
| | 653 | 159 | | joint = _joints[jointId]; |
| | 653 | 160 | | return joint != null && joint.IsActive; |
| | | 161 | | } |
| | | 162 | | |
| | | 163 | | /// <summary> |
| | | 164 | | /// Registers a pure 2D ragdoll articulation and its owned joints. |
| | | 165 | | /// </summary> |
| | | 166 | | public RagdollRuntime2D RegisterRagdoll(RagdollDefinition2D definition) |
| | | 167 | | { |
| | 34 | 168 | | _context.ThrowIfDisposed(); |
| | 34 | 169 | | ValidateRagdollDefinition(definition); |
| | | 170 | | |
| | 30 | 171 | | int linkCount = definition.Links.Length; |
| | 30 | 172 | | var links = new SolidBody2D[linkCount]; |
| | 168 | 173 | | for (int i = 0; i < linkCount; i++) |
| | 54 | 174 | | links[i] = definition.Links[i].Body; |
| | | 175 | | |
| | 30 | 176 | | int jointCount = definition.Joints.Length; |
| | 30 | 177 | | var joints = new Joint2D[jointCount]; |
| | 108 | 178 | | for (int i = 0; i < jointCount; i++) |
| | | 179 | | { |
| | 24 | 180 | | RagdollJointDefinition2D authoredJoint = definition.Joints[i]; |
| | 24 | 181 | | SolidBody2D bodyA = ResolveRagdollLink(definition.Links, authoredJoint.LinkAId); |
| | 24 | 182 | | SolidBody2D bodyB = ResolveRagdollLink(definition.Links, authoredJoint.LinkBId); |
| | 24 | 183 | | JointCollisionPolicy collisionPolicy = definition.SelfCollisionPolicy == RagdollSelfCollisionPolicy.CollideA |
| | 24 | 184 | | ? JointCollisionPolicy.Collide |
| | 24 | 185 | | : authoredJoint.CollisionPolicy; |
| | | 186 | | |
| | 24 | 187 | | joints[i] = RegisterJoint(new JointDefinition2D( |
| | 24 | 188 | | bodyA, |
| | 24 | 189 | | bodyB, |
| | 24 | 190 | | authoredJoint.LocalFrameA, |
| | 24 | 191 | | authoredJoint.LocalFrameB, |
| | 24 | 192 | | authoredJoint.Type, |
| | 24 | 193 | | authoredJoint.Limits, |
| | 24 | 194 | | authoredJoint.Motor, |
| | 24 | 195 | | collisionPolicy)); |
| | | 196 | | } |
| | | 197 | | |
| | 30 | 198 | | if (definition.SelfCollisionPolicy == RagdollSelfCollisionPolicy.SuppressAllLinks) |
| | 2 | 199 | | AddAllRagdollPairSuppressions(links); |
| | | 200 | | |
| | 30 | 201 | | var runtime = new RagdollRuntime2D( |
| | 30 | 202 | | this, |
| | 30 | 203 | | ++_nextRagdollId, |
| | 30 | 204 | | links, |
| | 30 | 205 | | joints, |
| | 30 | 206 | | definition.SelfCollisionPolicy, |
| | 30 | 207 | | startsActive: !HasKinematicLink(links)); |
| | 30 | 208 | | AppendRagdoll(runtime); |
| | 30 | 209 | | _ragdollsById.Add(runtime.Id, runtime); |
| | 168 | 210 | | for (int i = 0; i < links.Length; i++) |
| | 54 | 211 | | _ragdollByBody.Add(links[i], runtime); |
| | 108 | 212 | | for (int i = 0; i < joints.Length; i++) |
| | 24 | 213 | | joints[i].OwningRagdoll = runtime; |
| | 30 | 214 | | return runtime; |
| | | 215 | | } |
| | | 216 | | |
| | | 217 | | /// <summary> |
| | | 218 | | /// Removes a registered pure 2D ragdoll and every joint and collision suppression it owns. |
| | | 219 | | /// </summary> |
| | | 220 | | public bool RemoveRagdoll(int ragdollId) |
| | | 221 | | { |
| | 9 | 222 | | _context.ThrowIfDisposed(); |
| | 9 | 223 | | if (!_ragdollsById.TryGetValue(ragdollId, out RagdollRuntime2D? ragdoll)) |
| | 2 | 224 | | return false; |
| | | 225 | | |
| | 7 | 226 | | RemoveRagdollCore(ragdoll); |
| | 7 | 227 | | return true; |
| | | 228 | | } |
| | | 229 | | |
| | | 230 | | /// <summary> |
| | | 231 | | /// Updates a pure 2D joint motor target while preserving the motor's gains and impulse cap. |
| | | 232 | | /// </summary> |
| | | 233 | | public bool SetJointMotorTarget(int jointId, Fixed64 target) |
| | | 234 | | { |
| | 3 | 235 | | _context.ThrowIfDisposed(); |
| | 3 | 236 | | if (!TryGetJoint(jointId, out Joint2D? joint)) |
| | 1 | 237 | | return false; |
| | | 238 | | |
| | 2 | 239 | | JointMotor2D current = joint!.Motor; |
| | 2 | 240 | | JointMotor2D replacement = current.Kind == JointMotorKind2D.Linear |
| | 2 | 241 | | ? JointMotor2D.Linear(target, current.DriveStrength, current.Damping, current.MaximumMotorImpulse) |
| | 2 | 242 | | : JointMotor2D.Angular(target, current.DriveStrength, current.Damping, current.MaximumMotorImpulse); |
| | 2 | 243 | | joint.SetMotor(replacement); |
| | 2 | 244 | | return true; |
| | | 245 | | } |
| | | 246 | | |
| | | 247 | | /// <summary> |
| | | 248 | | /// Disables a pure 2D joint motor target and clears its cached solver impulses. |
| | | 249 | | /// </summary> |
| | | 250 | | public bool ClearJointMotorTarget(int jointId) |
| | | 251 | | { |
| | 2 | 252 | | _context.ThrowIfDisposed(); |
| | 2 | 253 | | if (!TryGetJoint(jointId, out Joint2D? joint)) |
| | 1 | 254 | | return false; |
| | | 255 | | |
| | 1 | 256 | | joint!.ClearMotor(); |
| | 1 | 257 | | return true; |
| | | 258 | | } |
| | | 259 | | |
| | | 260 | | /// <summary> |
| | | 261 | | /// Applies caller-owned motor payloads to every joint in a pure 2D ragdoll runtime. |
| | | 262 | | /// </summary> |
| | | 263 | | public void SetRagdollPoseTargets(RagdollRuntime2D ragdoll, ReadOnlySpan<JointMotor2D> motors) |
| | | 264 | | { |
| | 4 | 265 | | _context.ThrowIfDisposed(); |
| | 4 | 266 | | SwiftThrowHelper.ThrowIfNull(ragdoll, nameof(ragdoll)); |
| | 4 | 267 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4 | 268 | | !ReferenceEquals(ragdoll.Service, this), |
| | 4 | 269 | | nameof(ragdoll), |
| | 4 | 270 | | "Ragdoll targets must be applied through the owning 2D constraint service."); |
| | 3 | 271 | | SwiftThrowHelper.ThrowIfTrue( |
| | 3 | 272 | | !ragdoll.IsRegistered, |
| | 3 | 273 | | nameof(ragdoll), |
| | 3 | 274 | | "Removed ragdolls cannot mutate simulation state."); |
| | 1 | 275 | | SwiftThrowHelper.ThrowIfArgument( |
| | 1 | 276 | | motors.Length != ragdoll.JointCount, |
| | 1 | 277 | | nameof(motors), |
| | 1 | 278 | | "Ragdoll pose target count must match the ragdoll joint count."); |
| | | 279 | | |
| | 4 | 280 | | for (int i = 0; i < motors.Length; i++) |
| | | 281 | | { |
| | 1 | 282 | | Joint2D joint = ragdoll.GetJoint(i); |
| | 1 | 283 | | joint.SetMotor(motors[i]); |
| | | 284 | | } |
| | 1 | 285 | | } |
| | | 286 | | |
| | | 287 | | /// <summary> |
| | | 288 | | /// Gets whether the supplied pure 2D colliders should be excluded because a registered articulation links them. |
| | | 289 | | /// </summary> |
| | | 290 | | public bool ShouldExcludeLinkedCollision(LSCollider2D colliderA, LSCollider2D colliderB) |
| | | 291 | | { |
| | 8798 | 292 | | if (colliderA == null || colliderB == null) |
| | 2 | 293 | | return false; |
| | 8796 | 294 | | if (colliderA.Id < 0 || colliderB.Id < 0 || colliderA.Id == colliderB.Id) |
| | 4 | 295 | | return false; |
| | | 296 | | |
| | 8792 | 297 | | return _suppressedColliderPairs.ContainsKey(CreateColliderPairKey(colliderA.Id, colliderB.Id)); |
| | | 298 | | } |
| | | 299 | | |
| | | 300 | | internal bool TryGetJointForSolver(int jointId, out Joint2D? joint) |
| | | 301 | | { |
| | 601 | 302 | | if (!TryGetJoint(jointId, out joint)) |
| | 12 | 303 | | return false; |
| | | 304 | | |
| | 589 | 305 | | if (!joint!.IsEnabled || !joint.HasSolverParticipant()) |
| | | 306 | | { |
| | 3 | 307 | | joint = null; |
| | 3 | 308 | | return false; |
| | | 309 | | } |
| | | 310 | | |
| | 586 | 311 | | return true; |
| | | 312 | | } |
| | | 313 | | |
| | | 314 | | internal void RemoveJointsForBody(SolidBody2D body) |
| | | 315 | | { |
| | 123 | 316 | | if (_ragdollByBody.TryGetValue(body, out RagdollRuntime2D? ragdoll)) |
| | 3 | 317 | | RemoveRagdollCore(ragdoll); |
| | | 318 | | |
| | 135 | 319 | | while (_jointIdsByBody.TryGetLast(body, out int jointId)) |
| | 12 | 320 | | RemoveJointCore(GetJoint(jointId)); |
| | 123 | 321 | | } |
| | | 322 | | |
| | | 323 | | internal void UpdateJointCollisionPolicy( |
| | | 324 | | Joint2D joint, |
| | | 325 | | JointCollisionPolicy oldPolicy, |
| | | 326 | | JointCollisionPolicy newPolicy) |
| | | 327 | | { |
| | 10 | 328 | | if (!joint.IsActive || oldPolicy == newPolicy) |
| | 3 | 329 | | return; |
| | | 330 | | |
| | 7 | 331 | | if (oldPolicy == JointCollisionPolicy.SuppressLinked) |
| | 6 | 332 | | RemoveSuppressedColliderPair(joint.BodyA.Collider, joint.BodyB.Collider); |
| | 7 | 333 | | if (newPolicy == JointCollisionPolicy.SuppressLinked) |
| | 1 | 334 | | AddSuppressedColliderPair(joint.BodyA.Collider, joint.BodyB.Collider); |
| | 7 | 335 | | } |
| | | 336 | | |
| | | 337 | | internal void UpdateJointEnabledState( |
| | | 338 | | Joint2D joint, |
| | | 339 | | bool oldEnabled, |
| | | 340 | | bool newEnabled) |
| | | 341 | | { |
| | 19 | 342 | | if (!joint.IsActive || oldEnabled == newEnabled) |
| | 1 | 343 | | return; |
| | | 344 | | |
| | 18 | 345 | | _enabledJointCount += newEnabled ? 1 : -1; |
| | 18 | 346 | | } |
| | | 347 | | |
| | | 348 | | internal void Reset() |
| | | 349 | | { |
| | 5114 | 350 | | RagdollRuntime2D? ragdoll = _firstRagdoll; |
| | 5134 | 351 | | while (ragdoll != null) |
| | | 352 | | { |
| | 20 | 353 | | RagdollRuntime2D? next = ragdoll.NextRegistered; |
| | 20 | 354 | | ragdoll.Invalidate(); |
| | 20 | 355 | | ragdoll = next; |
| | | 356 | | } |
| | | 357 | | |
| | 10618 | 358 | | for (int i = 1; i <= PeakJointCount && i < _joints.Length; i++) |
| | | 359 | | { |
| | 195 | 360 | | Joint2D? joint = _joints[i]; |
| | 195 | 361 | | if (joint == null) |
| | | 362 | | continue; |
| | | 363 | | |
| | 167 | 364 | | joint.IsActive = false; |
| | 167 | 365 | | joint.ClearSolverCache(); |
| | 167 | 366 | | joint.OwningRagdoll = null; |
| | 167 | 367 | | _joints[i] = null; |
| | | 368 | | } |
| | | 369 | | |
| | 5114 | 370 | | _suppressedColliderPairs.Clear(); |
| | 5114 | 371 | | _jointIdsByBody.Clear(); |
| | 5114 | 372 | | _ragdollByBody.Clear(); |
| | 5114 | 373 | | _ragdollsById.Clear(); |
| | 5114 | 374 | | _firstRagdoll = null; |
| | 5114 | 375 | | _lastRagdoll = null; |
| | 5114 | 376 | | PeakJointCount = 0; |
| | 5114 | 377 | | RegisteredJointCount = 0; |
| | 5114 | 378 | | _registeredRagdollCount = 0; |
| | 5114 | 379 | | _enabledJointCount = 0; |
| | 5114 | 380 | | _nextRagdollId = 0; |
| | 5114 | 381 | | } |
| | | 382 | | |
| | | 383 | | internal void ContributeReplayHash( |
| | | 384 | | ref ChronicleHashWriter writer, |
| | | 385 | | GravitasReplayHashMode mode) |
| | | 386 | | { |
| | 997 | 387 | | writer.WriteSection("constraints.2d", 1); |
| | 997 | 388 | | writer.WriteInt32(PeakJointCount); |
| | 997 | 389 | | writer.WriteInt32(RegisteredJointCount); |
| | 2022 | 390 | | for (int jointId = 1; jointId <= PeakJointCount; jointId++) |
| | | 391 | | { |
| | 14 | 392 | | bool hasJoint = TryGetJoint(jointId, out Joint2D? joint); |
| | 14 | 393 | | writer.WriteBool(hasJoint); |
| | 14 | 394 | | if (hasJoint) |
| | 7 | 395 | | joint!.ContributeReplayHash(ref writer, mode); |
| | | 396 | | } |
| | | 397 | | |
| | 997 | 398 | | writer.WriteInt32(_registeredRagdollCount); |
| | 997 | 399 | | for (RagdollRuntime2D? ragdoll = _firstRagdoll; |
| | 1007 | 400 | | ragdoll != null; |
| | 10 | 401 | | ragdoll = ragdoll.NextRegistered) |
| | | 402 | | { |
| | 10 | 403 | | writer.WriteInt32(ragdoll.Id); |
| | 10 | 404 | | writer.WriteEnum(ragdoll.SelfCollisionPolicy); |
| | 10 | 405 | | writer.WriteBool(ragdoll.IsActive); |
| | 10 | 406 | | writer.WriteInt32(ragdoll.LinkCount); |
| | 10 | 407 | | writer.WriteInt32(ragdoll.JointCount); |
| | | 408 | | } |
| | 997 | 409 | | } |
| | | 410 | | |
| | | 411 | | internal static ulong CreateColliderPairKey(int colliderAId, int colliderBId) |
| | | 412 | | { |
| | 9004 | 413 | | uint min = (uint)(colliderAId <= colliderBId ? colliderAId : colliderBId); |
| | 9004 | 414 | | uint max = (uint)(colliderAId <= colliderBId ? colliderBId : colliderAId); |
| | 9004 | 415 | | return ((ulong)min << 32) | max; |
| | | 416 | | } |
| | | 417 | | |
| | | 418 | | private void EnsureJointCapacity(int required) |
| | | 419 | | { |
| | 195 | 420 | | if (required <= _joints.Length) |
| | 194 | 421 | | return; |
| | | 422 | | |
| | 1 | 423 | | int newSize = _joints.Length; |
| | 2 | 424 | | while (newSize < required) |
| | 1 | 425 | | newSize *= 2; |
| | | 426 | | |
| | 1 | 427 | | Array.Resize(ref _joints, newSize); |
| | 1 | 428 | | } |
| | | 429 | | |
| | | 430 | | private void ValidateDefinition(in JointDefinition2D definition) |
| | | 431 | | { |
| | 228 | 432 | | SwiftThrowHelper.ThrowIfNull(definition.BodyA, nameof(definition.BodyA)); |
| | 228 | 433 | | SwiftThrowHelper.ThrowIfNull(definition.BodyB, nameof(definition.BodyB)); |
| | 228 | 434 | | SwiftThrowHelper.ThrowIfArgument( |
| | 228 | 435 | | ReferenceEquals(definition.BodyA, definition.BodyB), |
| | 228 | 436 | | nameof(definition), |
| | 228 | 437 | | "A 2D joint cannot link a body to itself."); |
| | 227 | 438 | | SwiftThrowHelper.ThrowIfArgument( |
| | 227 | 439 | | !ReferenceEquals(definition.BodyA.Context, _context) || !ReferenceEquals(definition.BodyB.Context, _context) |
| | 227 | 440 | | nameof(definition), |
| | 227 | 441 | | "Both 2D joint bodies must belong to this constraint service context."); |
| | 225 | 442 | | SwiftThrowHelper.ThrowIfArgument( |
| | 225 | 443 | | !definition.BodyA.Active || !definition.BodyB.Active, |
| | 225 | 444 | | nameof(definition), |
| | 225 | 445 | | "2D joint bodies must be active before registration."); |
| | 223 | 446 | | SwiftThrowHelper.ThrowIfArgument( |
| | 223 | 447 | | !Joint2D.IsSupportedType(definition.Type), |
| | 223 | 448 | | nameof(definition.Type), |
| | 223 | 449 | | "Unsupported 2D joint type."); |
| | 222 | 450 | | SwiftThrowHelper.ThrowIfArgument( |
| | 222 | 451 | | definition.CollisionPolicy != JointCollisionPolicy.SuppressLinked |
| | 222 | 452 | | && definition.CollisionPolicy != JointCollisionPolicy.Collide, |
| | 222 | 453 | | nameof(definition.CollisionPolicy), |
| | 222 | 454 | | "Unsupported joint collision policy."); |
| | 221 | 455 | | definition.Limits.Validate(); |
| | 221 | 456 | | definition.Motor.Validate(); |
| | 221 | 457 | | Joint2D.ValidatePayload(definition.Type, definition.Limits, definition.Motor); |
| | 220 | 458 | | } |
| | | 459 | | |
| | | 460 | | private void ValidateRagdollDefinition(RagdollDefinition2D definition) |
| | | 461 | | { |
| | 34 | 462 | | SwiftThrowHelper.ThrowIfNull(definition, nameof(definition)); |
| | 34 | 463 | | SwiftThrowHelper.ThrowIfNull(definition.Links, nameof(definition.Links)); |
| | 34 | 464 | | SwiftThrowHelper.ThrowIfNull(definition.Joints, nameof(definition.Joints)); |
| | 34 | 465 | | SwiftThrowHelper.ThrowIfArgument(definition.Links.Length == 0, nameof(definition.Links), "A 2D ragdoll must cont |
| | 34 | 466 | | SwiftThrowHelper.ThrowIfArgument( |
| | 34 | 467 | | definition.SelfCollisionPolicy != RagdollSelfCollisionPolicy.SuppressAdjacentLinks |
| | 34 | 468 | | && definition.SelfCollisionPolicy != RagdollSelfCollisionPolicy.CollideAllLinks |
| | 34 | 469 | | && definition.SelfCollisionPolicy != RagdollSelfCollisionPolicy.SuppressAllLinks, |
| | 34 | 470 | | nameof(definition.SelfCollisionPolicy), |
| | 34 | 471 | | "Unsupported ragdoll self-collision policy."); |
| | | 472 | | |
| | 186 | 473 | | for (int i = 0; i < definition.Links.Length; i++) |
| | | 474 | | { |
| | 61 | 475 | | RagdollLinkDefinition2D link = definition.Links[i]; |
| | 61 | 476 | | SwiftThrowHelper.ThrowIfNull(link.Body, nameof(definition.Links)); |
| | 61 | 477 | | SwiftThrowHelper.ThrowIfNull(link.Collider, nameof(definition.Links)); |
| | 61 | 478 | | SwiftThrowHelper.ThrowIfArgument( |
| | 61 | 479 | | !ReferenceEquals(link.Body.Context, _context), |
| | 61 | 480 | | nameof(definition.Links), |
| | 61 | 481 | | "All 2D ragdoll links must belong to this context."); |
| | 61 | 482 | | SwiftThrowHelper.ThrowIfArgument( |
| | 61 | 483 | | !link.Body.Active, |
| | 61 | 484 | | nameof(definition.Links), |
| | 61 | 485 | | "2D ragdoll links must have active 2D colliders."); |
| | 61 | 486 | | SwiftThrowHelper.ThrowIfArgument( |
| | 61 | 487 | | !ReferenceEquals(link.Body.Collider, link.Collider), |
| | 61 | 488 | | nameof(definition.Links), |
| | 61 | 489 | | "2D ragdoll link collider must match the link body's collider."); |
| | 61 | 490 | | SwiftThrowHelper.ThrowIfArgument( |
| | 61 | 491 | | _ragdollByBody.ContainsKey(link.Body), |
| | 61 | 492 | | nameof(definition.Links), |
| | 61 | 493 | | "A 2D body cannot belong to more than one registered ragdoll."); |
| | | 494 | | |
| | 184 | 495 | | for (int j = i + 1; j < definition.Links.Length; j++) |
| | | 496 | | { |
| | 33 | 497 | | SwiftThrowHelper.ThrowIfArgument( |
| | 33 | 498 | | link.LinkId == definition.Links[j].LinkId, |
| | 33 | 499 | | nameof(definition.Links), |
| | 33 | 500 | | "2D ragdoll link IDs must be unique."); |
| | 33 | 501 | | SwiftThrowHelper.ThrowIfArgument( |
| | 33 | 502 | | ReferenceEquals(link.Body, definition.Links[j].Body), |
| | 33 | 503 | | nameof(definition.Links), |
| | 33 | 504 | | "2D ragdoll bodies must be unique."); |
| | | 505 | | } |
| | | 506 | | } |
| | | 507 | | |
| | 114 | 508 | | for (int i = 0; i < definition.Joints.Length; i++) |
| | | 509 | | { |
| | 27 | 510 | | RagdollJointDefinition2D joint = definition.Joints[i]; |
| | 27 | 511 | | SolidBody2D bodyA = ResolveRagdollLink(definition.Links, joint.LinkAId); |
| | 27 | 512 | | SolidBody2D bodyB = ResolveRagdollLink(definition.Links, joint.LinkBId); |
| | 26 | 513 | | SwiftThrowHelper.ThrowIfArgument( |
| | 26 | 514 | | joint.LinkAId == joint.LinkBId, |
| | 26 | 515 | | nameof(definition.Joints), |
| | 26 | 516 | | "A 2D ragdoll joint cannot link a body to itself."); |
| | 26 | 517 | | JointCollisionPolicy collisionPolicy = definition.SelfCollisionPolicy == RagdollSelfCollisionPolicy.CollideA |
| | 26 | 518 | | ? JointCollisionPolicy.Collide |
| | 26 | 519 | | : joint.CollisionPolicy; |
| | 26 | 520 | | ValidateDefinition(new JointDefinition2D( |
| | 26 | 521 | | bodyA, |
| | 26 | 522 | | bodyB, |
| | 26 | 523 | | joint.LocalFrameA, |
| | 26 | 524 | | joint.LocalFrameB, |
| | 26 | 525 | | joint.Type, |
| | 26 | 526 | | joint.Limits, |
| | 26 | 527 | | joint.Motor, |
| | 26 | 528 | | collisionPolicy)); |
| | | 529 | | } |
| | 30 | 530 | | } |
| | | 531 | | |
| | | 532 | | private static bool HasKinematicLink(SolidBody2D[] links) |
| | | 533 | | { |
| | 154 | 534 | | for (int i = 0; i < links.Length; i++) |
| | | 535 | | { |
| | 51 | 536 | | if (links[i].IsKinematic) |
| | 4 | 537 | | return true; |
| | | 538 | | } |
| | | 539 | | |
| | 26 | 540 | | return false; |
| | | 541 | | } |
| | | 542 | | |
| | | 543 | | private static SolidBody2D ResolveRagdollLink(RagdollLinkDefinition2D[] links, int linkId) |
| | | 544 | | { |
| | 344 | 545 | | for (int i = 0; i < links.Length; i++) |
| | | 546 | | { |
| | 171 | 547 | | if (links[i].LinkId == linkId) |
| | 101 | 548 | | return links[i].Body; |
| | | 549 | | } |
| | | 550 | | |
| | 1 | 551 | | throw new ArgumentException("2D ragdoll joint references an unknown link ID.", nameof(linkId)); |
| | | 552 | | } |
| | | 553 | | |
| | | 554 | | private void AddAllRagdollPairSuppressions(SolidBody2D[] links) |
| | | 555 | | { |
| | 16 | 556 | | for (int i = 0; i < links.Length; i++) |
| | | 557 | | { |
| | 24 | 558 | | for (int j = i + 1; j < links.Length; j++) |
| | 6 | 559 | | AddSuppressedColliderPair(links[i].Collider, links[j].Collider); |
| | | 560 | | } |
| | 2 | 561 | | } |
| | | 562 | | |
| | | 563 | | private void RemoveRagdollCore(RagdollRuntime2D ragdoll) |
| | | 564 | | { |
| | 10 | 565 | | _ragdollsById.Remove(ragdoll.Id); |
| | 52 | 566 | | for (int i = 0; i < ragdoll.LinkCount; i++) |
| | 16 | 567 | | _ragdollByBody.Remove(ragdoll.GetLink(i)); |
| | | 568 | | |
| | 10 | 569 | | if (ragdoll.SelfCollisionPolicy == RagdollSelfCollisionPolicy.SuppressAllLinks) |
| | | 570 | | { |
| | 8 | 571 | | for (int i = 0; i < ragdoll.LinkCount; i++) |
| | | 572 | | { |
| | 12 | 573 | | for (int j = i + 1; j < ragdoll.LinkCount; j++) |
| | | 574 | | { |
| | 3 | 575 | | RemoveSuppressedColliderPair( |
| | 3 | 576 | | ragdoll.GetLink(i).Collider, |
| | 3 | 577 | | ragdoll.GetLink(j).Collider); |
| | | 578 | | } |
| | | 579 | | } |
| | | 580 | | } |
| | | 581 | | |
| | 10 | 582 | | UnlinkRagdoll(ragdoll); |
| | 10 | 583 | | ragdoll.Invalidate(); |
| | | 584 | | |
| | 32 | 585 | | for (int i = 0; i < ragdoll.JointCount; i++) |
| | | 586 | | { |
| | 6 | 587 | | Joint2D joint = ragdoll.GetJoint(i); |
| | 6 | 588 | | joint.OwningRagdoll = null; |
| | 6 | 589 | | RemoveJointCore(joint); |
| | | 590 | | } |
| | 10 | 591 | | } |
| | | 592 | | |
| | | 593 | | private void AppendRagdoll(RagdollRuntime2D ragdoll) |
| | | 594 | | { |
| | 30 | 595 | | ragdoll.PreviousRegistered = _lastRagdoll; |
| | 30 | 596 | | if (_lastRagdoll == null) |
| | 23 | 597 | | _firstRagdoll = ragdoll; |
| | | 598 | | else |
| | 7 | 599 | | _lastRagdoll.NextRegistered = ragdoll; |
| | | 600 | | |
| | 30 | 601 | | _lastRagdoll = ragdoll; |
| | 30 | 602 | | _registeredRagdollCount++; |
| | 30 | 603 | | } |
| | | 604 | | |
| | | 605 | | private void UnlinkRagdoll(RagdollRuntime2D ragdoll) |
| | | 606 | | { |
| | 10 | 607 | | if (ragdoll.PreviousRegistered == null) |
| | 9 | 608 | | _firstRagdoll = ragdoll.NextRegistered; |
| | | 609 | | else |
| | 1 | 610 | | ragdoll.PreviousRegistered.NextRegistered = ragdoll.NextRegistered; |
| | | 611 | | |
| | 10 | 612 | | if (ragdoll.NextRegistered == null) |
| | 5 | 613 | | _lastRagdoll = ragdoll.PreviousRegistered; |
| | | 614 | | else |
| | 5 | 615 | | ragdoll.NextRegistered.PreviousRegistered = ragdoll.PreviousRegistered; |
| | | 616 | | |
| | 10 | 617 | | _registeredRagdollCount--; |
| | 10 | 618 | | } |
| | | 619 | | |
| | | 620 | | private void AddSuppressedColliderPair(LSCollider2D colliderA, LSCollider2D colliderB) |
| | | 621 | | { |
| | 176 | 622 | | ulong key = CreateColliderPairKey(colliderA.Id, colliderB.Id); |
| | 176 | 623 | | if (_suppressedColliderPairs.TryGetValue(key, out int count)) |
| | 79 | 624 | | _suppressedColliderPairs[key] = count + 1; |
| | | 625 | | else |
| | 97 | 626 | | _suppressedColliderPairs.Add(key, 1); |
| | 97 | 627 | | } |
| | | 628 | | |
| | | 629 | | private void RemoveSuppressedColliderPair(LSCollider2D colliderA, LSCollider2D colliderB) |
| | | 630 | | { |
| | 36 | 631 | | ulong key = CreateColliderPairKey(colliderA.Id, colliderB.Id); |
| | 36 | 632 | | if (!_suppressedColliderPairs.TryGetValue(key, out int count)) |
| | 1 | 633 | | return; |
| | | 634 | | |
| | 35 | 635 | | if (count <= 1) |
| | 29 | 636 | | _suppressedColliderPairs.Remove(key); |
| | | 637 | | else |
| | 6 | 638 | | _suppressedColliderPairs[key] = count - 1; |
| | 6 | 639 | | } |
| | | 640 | | } |