| | | 1 | | //======================================================================= |
| | | 2 | | // LSCollider.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 FixedMathSharp.Geometry; |
| | | 11 | | using Gravitas.CollisionHandling; |
| | | 12 | | using Gravitas.Materials; |
| | | 13 | | using Gravitas.Queries; |
| | | 14 | | using Gravitas.Support; |
| | | 15 | | using GridForge.Grids; |
| | | 16 | | using GridForge.Spatial; |
| | | 17 | | using SwiftCollections; |
| | | 18 | | using System; |
| | | 19 | | using System.Runtime.CompilerServices; |
| | | 20 | | |
| | | 21 | | namespace Gravitas.Colliders; |
| | | 22 | | |
| | | 23 | | /// <summary> |
| | | 24 | | /// Common runtime type for the closed set of Gravitas-owned 3D collider |
| | | 25 | | /// shapes. Use <see cref="ColliderShapeDefinition.CreateCollider"/> when |
| | | 26 | | /// constructing from engine-adapter shape data. |
| | | 27 | | /// </summary> |
| | | 28 | | public abstract partial class LSCollider : IRecordable, IColliderHierarchyNode, IPhysicsColliderRegistryItem |
| | | 29 | | { |
| | 12204 | 30 | | internal LSCollider() { } |
| | | 31 | | |
| | | 32 | | #region Fields and Properties |
| | | 33 | | |
| | | 34 | | /// <summary>Controls collider debug diagnostics.</summary> |
| | | 35 | | protected bool _debug; |
| | | 36 | | /// <summary>Controls collider shape debug drawing.</summary> |
| | | 37 | | protected bool _drawShape; |
| | | 38 | | private bool _drawPartitions; |
| | | 39 | | private bool _drawBoundingBox; |
| | | 40 | | |
| | 6102 | 41 | | private bool _active = true; |
| | | 42 | | private bool _deactivationInProgress; |
| | | 43 | | /// <summary>Gets or sets whether this collider participates in runtime physics.</summary> |
| | | 44 | | public bool IsActive |
| | | 45 | | { |
| | | 46 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 383453 | 47 | | get => _active; |
| | | 48 | | set |
| | | 49 | | { |
| | 18 | 50 | | ThrowIfCompoundPartLifecycle(nameof(IsActive)); |
| | | 51 | | |
| | 18 | 52 | | if (_active == value) |
| | 2 | 53 | | return; |
| | | 54 | | |
| | 16 | 55 | | if (_context == null || _id < 0) |
| | | 56 | | { |
| | 2 | 57 | | _active = value; |
| | 2 | 58 | | return; |
| | | 59 | | } |
| | | 60 | | |
| | 14 | 61 | | if (value) |
| | | 62 | | { |
| | 5 | 63 | | RebuildRuntimeShapeState(); |
| | 5 | 64 | | _active = true; |
| | 5 | 65 | | InitialPartition(); |
| | 5 | 66 | | if (_context.Settings.RuntimeMode.RunsMixedContacts()) |
| | 3 | 67 | | _context.MixedCollisions.Refresh3DColliderPartition(this); |
| | 5 | 68 | | return; |
| | | 69 | | } |
| | | 70 | | |
| | 9 | 71 | | _active = false; |
| | 9 | 72 | | if (IsPartitioned) |
| | 8 | 73 | | _context.Collisions.ClearPartitionedObject(this, force: true); |
| | | 74 | | |
| | 9 | 75 | | if (IsMixedPartitioned) |
| | | 76 | | { |
| | 3 | 77 | | _context.MixedCollisions.ClearPartitioned3DCollider(this, force: true); |
| | 3 | 78 | | MarkMixedUnpartitioned(); |
| | 3 | 79 | | ClearMixedPartitionCoordinates(); |
| | | 80 | | } |
| | 9 | 81 | | } |
| | | 82 | | } |
| | | 83 | | |
| | | 84 | | private bool _isTrigger; |
| | | 85 | | |
| | | 86 | | /// <summary> |
| | | 87 | | /// Gets or sets whether this bodyless collider is a trigger volume. |
| | | 88 | | /// Trigger volumes raise trigger enter/stay/exit callbacks for valid overlap |
| | | 89 | | /// pairs and never apply physical response. |
| | | 90 | | /// </summary> |
| | | 91 | | public bool IsTrigger |
| | | 92 | | { |
| | | 93 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 48313 | 94 | | get => _isTrigger; |
| | 41 | 95 | | set => SetTrigger(value); |
| | | 96 | | } |
| | | 97 | | |
| | 6102 | 98 | | private int _id = -1; |
| | | 99 | | /// <summary>Gets the context-local runtime collider identifier.</summary> |
| | 4807769 | 100 | | public int Id => _id; |
| | | 101 | | |
| | 6102 | 102 | | private int _serviceIndex = -1; |
| | | 103 | | |
| | 6102 | 104 | | private int _replayOrder = -1; |
| | | 105 | | |
| | 6102 | 106 | | private int _replayOrdinal = -1; |
| | 2390 | 107 | | internal int ReplayOrdinal => _replayOrdinal; |
| | | 108 | | |
| | | 109 | | private long _lifetimeVersion; |
| | 75031 | 110 | | internal long LifetimeVersion => _lifetimeVersion; |
| | | 111 | | |
| | 346783 | 112 | | internal bool IsDeactivationInProgress => _deactivationInProgress; |
| | | 113 | | |
| | 6102 | 114 | | private int _serviceRefreshIndex = -1; |
| | 4795 | 115 | | internal int ServiceRefreshIndex => _serviceRefreshIndex; |
| | | 116 | | |
| | | 117 | | private IMatterAgent? _agent; |
| | 5253 | 118 | | internal IMatterAgent? AgentOrNull => _agent; |
| | | 119 | | |
| | | 120 | | private GravitasWorldContext? _context; |
| | | 121 | | /// <summary>Gets the world context to which this collider is bound.</summary> |
| | | 122 | | public GravitasWorldContext Context |
| | | 123 | | { |
| | | 124 | | get |
| | | 125 | | { |
| | 137089 | 126 | | SwiftThrowHelper.ThrowIfTrue( |
| | 137089 | 127 | | _context == null, |
| | 137089 | 128 | | nameof(LSCollider), |
| | 137089 | 129 | | "Collider is not bound to a GravitasWorldContext."); |
| | 137087 | 130 | | return _context!; |
| | | 131 | | } |
| | | 132 | | } |
| | | 133 | | |
| | | 134 | | private SolidBody? _body; |
| | | 135 | | /// <summary>Gets the owning 3D body, or <see langword="null"/> for a bodyless collider.</summary> |
| | 710331 | 136 | | public SolidBody? Body => _body; |
| | | 137 | | |
| | | 138 | | /// <summary> |
| | | 139 | | /// Gets whether this collider is bodyless or belongs to an explicit static |
| | | 140 | | /// body role. |
| | | 141 | | /// </summary> |
| | 102460 | 142 | | public bool IsStatic => _body == null || _body.IsStatic; |
| | | 143 | | |
| | 5749 | 144 | | internal bool RequiresServiceSideRefresh => _body == null || _body.DynamicId < 0; |
| | | 145 | | |
| | | 146 | | private LSCompoundCollider? _compoundOwner; |
| | 6102 | 147 | | private FixedQuaternion _compoundLocalRotation = FixedQuaternion.Identity; |
| | 6102 | 148 | | private Vector3d _compoundLocalScale = Vector3d.One; |
| | | 149 | | |
| | 6102 | 150 | | private readonly ColliderRuntimeShapeState<ColliderShapeSnapshot> _runtimeShapeState = new(); |
| | | 151 | | private ColliderPartitionState _partitionState; |
| | | 152 | | private ColliderPartitionState _mixedPartitionState; |
| | | 153 | | private ColliderQueryState _queryState; |
| | | 154 | | private ColliderPairState<CollisionPair> _pairState; |
| | | 155 | | private ColliderHierarchyState _hierarchyState; |
| | | 156 | | |
| | 1712 | 157 | | internal uint RuntimeShapeVersion => _runtimeShapeState.RuntimeVersion; |
| | | 158 | | |
| | 6066 | 159 | | internal bool HasHostBinding => _agent != null; |
| | | 160 | | |
| | 832 | 161 | | internal LSCompoundCollider? CompoundOwner => _compoundOwner; |
| | | 162 | | |
| | | 163 | | /// <summary>Gets the runtime world position, or sets it for a bodyless collider.</summary> |
| | | 164 | | public Vector3d Position |
| | | 165 | | { |
| | 2261 | 166 | | get => _compoundOwner?.Center |
| | 2261 | 167 | | ?? Body?.Position3d |
| | 2261 | 168 | | ?? _agent?.Transform.WorldPosition |
| | 2261 | 169 | | ?? throw new InvalidOperationException("Collider has no body or static transform."); |
| | | 170 | | set |
| | | 171 | | { |
| | 14 | 172 | | SwiftThrowHelper.ThrowIfTrue( |
| | 14 | 173 | | _compoundOwner != null, |
| | 14 | 174 | | nameof(Position), |
| | 14 | 175 | | "Compound collider parts inherit position from their owning compound collider."); |
| | 13 | 176 | | SwiftThrowHelper.ThrowIfTrue( |
| | 13 | 177 | | _agent == null, |
| | 13 | 178 | | nameof(Position), |
| | 13 | 179 | | "Collider is not bound to a static transform."); |
| | 12 | 180 | | SwiftThrowHelper.ThrowIfTrue( |
| | 12 | 181 | | _body != null, |
| | 12 | 182 | | nameof(Position), |
| | 12 | 183 | | "Body-owned collider positions must be changed through their SolidBody."); |
| | | 184 | | |
| | 11 | 185 | | if (_agent.Transform.WorldPosition == value) |
| | 1 | 186 | | return; |
| | 10 | 187 | | SetBodylessWorldPose(value, default, setRotation: false, nameof(Position)); |
| | 7 | 188 | | } |
| | | 189 | | } |
| | | 190 | | |
| | | 191 | | /// <summary>Gets the committed world rotation, or sets it for a bodyless collider.</summary> |
| | | 192 | | public FixedQuaternion Rotation |
| | | 193 | | { |
| | 138403 | 194 | | get => _hasCommittedShape |
| | 138403 | 195 | | ? _committedRotation |
| | 138403 | 196 | | : throw new InvalidOperationException( |
| | 138403 | 197 | | "Collider has no committed runtime shape."); |
| | | 198 | | set |
| | | 199 | | { |
| | 10 | 200 | | SwiftThrowHelper.ThrowIfTrue( |
| | 10 | 201 | | _compoundOwner != null, |
| | 10 | 202 | | nameof(Rotation), |
| | 10 | 203 | | "Compound collider parts inherit rotation from their owning compound collider."); |
| | 9 | 204 | | SwiftThrowHelper.ThrowIfTrue( |
| | 9 | 205 | | _agent == null, |
| | 9 | 206 | | nameof(Rotation), |
| | 9 | 207 | | "Collider is not bound to a static transform."); |
| | 8 | 208 | | SwiftThrowHelper.ThrowIfTrue( |
| | 8 | 209 | | _body != null, |
| | 8 | 210 | | nameof(Rotation), |
| | 8 | 211 | | "Body-owned collider rotations must be changed through their SolidBody."); |
| | | 212 | | |
| | 7 | 213 | | if (_agent.Transform.WorldRotation == value) |
| | 1 | 214 | | return; |
| | 6 | 215 | | SetBodylessWorldPose(default, value, setRotation: true, nameof(Rotation)); |
| | 4 | 216 | | } |
| | | 217 | | } |
| | | 218 | | |
| | | 219 | | /// <summary>Gets the owning body's linear velocity, or zero for a bodyless collider.</summary> |
| | 3366 | 220 | | public Vector3d Velocity => Body?.LinearVelocity ?? Vector3d.Zero; |
| | | 221 | | |
| | | 222 | | /// <summary>Gets the GridForge world owned by this collider's context.</summary> |
| | 6 | 223 | | public GridWorld World => Context.World; |
| | | 224 | | |
| | | 225 | | /// <summary>Gets the transform that supplies this collider's runtime pose.</summary> |
| | 306 | 226 | | public FixedTransform Transform => _compoundOwner?.Transform |
| | 306 | 227 | | ?? Body?.PositionTransform |
| | 306 | 228 | | ?? _agent?.Transform |
| | 306 | 229 | | ?? throw new InvalidOperationException("Collider has no body or static transform."); |
| | | 230 | | |
| | | 231 | | private PhysicsLayer _layer = new(); |
| | 6102 | 232 | | private PhysicsLayerMask _ignoredCollisionLayers = PhysicsLayerMask.None; |
| | | 233 | | |
| | | 234 | | /// <summary>Gets or sets the single physics layer used for collision and query filtering.</summary> |
| | | 235 | | public PhysicsLayer Layer |
| | | 236 | | { |
| | | 237 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 865631 | 238 | | get => _layer; |
| | | 239 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 49 | 240 | | set => _layer = value; |
| | | 241 | | } |
| | | 242 | | |
| | | 243 | | /// <summary> |
| | | 244 | | /// Gets or sets physical layers this collider ignores for collider-to-collider |
| | | 245 | | /// interactions. Public queries continue to use the caller's query mask. |
| | | 246 | | /// </summary> |
| | | 247 | | public PhysicsLayerMask IgnoredCollisionLayers |
| | | 248 | | { |
| | | 249 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 9 | 250 | | get => _ignoredCollisionLayers; |
| | | 251 | | set |
| | | 252 | | { |
| | 22 | 253 | | if (_ignoredCollisionLayers == value) |
| | 1 | 254 | | return; |
| | | 255 | | |
| | 21 | 256 | | _ignoredCollisionLayers = value; |
| | 21 | 257 | | _body?.Wake(); |
| | 20 | 258 | | } |
| | | 259 | | } |
| | | 260 | | |
| | 6102 | 261 | | private PhysicsMaterial _material = PhysicsMaterial.Default; |
| | | 262 | | |
| | | 263 | | /// <summary> |
| | | 264 | | /// Gets or sets the deterministic surface material used by collision |
| | | 265 | | /// response for this collider. |
| | | 266 | | /// </summary> |
| | | 267 | | public PhysicsMaterial Material |
| | | 268 | | { |
| | | 269 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 22217 | 270 | | get => _material; |
| | | 271 | | set |
| | | 272 | | { |
| | 1271 | 273 | | if (_material == value) |
| | 600 | 274 | | return; |
| | | 275 | | |
| | 671 | 276 | | _material = value; |
| | 671 | 277 | | OnMaterialChanged(); |
| | 671 | 278 | | _body?.Wake(); |
| | 178 | 279 | | } |
| | | 280 | | } |
| | | 281 | | |
| | | 282 | | /// <summary>Gets whether this collider currently has 3D broad-phase partition membership.</summary> |
| | 48163 | 283 | | public bool IsPartitioned => _partitionState.IsPartitioned; |
| | | 284 | | |
| | | 285 | | /// <summary> |
| | | 286 | | /// Used for preventing culling for the first frame this object is added to a new partition node. |
| | | 287 | | /// </summary> |
| | | 288 | | public bool PartitionChanged |
| | | 289 | | { |
| | | 290 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 6181 | 291 | | get => _partitionState.PartitionChanged; |
| | | 292 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 27279 | 293 | | set => _partitionState.PartitionChanged = value; |
| | | 294 | | } |
| | | 295 | | |
| | 16022 | 296 | | internal uint BroadPhaseVersion => _partitionState.BroadPhaseVersion; |
| | | 297 | | |
| | | 298 | | /// <summary> |
| | | 299 | | /// Center of collider in local space, used for calculating bounds and offsets. Should be set in the Setup method of |
| | | 300 | | /// </summary> |
| | | 301 | | protected Vector3d _offset; |
| | | 302 | | |
| | | 303 | | /// <summary> |
| | | 304 | | /// Gets or sets the unscaled local center offset used by bounds and shape-derived state. |
| | | 305 | | /// </summary> |
| | | 306 | | public Vector3d LocalOffset |
| | | 307 | | { |
| | | 308 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 35482 | 309 | | get => _offset; |
| | | 310 | | set |
| | | 311 | | { |
| | 346 | 312 | | if (_offset == value) |
| | 84 | 313 | | return; |
| | | 314 | | |
| | 262 | 315 | | _offset = value; |
| | 262 | 316 | | MarkShapeDirty(); |
| | 262 | 317 | | } |
| | | 318 | | } |
| | | 319 | | |
| | | 320 | | /// <summary>Gets the runtime 3D shape family.</summary> |
| | | 321 | | public abstract ColliderType Shape { get; } |
| | | 322 | | /// <summary>Gets the deterministic narrow-phase ordering priority.</summary> |
| | | 323 | | public abstract int Priority { get; } |
| | | 324 | | |
| | | 325 | | /// <summary>Stores the unscaled radius for radius-based collider shapes.</summary> |
| | 6102 | 326 | | protected Fixed64 _radius = Fixed64.Half; |
| | | 327 | | |
| | | 328 | | /// <summary> |
| | | 329 | | /// Gets or sets the unscaled shape radius used by radius-based colliders. |
| | | 330 | | /// </summary> |
| | | 331 | | public Fixed64 Radius |
| | | 332 | | { |
| | | 333 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 28 | 334 | | get => _radius; |
| | | 335 | | set |
| | | 336 | | { |
| | 726 | 337 | | SwiftThrowHelper.ThrowIfArgument( |
| | 726 | 338 | | value <= Fixed64.Zero, |
| | 726 | 339 | | nameof(value), |
| | 726 | 340 | | "Collider radius must be greater than zero."); |
| | | 341 | | |
| | 726 | 342 | | if (_radius == value) |
| | 266 | 343 | | return; |
| | | 344 | | |
| | 460 | 345 | | _radius = value; |
| | 460 | 346 | | OnRadiusChanged(); |
| | 460 | 347 | | MarkShapeDirty(); |
| | 460 | 348 | | } |
| | | 349 | | } |
| | | 350 | | |
| | | 351 | | /// <summary> |
| | | 352 | | /// Gets the bounding circle radius. |
| | | 353 | | /// For boxes, this is half of the diagonal length of the cube |
| | | 354 | | /// </summary> |
| | | 355 | | /// <value>The radius.</value> |
| | 5 | 356 | | public virtual Fixed64 ScaledRadius => GetCurrentScaledRadius(); |
| | | 357 | | |
| | | 358 | | /// <summary>Gets the squared scaled bounding radius.</summary> |
| | 10094 | 359 | | public Fixed64 ScaledRadiusSqr => ScaledRadius * ScaledRadius; |
| | | 360 | | |
| | | 361 | | /// <summary>Stores the unscaled local collider size.</summary> |
| | 6102 | 362 | | protected Vector3d _size = Vector3d.One; |
| | | 363 | | |
| | | 364 | | /// <summary> |
| | | 365 | | /// Gets or sets the unscaled local size used when rebuilding collider bounds. |
| | | 366 | | /// </summary> |
| | | 367 | | public Vector3d Size |
| | | 368 | | { |
| | | 369 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 27 | 370 | | get => _size; |
| | | 371 | | set |
| | | 372 | | { |
| | 1771 | 373 | | ValidateSize(value); |
| | 1765 | 374 | | Vector3d normalizedSize = NormalizeSize(value); |
| | 1765 | 375 | | if (_size == normalizedSize) |
| | 851 | 376 | | return; |
| | | 377 | | |
| | 914 | 378 | | _size = normalizedSize; |
| | 914 | 379 | | MarkShapeDirty(); |
| | 914 | 380 | | } |
| | | 381 | | } |
| | | 382 | | |
| | | 383 | | /// <summary>Gets the committed shape-specific measure used as the frontal-area fallback.</summary> |
| | | 384 | | public virtual Fixed64 Area { get; protected set; } = Fixed64.Zero; |
| | | 385 | | |
| | | 386 | | #region Grid & Partition Bounds |
| | | 387 | | |
| | | 388 | | /// <summary>Gets the combined owner and compound-part scale.</summary> |
| | | 389 | | public virtual Vector3d LocalScale |
| | | 390 | | { |
| | | 391 | | get |
| | | 392 | | { |
| | 3 | 393 | | bool representable = TryGetLocalScale(out Vector3d scale); |
| | 3 | 394 | | SwiftThrowHelper.ThrowIfTrue( |
| | 3 | 395 | | !representable, |
| | 3 | 396 | | nameof(LocalScale), |
| | 3 | 397 | | "The combined 3D collider scale is outside the Fixed64 coordinate domain."); |
| | 2 | 398 | | return scale; |
| | | 399 | | } |
| | | 400 | | } |
| | | 401 | | |
| | | 402 | | /// <summary> |
| | | 403 | | /// Attempts to materialize the combined owner and compound-part scale. |
| | | 404 | | /// Shape preparation retains those factors separately and does not require |
| | | 405 | | /// their product to be representable. |
| | | 406 | | /// </summary> |
| | | 407 | | public bool TryGetLocalScale(out Vector3d scale) |
| | | 408 | | { |
| | 6 | 409 | | GetCurrentShapeScales( |
| | 6 | 410 | | out Vector3d ownerScale, |
| | 6 | 411 | | out Vector3d partScale); |
| | | 412 | | |
| | 6 | 413 | | bool representable = Fixed64.TryMultiplyDivide( |
| | 6 | 414 | | ownerScale.X, |
| | 6 | 415 | | partScale.X, |
| | 6 | 416 | | Fixed64.One, |
| | 6 | 417 | | out Fixed64 x) |
| | 6 | 418 | | & Fixed64.TryMultiplyDivide( |
| | 6 | 419 | | ownerScale.Y, |
| | 6 | 420 | | partScale.Y, |
| | 6 | 421 | | Fixed64.One, |
| | 6 | 422 | | out Fixed64 y) |
| | 6 | 423 | | & Fixed64.TryMultiplyDivide( |
| | 6 | 424 | | ownerScale.Z, |
| | 6 | 425 | | partScale.Z, |
| | 6 | 426 | | Fixed64.One, |
| | 6 | 427 | | out Fixed64 z); |
| | 6 | 428 | | scale = representable ? new Vector3d(x, y, z) : default; |
| | 6 | 429 | | return representable; |
| | | 430 | | } |
| | | 431 | | |
| | | 432 | | /// <summary> |
| | | 433 | | /// Gets the component-scaled local offset. |
| | | 434 | | /// </summary> |
| | | 435 | | /// <exception cref="InvalidOperationException"> |
| | | 436 | | /// The scaled offset itself is outside the Fixed64 coordinate domain. The |
| | | 437 | | /// collider may still have a representable world center through exact |
| | | 438 | | /// transform cancellation. |
| | | 439 | | /// </exception> |
| | 10 | 440 | | public Vector3d ScaledOffset => GetCurrentScaledOffset(); |
| | | 441 | | |
| | | 442 | | /// <summary> |
| | | 443 | | /// Attempts to materialize the component-scaled local offset independently |
| | | 444 | | /// from the collider's complete world transform. |
| | | 445 | | /// </summary> |
| | | 446 | | public bool TryGetScaledOffset(out Vector3d scaledOffset) => |
| | 2 | 447 | | TryGetCurrentScaledOffset(out scaledOffset); |
| | | 448 | | |
| | | 449 | | /// <summary> |
| | | 450 | | /// Bodies position in world space + collider offset (center) value |
| | | 451 | | /// </summary> |
| | 102582 | 452 | | public Vector3d Center => _hasCommittedShape |
| | 102582 | 453 | | ? _committedCenter |
| | 102582 | 454 | | : throw new InvalidOperationException( |
| | 102582 | 455 | | "Collider has no committed runtime shape."); |
| | | 456 | | |
| | | 457 | | /// <summary>Stores the committed world-space axis-aligned bounds.</summary> |
| | | 458 | | protected FixedBoundBox _bounds; |
| | | 459 | | /// <summary>Gets the committed world-space axis-aligned bounds.</summary> |
| | 16136 | 460 | | public FixedBoundBox Bounds => _bounds; |
| | | 461 | | /// <summary>Gets the minimum corner of <see cref="Bounds"/>.</summary> |
| | 3594258 | 462 | | public Vector3d BoundsMin => _bounds.Min; |
| | | 463 | | /// <summary>Gets the maximum corner of <see cref="Bounds"/>.</summary> |
| | 3496650 | 464 | | public Vector3d BoundsMax => _bounds.Max; |
| | | 465 | | |
| | 1034047 | 466 | | internal SwiftList<WorldVoxelIndex>? PartitionCoordinates => _partitionState.Coordinates; |
| | | 467 | | |
| | 19695 | 468 | | internal int PartitionKind => _partitionState.LastPartitionKind; |
| | | 469 | | |
| | 3798 | 470 | | internal bool IsMixedPartitioned => _mixedPartitionState.IsPartitioned; |
| | | 471 | | |
| | 1225 | 472 | | internal SwiftList<WorldVoxelIndex>? MixedPartitionCoordinates => _mixedPartitionState.Coordinates; |
| | | 473 | | |
| | 1943 | 474 | | internal int MixedPartitionKind => _mixedPartitionState.LastPartitionKind; |
| | | 475 | | |
| | | 476 | | #endregion |
| | | 477 | | |
| | | 478 | | internal uint RaycastVersion |
| | | 479 | | { |
| | | 480 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 215117 | 481 | | get => _queryState.RaycastVersion; |
| | | 482 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 11338 | 483 | | set => _queryState.RaycastVersion = value; |
| | | 484 | | } |
| | | 485 | | |
| | | 486 | | internal uint CircleQueryVersion |
| | | 487 | | { |
| | | 488 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 24451 | 489 | | get => _queryState.CircleQueryVersion; |
| | | 490 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 741 | 491 | | set => _queryState.CircleQueryVersion = value; |
| | | 492 | | } |
| | | 493 | | |
| | | 494 | | /// <summary>Gets whether this collider has a hierarchy parent.</summary> |
| | 2 | 495 | | public bool IsChild => _hierarchyState.IsChild; |
| | | 496 | | /// <summary>Gets whether this collider is configured as or currently owns a hierarchy parent.</summary> |
| | 4 | 497 | | public bool IsParent => _hierarchyState.IsParent; |
| | | 498 | | /// <summary>Gets the context-local identifier of the parent collider, or -1 when unparented.</summary> |
| | 9 | 499 | | public int ParentId => ParentKey.Id; |
| | | 500 | | /// <summary>Gets the 3D parent collider, when the parent belongs to the 3D runtime.</summary> |
| | 7 | 501 | | public LSCollider? Parent3D => _hierarchyState.Parent as LSCollider; |
| | | 502 | | /// <summary>Gets the 2D parent collider, when the parent belongs to the 2D runtime.</summary> |
| | 4 | 503 | | public LSCollider2D? Parent2D => _hierarchyState.Parent as LSCollider2D; |
| | 17054 | 504 | | internal LSCollider? TopParent3D => _hierarchyState.TopParent as LSCollider; |
| | 1 | 505 | | internal LSCollider2D? TopParent2D => _hierarchyState.TopParent as LSCollider2D; |
| | 2257 | 506 | | internal int HierarchyChildCount => _hierarchyState.ChildCount; |
| | 213504 | 507 | | internal ColliderHierarchyKey HierarchyKey => Id >= 0 ? ColliderHierarchyKey.Create3D(Id) : ColliderHierarchyKey.Non |
| | 2258 | 508 | | internal ColliderHierarchyKey ParentKey => _hierarchyState.ParentKey; |
| | 2248 | 509 | | internal ColliderHierarchyKey TopParentKey => _hierarchyState.TopParentKey; |
| | 1 | 510 | | internal ColliderHierarchyState HierarchyState => _hierarchyState; |
| | 141 | 511 | | ColliderHierarchyKey IColliderHierarchyNode.HierarchyKey => HierarchyKey; |
| | 43 | 512 | | IColliderHierarchyNode? IColliderHierarchyNode.HierarchyParent => _hierarchyState.Parent; |
| | | 513 | | |
| | | 514 | | #endregion |
| | | 515 | | |
| | | 516 | | internal void Initialize(SolidBody body) |
| | | 517 | | { |
| | 5083 | 518 | | _body = body; |
| | 5083 | 519 | | InitCore(body.Agent); |
| | 5083 | 520 | | } |
| | | 521 | | |
| | | 522 | | /// <summary>Binds and registers this collider as a bodyless runtime collider.</summary> |
| | | 523 | | public void InitializeWithNoBody(IMatterAgent agent) |
| | | 524 | | { |
| | 610 | 525 | | ThrowIfCompoundPartLifecycle(nameof(InitializeWithNoBody)); |
| | 610 | 526 | | SwiftThrowHelper.ThrowIfNull(agent, nameof(agent)); |
| | 610 | 527 | | SwiftThrowHelper.ThrowIfArgument( |
| | 610 | 528 | | Id >= 0 || (HasHostBinding && !ReferenceEquals(_agent, agent)), |
| | 610 | 529 | | nameof(agent), |
| | 610 | 530 | | "Collider is already registered or bound to another host agent."); |
| | 608 | 531 | | PreflightInitialization(agent); |
| | 589 | 532 | | InitCore(agent); |
| | 589 | 533 | | } |
| | | 534 | | |
| | | 535 | | internal void PreflightBodyInitialization( |
| | | 536 | | SolidBody body, |
| | | 537 | | Vector3d requestedPosition, |
| | | 538 | | FixedQuaternion requestedRotation) |
| | | 539 | | { |
| | 5089 | 540 | | ThrowIfCompoundPartLifecycle(nameof(Initialize)); |
| | 5089 | 541 | | SwiftThrowHelper.ThrowIfNull(body, nameof(body)); |
| | 5089 | 542 | | ThrowIfTriggerWouldAttachToBody(nameof(Initialize)); |
| | 5088 | 543 | | PreflightInitialization( |
| | 5088 | 544 | | body.Agent, |
| | 5088 | 545 | | useRequestedPose: true, |
| | 5088 | 546 | | requestedPosition, |
| | 5088 | 547 | | requestedRotation); |
| | 5082 | 548 | | } |
| | | 549 | | |
| | | 550 | | private void PreflightInitialization( |
| | | 551 | | IMatterAgent agent, |
| | | 552 | | bool useRequestedPose = false, |
| | | 553 | | Vector3d requestedPosition = default, |
| | | 554 | | FixedQuaternion requestedRotation = default) |
| | | 555 | | { |
| | 5696 | 556 | | SwiftThrowHelper.ThrowIfNull(agent, nameof(agent)); |
| | 5696 | 557 | | OnBeforeInitialize(agent); |
| | 5696 | 558 | | PrepareStandaloneInitialization( |
| | 5696 | 559 | | agent, |
| | 5696 | 560 | | useRequestedPose, |
| | 5696 | 561 | | requestedPosition, |
| | 5696 | 562 | | requestedRotation); |
| | 5671 | 563 | | } |
| | | 564 | | |
| | | 565 | | private void InitCore(IMatterAgent agent) |
| | | 566 | | { |
| | 5672 | 567 | | _lifetimeVersion++; |
| | | 568 | | |
| | 5672 | 569 | | _queryState.Reset(); |
| | | 570 | | |
| | 5672 | 571 | | _agent = agent; |
| | 5672 | 572 | | _active = true; |
| | 5672 | 573 | | _deactivationInProgress = false; |
| | 5672 | 574 | | BindContext(agent.Context); |
| | 5672 | 575 | | PublishPreparedShape(); |
| | 5672 | 576 | | Context.Physics.AssimilateCollider(this); |
| | 5672 | 577 | | _hierarchyState.Initialize(_agent.IsParent); |
| | 5672 | 578 | | OnInitialize(); |
| | | 579 | | |
| | 5672 | 580 | | _partitionState.SetPreviousGridBounds(Vector3d.Zero, Vector3d.Zero); |
| | | 581 | | |
| | 5672 | 582 | | InitialPartition(); |
| | 5672 | 583 | | } |
| | | 584 | | |
| | | 585 | | /// <summary>Validates or prepares derived shape state before runtime registration.</summary> |
| | 5696 | 586 | | protected virtual void OnBeforeInitialize(IMatterAgent agent) { } |
| | | 587 | | |
| | | 588 | | /// <summary>Runs derived initialization after runtime registration.</summary> |
| | 5672 | 589 | | protected virtual void OnInitialize() { } |
| | | 590 | | |
| | | 591 | | internal void ValidateCurrentRuntimeTransform() |
| | | 592 | | { |
| | 112 | 593 | | ColliderShapeSnapshot snapshot = CaptureShapeSnapshot(); |
| | 109 | 594 | | PrepareRuntimeShape( |
| | 109 | 595 | | snapshot, |
| | 109 | 596 | | requireRepresentableMassPoint: true); |
| | 109 | 597 | | } |
| | | 598 | | |
| | | 599 | | private void InitialPartition() |
| | | 600 | | { |
| | 6120 | 601 | | RebuildRuntimeShapeState(); |
| | 6120 | 602 | | if (!IsActive) |
| | 1 | 603 | | return; |
| | | 604 | | |
| | 6119 | 605 | | _partitionState.Coordinates ??= new(); |
| | 6119 | 606 | | SwiftList<WorldVoxelIndex> partitionCoordinates = _partitionState.Coordinates!; |
| | 6119 | 607 | | Context.Collisions.PartitionObject(this, ref partitionCoordinates); |
| | 6119 | 608 | | _partitionState.Coordinates = partitionCoordinates; |
| | 6119 | 609 | | SetPreviousGridBounds(); |
| | 6119 | 610 | | _partitionState.MarkPartitioned(); |
| | 6119 | 611 | | MarkBroadPhaseChanged(); |
| | 6119 | 612 | | } |
| | | 613 | | |
| | | 614 | | /// <summary>Refreshes bodyless collider shape and broad-phase state for one simulation step.</summary> |
| | | 615 | | public void Simulate() |
| | | 616 | | { |
| | 27700 | 617 | | ThrowIfCompoundPartLifecycle(nameof(Simulate)); |
| | 27699 | 618 | | if (!IsActive) |
| | 3 | 619 | | return; |
| | | 620 | | |
| | 27696 | 621 | | if (!IsPartitioned) |
| | | 622 | | { |
| | 438 | 623 | | InitialPartition(); |
| | 438 | 624 | | return; |
| | | 625 | | } |
| | | 626 | | |
| | 27258 | 627 | | bool refreshPartition = RebuildRuntimeShapeState() |
| | 27258 | 628 | | || Context.Collisions.IsPartitionRefreshRequired(this); |
| | 27253 | 629 | | PartitionChanged = false; |
| | 27253 | 630 | | if (refreshPartition) |
| | 17763 | 631 | | UpdatePartition(); |
| | 27253 | 632 | | } |
| | | 633 | | |
| | | 634 | | private void UpdatePartition() |
| | | 635 | | { |
| | 17817 | 636 | | MarkBroadPhaseChanged(); |
| | | 637 | | |
| | 17817 | 638 | | if (!Context.Collisions.ClearPartitionedObject(this)) |
| | 29 | 639 | | return; |
| | | 640 | | |
| | 17788 | 641 | | SwiftList<WorldVoxelIndex> partitionCoordinates = _partitionState.Coordinates!; |
| | 17788 | 642 | | bool partitioned = Context.Collisions.PartitionObject(this, ref partitionCoordinates); |
| | 17788 | 643 | | _partitionState.Coordinates = partitionCoordinates; |
| | 17788 | 644 | | if (!partitioned) |
| | 485 | 645 | | return; |
| | | 646 | | |
| | 17303 | 647 | | SetPreviousGridBounds(); |
| | | 648 | | |
| | 17303 | 649 | | _partitionState.MarkPartitioned(); |
| | 17303 | 650 | | } |
| | | 651 | | |
| | | 652 | | private void SetBodylessWorldPose( |
| | | 653 | | Vector3d position, |
| | | 654 | | FixedQuaternion rotation, |
| | | 655 | | bool setRotation, |
| | | 656 | | string operation) |
| | | 657 | | { |
| | 16 | 658 | | GravitasWorldContext context = _context!; |
| | 16 | 659 | | context.ThrowIfFixedStepMutationNotAllowed(); |
| | 15 | 660 | | FixedTransform transform = _agent!.Transform; |
| | 15 | 661 | | Vector3d localPosition = transform.LocalPosition; |
| | 15 | 662 | | FixedQuaternion localRotation = transform.LocalRotation; |
| | 15 | 663 | | bool transformed = setRotation |
| | 15 | 664 | | ? transform.TrySetWorldPose(transform.WorldPosition, rotation) |
| | 15 | 665 | | : transform.TrySetWorldPosition(position); |
| | 15 | 666 | | SwiftThrowHelper.ThrowIfTrue( |
| | 15 | 667 | | !transformed, |
| | 15 | 668 | | operation, |
| | 15 | 669 | | setRotation |
| | 15 | 670 | | ? "Static collider transform cannot represent the requested world rotation." |
| | 15 | 671 | | : "Static collider transform cannot represent the requested world position."); |
| | | 672 | | |
| | | 673 | | try |
| | | 674 | | { |
| | 13 | 675 | | RebuildRuntimeShapeState(); |
| | 11 | 676 | | } |
| | 2 | 677 | | catch |
| | | 678 | | { |
| | 2 | 679 | | transform.LocalPosition = localPosition; |
| | 2 | 680 | | transform.LocalRotation = localRotation; |
| | 2 | 681 | | throw; |
| | | 682 | | } |
| | | 683 | | |
| | 11 | 684 | | if (!IsActive) |
| | 1 | 685 | | return; |
| | | 686 | | |
| | 10 | 687 | | if (IsPartitioned) |
| | 9 | 688 | | UpdatePartition(); |
| | | 689 | | else |
| | 1 | 690 | | InitialPartition(); |
| | | 691 | | |
| | 10 | 692 | | if (context.Settings.RuntimeMode.RunsMixedContacts()) |
| | 2 | 693 | | context.MixedCollisions.Refresh3DColliderPartition(this); |
| | 10 | 694 | | } |
| | | 695 | | |
| | | 696 | | /// <summary>Assigns a 3D hierarchy parent used for collision exclusion.</summary> |
| | | 697 | | public void SetParent(LSCollider parent) |
| | | 698 | | { |
| | 28 | 699 | | ThrowIfCompoundPartLifecycle(nameof(SetParent)); |
| | 28 | 700 | | _hierarchyState.SetParent(this, parent); |
| | 25 | 701 | | } |
| | | 702 | | |
| | | 703 | | /// <summary>Assigns a 2D hierarchy parent used for mixed collision exclusion.</summary> |
| | | 704 | | public void SetParent(LSCollider2D parent) |
| | | 705 | | { |
| | 7 | 706 | | ThrowIfCompoundPartLifecycle(nameof(SetParent)); |
| | 7 | 707 | | _hierarchyState.SetParent(this, parent); |
| | 7 | 708 | | } |
| | | 709 | | |
| | | 710 | | /// <summary>Removes this collider from its current hierarchy parent.</summary> |
| | | 711 | | public void ClearParent() |
| | | 712 | | { |
| | 224 | 713 | | ThrowIfCompoundPartLifecycle(nameof(ClearParent)); |
| | 224 | 714 | | _hierarchyState.ClearParent(this); |
| | 224 | 715 | | } |
| | | 716 | | |
| | | 717 | | /// <summary>Gets whether hierarchy rules exclude collision with another 3D collider.</summary> |
| | | 718 | | public bool IsSibling(LSCollider other) |
| | | 719 | | { |
| | 102942 | 720 | | return _hierarchyState.ExcludesCollisionWith(other._hierarchyState, HierarchyKey, other.HierarchyKey); |
| | | 721 | | } |
| | | 722 | | |
| | | 723 | | internal bool ExcludesMixedCollisionWith(LSCollider2D other) => |
| | 5224 | 724 | | _hierarchyState.ExcludesCollisionWith(other.HierarchyState, HierarchyKey, other.HierarchyKey); |
| | | 725 | | |
| | | 726 | | private bool RebuildRuntimeShapeState(bool refreshMassProperties = true) |
| | | 727 | | { |
| | 62178 | 728 | | ColliderShapeSnapshot snapshot = CaptureShapeSnapshot(); |
| | 62173 | 729 | | if (!_runtimeShapeState.ShouldRebuild(snapshot)) |
| | 43095 | 730 | | return false; |
| | | 731 | | |
| | 19078 | 732 | | PrepareRuntimeShape( |
| | 19078 | 733 | | snapshot, |
| | 19078 | 734 | | requireRepresentableMassPoint: |
| | 19078 | 735 | | _body != null || _compoundOwner != null); |
| | 19076 | 736 | | PublishPreparedShape(); |
| | 19076 | 737 | | if (refreshMassProperties) |
| | 12269 | 738 | | _body?.RefreshMassPropertiesFromColliderShape(); |
| | 19076 | 739 | | return true; |
| | | 740 | | } |
| | | 741 | | |
| | | 742 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 743 | | internal bool RebuildRuntimeShapeOnly(bool refreshMassProperties = true) => |
| | 28723 | 744 | | RebuildRuntimeShapeState(refreshMassProperties); |
| | | 745 | | |
| | | 746 | | /// <summary>Marks derived runtime shape state for deterministic rebuilding.</summary> |
| | | 747 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 748 | | protected void MarkShapeDirty() |
| | | 749 | | { |
| | 1637 | 750 | | _runtimeShapeState.MarkDirty(); |
| | 1637 | 751 | | if (_compoundOwner != null) |
| | | 752 | | { |
| | 1 | 753 | | _compoundOwner.MarkShapeDirty(); |
| | 1 | 754 | | return; |
| | | 755 | | } |
| | | 756 | | |
| | 1636 | 757 | | _body?.Wake(); |
| | 47 | 758 | | } |
| | | 759 | | |
| | | 760 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 23936 | 761 | | private void MarkBroadPhaseChanged() => _partitionState.MarkBroadPhaseChanged(); |
| | | 762 | | |
| | | 763 | | /// <summary>Updates derived authored dimensions after <see cref="Radius"/> changes.</summary> |
| | 2 | 764 | | protected virtual void OnRadiusChanged() { } |
| | | 765 | | |
| | | 766 | | /// <summary>Updates derived material state after <see cref="Material"/> changes.</summary> |
| | 660 | 767 | | protected virtual void OnMaterialChanged() { } |
| | | 768 | | |
| | | 769 | | /// <summary>Normalizes an authored size for the derived shape.</summary> |
| | 264 | 770 | | protected virtual Vector3d NormalizeSize(Vector3d value) => value; |
| | | 771 | | |
| | | 772 | | private static void ValidateSize(Vector3d value) |
| | | 773 | | { |
| | 1771 | 774 | | SwiftThrowHelper.ThrowIfArgument( |
| | 1771 | 775 | | value.X <= Fixed64.Zero || value.Y <= Fixed64.Zero || value.Z <= Fixed64.Zero, |
| | 1771 | 776 | | nameof(value), |
| | 1771 | 777 | | "Collider size components must be greater than zero."); |
| | 1765 | 778 | | } |
| | | 779 | | |
| | | 780 | | /// <summary> |
| | | 781 | | /// Gets the point on this collider's surface nearest to a world-space point. |
| | | 782 | | /// </summary> |
| | | 783 | | /// <param name="other"></param> |
| | | 784 | | /// <returns></returns> |
| | | 785 | | public abstract Vector3d ClosestPointOnSurface(Vector3d other); |
| | | 786 | | |
| | | 787 | | /// <summary> |
| | | 788 | | /// The direction pointing outward from the surface of the collider at the point |
| | | 789 | | /// </summary> |
| | | 790 | | /// <param name="point"></param> |
| | | 791 | | /// <returns></returns> |
| | | 792 | | public abstract Vector3d GetNormalAtPoint(Vector3d point); |
| | | 793 | | |
| | | 794 | | /// <summary> |
| | | 795 | | /// Returns the semantic closest surface feature without requiring its |
| | | 796 | | /// world point or distance to fit in one <see cref="Fixed64"/> vector. |
| | | 797 | | /// </summary> |
| | | 798 | | internal abstract FixedPointAnchor GetClosestSurfaceAnchor( |
| | | 799 | | Vector3d point, |
| | | 800 | | out Vector3d normal); |
| | | 801 | | |
| | | 802 | | /// <summary> |
| | | 803 | | /// Tries to resolve a normal that is invariant across the planar surface |
| | | 804 | | /// feature containing <paramref name="point"/>. A successful result must |
| | | 805 | | /// be a nonzero outward normal. |
| | | 806 | | /// </summary> |
| | | 807 | | internal virtual bool TryGetPlanarSurfaceNormal(Vector3d point, out Vector3d normal) |
| | | 808 | | { |
| | 111 | 809 | | normal = Vector3d.Zero; |
| | 111 | 810 | | return false; |
| | | 811 | | } |
| | | 812 | | |
| | | 813 | | /// <summary> |
| | | 814 | | /// Checks if position is in the padded bounds of the collider, used for broad phase collision detection |
| | | 815 | | /// </summary> |
| | | 816 | | /// <param name="cellPadding">The topology cell edge used to conservatively pad the bounds.</param> |
| | | 817 | | /// <param name="position">The position to check</param> |
| | | 818 | | /// <returns>True if position within bounds</returns> |
| | | 819 | | public bool IsPositionInBounds(Fixed64 cellPadding, Vector3d position) |
| | | 820 | | { |
| | 1258512 | 821 | | return position.X + cellPadding >= BoundsMin.X |
| | 1258512 | 822 | | && position.X - cellPadding <= BoundsMax.X |
| | 1258512 | 823 | | && position.Y + cellPadding >= BoundsMin.Y |
| | 1258512 | 824 | | && position.Y - cellPadding <= BoundsMax.Y |
| | 1258512 | 825 | | && position.Z + cellPadding >= BoundsMin.Z |
| | 1258512 | 826 | | && position.Z - cellPadding <= BoundsMax.Z; |
| | | 827 | | } |
| | | 828 | | |
| | | 829 | | /// <summary> |
| | | 830 | | /// Checks if this object overlaps the line formed by p1 and p2 |
| | | 831 | | /// </summary> |
| | | 832 | | /// <param name="worker">The prepared ray-axis worker for the owning query service.</param> |
| | | 833 | | /// <param name="outputIntersectionPoints"></param> |
| | | 834 | | /// <returns></returns> |
| | | 835 | | public abstract bool ColliderOverlapsRay(RaycastSegmentWorker worker, ref SwiftList<Vector3d> outputIntersectionPoin |
| | | 836 | | |
| | | 837 | | void IColliderHierarchyNode.AddChild(ColliderHierarchyKey key) |
| | | 838 | | { |
| | 30 | 839 | | ThrowIfCompoundPartLifecycle(nameof(IColliderHierarchyNode.AddChild)); |
| | 30 | 840 | | _hierarchyState.AddChild(key); |
| | 30 | 841 | | } |
| | | 842 | | |
| | | 843 | | void IColliderHierarchyNode.RemoveChild(ColliderHierarchyKey key) |
| | | 844 | | { |
| | 6 | 845 | | ThrowIfCompoundPartLifecycle(nameof(IColliderHierarchyNode.RemoveChild)); |
| | 6 | 846 | | _hierarchyState.RemoveChild(key); |
| | 6 | 847 | | } |
| | | 848 | | |
| | 2 | 849 | | void IColliderHierarchyNode.ClearParentReference() => _hierarchyState.ClearParentReference(); |
| | | 850 | | |
| | | 851 | | bool IColliderHierarchyNode.TryGetHierarchyColliderByKey(ColliderHierarchyKey key, out IColliderHierarchyNode? colli |
| | | 852 | | { |
| | 7 | 853 | | collider = null; |
| | 7 | 854 | | if (!key.IsValid || _context == null) |
| | 1 | 855 | | return false; |
| | | 856 | | |
| | 6 | 857 | | if (key.Is3D && _context.Physics.TryGetColliderById(key.Id, out LSCollider? collider3D)) |
| | | 858 | | { |
| | 2 | 859 | | collider = collider3D; |
| | 2 | 860 | | return true; |
| | | 861 | | } |
| | | 862 | | |
| | 4 | 863 | | if (key.Is2D && _context.Physics2D.TryGetColliderById(key.Id, out LSCollider2D? collider2D)) |
| | | 864 | | { |
| | 2 | 865 | | collider = collider2D; |
| | 2 | 866 | | return true; |
| | | 867 | | } |
| | | 868 | | |
| | 2 | 869 | | return false; |
| | | 870 | | } |
| | | 871 | | |
| | | 872 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 873 | | internal void SetPhysicsState(int id, int serviceIndex, int replayOrder) |
| | | 874 | | { |
| | 5685 | 875 | | SwiftThrowHelper.ThrowIfNegative(id, nameof(id)); |
| | 5685 | 876 | | SwiftThrowHelper.ThrowIfNegative(serviceIndex, nameof(serviceIndex)); |
| | 5685 | 877 | | SwiftThrowHelper.ThrowIfNegative(replayOrder, nameof(replayOrder)); |
| | 5685 | 878 | | _id = id; |
| | 5685 | 879 | | _serviceIndex = serviceIndex; |
| | 5685 | 880 | | _replayOrder = replayOrder; |
| | 5685 | 881 | | _replayOrdinal = -1; |
| | 5685 | 882 | | } |
| | | 883 | | |
| | | 884 | | internal void SetServiceIndex(int serviceIndex) |
| | | 885 | | { |
| | 28 | 886 | | SwiftThrowHelper.ThrowIfNegative(serviceIndex, nameof(serviceIndex)); |
| | 28 | 887 | | _serviceIndex = serviceIndex; |
| | 28 | 888 | | } |
| | | 889 | | |
| | | 890 | | internal void SetReplayOrdinal(int replayOrdinal) |
| | | 891 | | { |
| | 4666 | 892 | | SwiftThrowHelper.ThrowIfNegative(replayOrdinal, nameof(replayOrdinal)); |
| | 4666 | 893 | | _replayOrdinal = replayOrdinal; |
| | 4666 | 894 | | } |
| | | 895 | | |
| | | 896 | | internal void SetServiceRefreshIndex(int serviceRefreshIndex) |
| | | 897 | | { |
| | 1181 | 898 | | SwiftThrowHelper.ThrowIfNegative(serviceRefreshIndex, nameof(serviceRefreshIndex)); |
| | 1181 | 899 | | _serviceRefreshIndex = serviceRefreshIndex; |
| | 1181 | 900 | | } |
| | | 901 | | |
| | | 902 | | internal void ClearServiceRefreshIndex() |
| | | 903 | | { |
| | 4793 | 904 | | _serviceRefreshIndex = -1; |
| | 4793 | 905 | | } |
| | | 906 | | |
| | | 907 | | internal void ClearPhysicsState() |
| | | 908 | | { |
| | 248 | 909 | | _partitionState.MarkUnpartitioned(); |
| | 248 | 910 | | _partitionState.ClearCoordinates(); |
| | 248 | 911 | | _mixedPartitionState.MarkUnpartitioned(); |
| | 248 | 912 | | _mixedPartitionState.ClearCoordinates(); |
| | 248 | 913 | | _pairState.ClearCollisionPairs(); |
| | 248 | 914 | | _pairState.ClearCollisionPairHolders(); |
| | 248 | 915 | | _id = -1; |
| | 248 | 916 | | _serviceIndex = -1; |
| | 248 | 917 | | _replayOrder = -1; |
| | 248 | 918 | | _replayOrdinal = -1; |
| | 248 | 919 | | _serviceRefreshIndex = -1; |
| | 248 | 920 | | } |
| | | 921 | | |
| | | 922 | | void IPhysicsColliderRegistryItem.SetRegistryState(int id, int serviceIndex, int replayOrder) => |
| | 5685 | 923 | | SetPhysicsState(id, serviceIndex, replayOrder); |
| | | 924 | | |
| | 219 | 925 | | int IPhysicsColliderRegistryItem.ServiceIndex => _serviceIndex; |
| | | 926 | | |
| | 6106 | 927 | | int IPhysicsColliderRegistryItem.ReplayOrder => _replayOrder; |
| | | 928 | | |
| | | 929 | | void IPhysicsColliderRegistryItem.SetRegistryServiceIndex(int serviceIndex) => |
| | 28 | 930 | | SetServiceIndex(serviceIndex); |
| | | 931 | | |
| | | 932 | | void IPhysicsColliderRegistryItem.SetRegistryReplayOrdinal(int replayOrdinal) => |
| | 4666 | 933 | | SetReplayOrdinal(replayOrdinal); |
| | | 934 | | |
| | 248 | 935 | | void IPhysicsColliderRegistryItem.ClearRegistryState() => ClearPhysicsState(); |
| | | 936 | | |
| | | 937 | | internal SwiftList<WorldVoxelIndex> GetOrCreateMixedPartitionCoordinates() |
| | | 938 | | { |
| | 678 | 939 | | _mixedPartitionState.Coordinates ??= new(); |
| | 678 | 940 | | return _mixedPartitionState.Coordinates; |
| | | 941 | | } |
| | | 942 | | |
| | | 943 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 944 | | internal bool MatchesPartitionGridBounds(Vector3d min, Vector3d max, int partitionKind) => |
| | 37502 | 945 | | _partitionState.IsPartitioned |
| | 37502 | 946 | | && _partitionState.LastGridBoundsMin == min |
| | 37502 | 947 | | && _partitionState.LastGridBoundsMax == max |
| | 37502 | 948 | | && _partitionState.LastPartitionKind == partitionKind; |
| | | 949 | | |
| | | 950 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 18026 | 951 | | internal void MarkUnpartitioned() => _partitionState.MarkUnpartitioned(); |
| | | 952 | | |
| | | 953 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 18026 | 954 | | internal void ClearPartitionCoordinates() => _partitionState.ClearCoordinates(); |
| | | 955 | | |
| | | 956 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 957 | | internal bool MatchesMixedPartitionGridBounds(Vector3d min, Vector3d max, int partitionKind) => |
| | 1569 | 958 | | _mixedPartitionState.IsPartitioned |
| | 1569 | 959 | | && _mixedPartitionState.LastGridBoundsMin == min |
| | 1569 | 960 | | && _mixedPartitionState.LastGridBoundsMax == max |
| | 1569 | 961 | | && _mixedPartitionState.LastPartitionKind == partitionKind; |
| | | 962 | | |
| | | 963 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 964 | | internal void MarkMixedPartitioned(Vector3d min, Vector3d max, int partitionKind) |
| | | 965 | | { |
| | 662 | 966 | | _mixedPartitionState.SetPreviousGridBounds(min, max, partitionKind); |
| | 662 | 967 | | _mixedPartitionState.MarkPartitioned(); |
| | 662 | 968 | | } |
| | | 969 | | |
| | | 970 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 280 | 971 | | internal void MarkMixedUnpartitioned() => _mixedPartitionState.MarkUnpartitioned(); |
| | | 972 | | |
| | | 973 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 280 | 974 | | internal void ClearMixedPartitionCoordinates() => _mixedPartitionState.ClearCoordinates(); |
| | | 975 | | |
| | | 976 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 314802 | 977 | | internal bool IgnoresCollisionLayer(PhysicsLayer layer) => _ignoredCollisionLayers.Includes(layer); |
| | | 978 | | } |