| | | 1 | | //======================================================================= |
| | | 2 | | // ColliderShapeDefinition.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 Gravitas.Materials; |
| | | 10 | | using System; |
| | | 11 | | using System.Runtime.CompilerServices; |
| | | 12 | | |
| | | 13 | | namespace Gravitas.Colliders; |
| | | 14 | | |
| | | 15 | | /// <summary> |
| | | 16 | | /// Describes authoritative collider shape input without runtime collider |
| | | 17 | | /// lifecycle state such as IDs, bodies, partitions, pairs, or events. |
| | | 18 | | /// </summary> |
| | | 19 | | public readonly struct ColliderShapeDefinition : IEquatable<ColliderShapeDefinition> |
| | | 20 | | { |
| | | 21 | | private readonly Vector3d[]? _meshVertices; |
| | | 22 | | private readonly int[]? _meshTriangles; |
| | | 23 | | private readonly PhysicsMaterial _material; |
| | | 24 | | private readonly bool _hasMaterial; |
| | | 25 | | |
| | | 26 | | private ColliderShapeDefinition( |
| | | 27 | | ColliderShapeDefinitionKind kind, |
| | | 28 | | Fixed64 radius, |
| | | 29 | | Fixed64 height, |
| | | 30 | | Vector3d size, |
| | | 31 | | MeshInertiaPolicy meshInertiaPolicy, |
| | | 32 | | Vector3d[]? meshVertices, |
| | | 33 | | int[]? meshTriangles, |
| | | 34 | | PhysicsMaterial? material) |
| | | 35 | | { |
| | 350 | 36 | | Kind = kind; |
| | 350 | 37 | | Radius = radius; |
| | 350 | 38 | | Height = height; |
| | 350 | 39 | | Size = size; |
| | 350 | 40 | | MeshInertiaPolicy = meshInertiaPolicy; |
| | 350 | 41 | | _meshVertices = meshVertices; |
| | 350 | 42 | | _meshTriangles = meshTriangles; |
| | 350 | 43 | | _material = material ?? PhysicsMaterial.Default; |
| | 350 | 44 | | _hasMaterial = material.HasValue; |
| | 350 | 45 | | } |
| | | 46 | | |
| | | 47 | | /// <summary> |
| | | 48 | | /// Gets the shape family represented by this definition. |
| | | 49 | | /// </summary> |
| | | 50 | | public ColliderShapeDefinitionKind Kind { get; } |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// Gets the unscaled radius used by radius-based shapes. |
| | | 54 | | /// </summary> |
| | | 55 | | public Fixed64 Radius { get; } |
| | | 56 | | |
| | | 57 | | /// <summary> |
| | | 58 | | /// Gets the unscaled height used by capsule and finite-cylinder shapes. |
| | | 59 | | /// </summary> |
| | | 60 | | public Fixed64 Height { get; } |
| | | 61 | | |
| | | 62 | | /// <summary> |
| | | 63 | | /// Gets the unscaled size used by sized shapes. Radius-based definitions |
| | | 64 | | /// store their normalized runtime size here as well. |
| | | 65 | | /// </summary> |
| | | 66 | | public Vector3d Size { get; } |
| | | 67 | | |
| | | 68 | | /// <summary> |
| | | 69 | | /// Gets the inertia policy used when this definition represents a convex |
| | | 70 | | /// mesh. |
| | | 71 | | /// </summary> |
| | | 72 | | public MeshInertiaPolicy MeshInertiaPolicy { get; } |
| | | 73 | | |
| | | 74 | | /// <summary> |
| | | 75 | | /// Gets the authored surface material used when this definition creates a |
| | | 76 | | /// runtime collider directly. |
| | | 77 | | /// </summary> |
| | 369 | 78 | | public PhysicsMaterial Material => _hasMaterial ? _material : PhysicsMaterial.Default; |
| | | 79 | | |
| | 637 | 80 | | internal bool HasMaterial => _hasMaterial; |
| | | 81 | | |
| | | 82 | | /// <summary> |
| | | 83 | | /// Gets the number of local mesh vertices held by a convex mesh definition. |
| | | 84 | | /// </summary> |
| | | 85 | | public int MeshVertexCount |
| | | 86 | | { |
| | | 87 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 274 | 88 | | get => _meshVertices?.Length ?? 0; |
| | | 89 | | } |
| | | 90 | | |
| | | 91 | | /// <summary> |
| | | 92 | | /// Gets the number of triangle indices held by a convex mesh definition. |
| | | 93 | | /// </summary> |
| | | 94 | | public int MeshTriangleIndexCount |
| | | 95 | | { |
| | | 96 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 341 | 97 | | get => _meshTriangles?.Length ?? 0; |
| | | 98 | | } |
| | | 99 | | |
| | | 100 | | /// <summary> |
| | | 101 | | /// Creates a sphere shape definition. |
| | | 102 | | /// </summary> |
| | | 103 | | public static ColliderShapeDefinition Sphere(Fixed64 radius, PhysicsMaterial? material = null) |
| | | 104 | | { |
| | 164 | 105 | | ValidateRadius(radius); |
| | 164 | 106 | | Fixed64 diameter = radius * (Fixed64)2; |
| | 164 | 107 | | return new( |
| | 164 | 108 | | ColliderShapeDefinitionKind.Sphere, |
| | 164 | 109 | | radius, |
| | 164 | 110 | | diameter, |
| | 164 | 111 | | new Vector3d(diameter, diameter, diameter), |
| | 164 | 112 | | MeshInertiaPolicy.RequireClosedVolume, |
| | 164 | 113 | | null, |
| | 164 | 114 | | null, |
| | 164 | 115 | | material); |
| | | 116 | | } |
| | | 117 | | |
| | | 118 | | /// <summary> |
| | | 119 | | /// Creates a capsule shape definition. |
| | | 120 | | /// </summary> |
| | | 121 | | public static ColliderShapeDefinition Capsule(Fixed64 radius, Fixed64 height, PhysicsMaterial? material = null) |
| | | 122 | | { |
| | 35 | 123 | | ValidateRadius(radius); |
| | 35 | 124 | | ValidateHeight(height); |
| | 35 | 125 | | SwiftThrowHelper.ThrowIfArgument( |
| | 35 | 126 | | Fixed64.CompareProducts( |
| | 35 | 127 | | height, |
| | 35 | 128 | | Fixed64.One, |
| | 35 | 129 | | radius, |
| | 35 | 130 | | Fixed64.Two) < 0, |
| | 35 | 131 | | nameof(height), |
| | 35 | 132 | | "Capsule height must be at least the capsule diameter."); |
| | 34 | 133 | | Fixed64 diameter = radius * (Fixed64)2; |
| | 34 | 134 | | return new( |
| | 34 | 135 | | ColliderShapeDefinitionKind.Capsule, |
| | 34 | 136 | | radius, |
| | 34 | 137 | | height, |
| | 34 | 138 | | new Vector3d(diameter, height, diameter), |
| | 34 | 139 | | MeshInertiaPolicy.RequireClosedVolume, |
| | 34 | 140 | | null, |
| | 34 | 141 | | null, |
| | 34 | 142 | | material); |
| | | 143 | | } |
| | | 144 | | |
| | | 145 | | /// <summary> |
| | | 146 | | /// Creates a cuboid shape definition. |
| | | 147 | | /// </summary> |
| | | 148 | | public static ColliderShapeDefinition Cuboid(Vector3d size, PhysicsMaterial? material = null) |
| | | 149 | | { |
| | 47 | 150 | | ValidateSize(size); |
| | 44 | 151 | | return new( |
| | 44 | 152 | | ColliderShapeDefinitionKind.Cuboid, |
| | 44 | 153 | | Fixed64.Zero, |
| | 44 | 154 | | size.Y, |
| | 44 | 155 | | size, |
| | 44 | 156 | | MeshInertiaPolicy.RequireClosedVolume, |
| | 44 | 157 | | null, |
| | 44 | 158 | | null, |
| | 44 | 159 | | material); |
| | | 160 | | } |
| | | 161 | | |
| | | 162 | | /// <summary> |
| | | 163 | | /// Creates a finite-cylinder shape definition. |
| | | 164 | | /// </summary> |
| | | 165 | | public static ColliderShapeDefinition Cylinder(Fixed64 radius, Fixed64 height, PhysicsMaterial? material = null) |
| | | 166 | | { |
| | 28 | 167 | | ValidateRadius(radius); |
| | 28 | 168 | | ValidateHeight(height); |
| | 28 | 169 | | Fixed64 diameter = radius * (Fixed64)2; |
| | 28 | 170 | | return new( |
| | 28 | 171 | | ColliderShapeDefinitionKind.Cylinder, |
| | 28 | 172 | | radius, |
| | 28 | 173 | | height, |
| | 28 | 174 | | new Vector3d(diameter, height, diameter), |
| | 28 | 175 | | MeshInertiaPolicy.RequireClosedVolume, |
| | 28 | 176 | | null, |
| | 28 | 177 | | null, |
| | 28 | 178 | | material); |
| | | 179 | | } |
| | | 180 | | |
| | | 181 | | /// <summary> |
| | | 182 | | /// Creates a finite circular cone shape definition whose local origin is |
| | | 183 | | /// the bounding center between its base plane and apex. |
| | | 184 | | /// </summary> |
| | | 185 | | public static ColliderShapeDefinition Cone(Fixed64 radius, Fixed64 height, PhysicsMaterial? material = null) |
| | | 186 | | { |
| | 38 | 187 | | ValidateRadius(radius); |
| | 37 | 188 | | ValidateHeight(height); |
| | 36 | 189 | | Fixed64 diameter = radius * (Fixed64)2; |
| | 36 | 190 | | return new( |
| | 36 | 191 | | ColliderShapeDefinitionKind.Cone, |
| | 36 | 192 | | radius, |
| | 36 | 193 | | height, |
| | 36 | 194 | | new Vector3d(diameter, height, diameter), |
| | 36 | 195 | | MeshInertiaPolicy.RequireClosedVolume, |
| | 36 | 196 | | null, |
| | 36 | 197 | | null, |
| | 36 | 198 | | material); |
| | | 199 | | } |
| | | 200 | | |
| | | 201 | | /// <summary> |
| | | 202 | | /// Creates a convex mesh shape definition. |
| | | 203 | | /// </summary> |
| | | 204 | | public static ColliderShapeDefinition ConvexMesh( |
| | | 205 | | Vector3d[] vertices, |
| | | 206 | | int[] triangles, |
| | | 207 | | MeshInertiaPolicy inertiaPolicy = MeshInertiaPolicy.RequireClosedVolume, |
| | | 208 | | PhysicsMaterial? material = null) |
| | | 209 | | { |
| | 46 | 210 | | ValidateMeshInput(vertices, triangles); |
| | 44 | 211 | | ValidateMeshInertiaPolicy(inertiaPolicy); |
| | | 212 | | |
| | 44 | 213 | | var vertexSnapshot = new Vector3d[vertices.Length]; |
| | 44 | 214 | | Array.Copy(vertices, vertexSnapshot, vertices.Length); |
| | 44 | 215 | | var triangleSnapshot = new int[triangles.Length]; |
| | 44 | 216 | | Array.Copy(triangles, triangleSnapshot, triangles.Length); |
| | | 217 | | |
| | 44 | 218 | | return new( |
| | 44 | 219 | | ColliderShapeDefinitionKind.ConvexMesh, |
| | 44 | 220 | | Fixed64.Zero, |
| | 44 | 221 | | Fixed64.Zero, |
| | 44 | 222 | | Vector3d.Zero, |
| | 44 | 223 | | inertiaPolicy, |
| | 44 | 224 | | vertexSnapshot, |
| | 44 | 225 | | triangleSnapshot, |
| | 44 | 226 | | material); |
| | | 227 | | } |
| | | 228 | | |
| | | 229 | | /// <summary> |
| | | 230 | | /// Gets a local mesh vertex by stable source order. |
| | | 231 | | /// </summary> |
| | | 232 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 233 | | public Vector3d GetMeshVertex(int index) |
| | | 234 | | { |
| | 39 | 235 | | EnsureMeshDefinition(); |
| | 37 | 236 | | SwiftThrowHelper.ThrowIfArrayIndexInvalid(index, _meshVertices!.Length, nameof(index)); |
| | 37 | 237 | | return _meshVertices[index]; |
| | | 238 | | } |
| | | 239 | | |
| | | 240 | | /// <summary> |
| | | 241 | | /// Gets a mesh triangle index by stable source order. |
| | | 242 | | /// </summary> |
| | | 243 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 244 | | public int GetMeshTriangleIndex(int index) |
| | | 245 | | { |
| | 110 | 246 | | EnsureMeshDefinition(); |
| | 109 | 247 | | SwiftThrowHelper.ThrowIfArrayIndexInvalid(index, _meshTriangles!.Length, nameof(index)); |
| | 109 | 248 | | return _meshTriangles[index]; |
| | | 249 | | } |
| | | 250 | | |
| | | 251 | | /// <summary> |
| | | 252 | | /// Creates a new unbound runtime collider from this shape definition. |
| | | 253 | | /// </summary> |
| | | 254 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | 14 | 255 | | public LSCollider CreateCollider() => CreateRuntimeCollider(); |
| | | 256 | | |
| | | 257 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 258 | | internal LSCollider CreateRuntimeCollider() |
| | | 259 | | { |
| | 301 | 260 | | return Kind switch |
| | 301 | 261 | | { |
| | 153 | 262 | | ColliderShapeDefinitionKind.Sphere => new LSSphereCollider(this), |
| | 27 | 263 | | ColliderShapeDefinitionKind.Capsule => new LSCapsuleCollider(this), |
| | 34 | 264 | | ColliderShapeDefinitionKind.Cuboid => new LSCuboidCollider(this), |
| | 25 | 265 | | ColliderShapeDefinitionKind.Cylinder => new LSCylinderCollider(this), |
| | 30 | 266 | | ColliderShapeDefinitionKind.Cone => new LSConeCollider(this), |
| | 31 | 267 | | ColliderShapeDefinitionKind.ConvexMesh => new LSMeshCollider(this), |
| | 1 | 268 | | _ => throw new ArgumentException( |
| | 1 | 269 | | "Collider shape definition cannot be default.", |
| | 1 | 270 | | nameof(ColliderShapeDefinition)) |
| | 301 | 271 | | }; |
| | | 272 | | } |
| | | 273 | | |
| | | 274 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 275 | | internal Vector3d[] GetMeshVerticesForRuntime() |
| | | 276 | | { |
| | 31 | 277 | | EnsureMeshDefinition(); |
| | 31 | 278 | | return _meshVertices!; |
| | | 279 | | } |
| | | 280 | | |
| | | 281 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 282 | | internal int[] GetMeshTrianglesForRuntime() |
| | | 283 | | { |
| | 31 | 284 | | EnsureMeshDefinition(); |
| | 31 | 285 | | return _meshTriangles!; |
| | | 286 | | } |
| | | 287 | | |
| | | 288 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 289 | | internal void EnsureDefined() |
| | | 290 | | { |
| | 884 | 291 | | SwiftThrowHelper.ThrowIfArgument( |
| | 884 | 292 | | Kind == ColliderShapeDefinitionKind.Undefined, |
| | 884 | 293 | | nameof(ColliderShapeDefinition), |
| | 884 | 294 | | "Collider shape definition cannot be default."); |
| | 883 | 295 | | } |
| | | 296 | | |
| | | 297 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 298 | | internal void EnsureKind(ColliderShapeDefinitionKind expectedKind) |
| | | 299 | | { |
| | 580 | 300 | | EnsureDefined(); |
| | 579 | 301 | | SwiftThrowHelper.ThrowIfArgument( |
| | 579 | 302 | | Kind != expectedKind, |
| | 579 | 303 | | nameof(ColliderShapeDefinition), |
| | 579 | 304 | | $"Collider shape definition must be {expectedKind}."); |
| | 576 | 305 | | } |
| | | 306 | | |
| | | 307 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 308 | | private void EnsureMeshDefinition() => |
| | 211 | 309 | | EnsureKind(ColliderShapeDefinitionKind.ConvexMesh); |
| | | 310 | | |
| | | 311 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 312 | | private static void ValidateRadius(Fixed64 radius) => |
| | 265 | 313 | | SwiftThrowHelper.ThrowIfArgument( |
| | 265 | 314 | | radius <= Fixed64.Zero, |
| | 265 | 315 | | nameof(radius), |
| | 265 | 316 | | "Collider radius must be greater than zero."); |
| | | 317 | | |
| | | 318 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 319 | | private static void ValidateHeight(Fixed64 height) => |
| | 100 | 320 | | SwiftThrowHelper.ThrowIfArgument( |
| | 100 | 321 | | height <= Fixed64.Zero, |
| | 100 | 322 | | nameof(height), |
| | 100 | 323 | | "Collider height must be greater than zero."); |
| | | 324 | | |
| | | 325 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 326 | | private static void ValidateSize(Vector3d size) => |
| | 47 | 327 | | SwiftThrowHelper.ThrowIfArgument( |
| | 47 | 328 | | size.X <= Fixed64.Zero || size.Y <= Fixed64.Zero || size.Z <= Fixed64.Zero, |
| | 47 | 329 | | nameof(size), |
| | 47 | 330 | | "Collider size components must be greater than zero."); |
| | | 331 | | |
| | | 332 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 333 | | private static void ValidateMeshInertiaPolicy(MeshInertiaPolicy inertiaPolicy) => |
| | 44 | 334 | | SwiftThrowHelper.ThrowIfArgument( |
| | 44 | 335 | | inertiaPolicy != MeshInertiaPolicy.RequireClosedVolume && |
| | 44 | 336 | | inertiaPolicy != MeshInertiaPolicy.SurfaceApproximation, |
| | 44 | 337 | | nameof(inertiaPolicy), |
| | 44 | 338 | | "Unsupported mesh inertia policy."); |
| | | 339 | | |
| | | 340 | | private static void ValidateMeshInput(Vector3d[] vertices, int[] triangles) |
| | | 341 | | { |
| | 46 | 342 | | SwiftThrowHelper.ThrowIfNull(vertices, nameof(vertices)); |
| | 46 | 343 | | SwiftThrowHelper.ThrowIfNull(triangles, nameof(triangles)); |
| | 46 | 344 | | SwiftThrowHelper.ThrowIfArgument(vertices.Length < 3, nameof(vertices), "Mesh must contain at least three vertic |
| | 46 | 345 | | SwiftThrowHelper.ThrowIfArgument(triangles.Length < 3, nameof(triangles), "Mesh must contain at least one triang |
| | 46 | 346 | | SwiftThrowHelper.ThrowIfArgument(triangles.Length % 3 != 0, nameof(triangles), "Triangle index count must be div |
| | 46 | 347 | | SwiftThrowHelper.ThrowIfArgument(vertices.Length > PhysicsMesh.MaxVertexCount, nameof(vertices), "Mesh vertex co |
| | 46 | 348 | | SwiftThrowHelper.ThrowIfArgument(triangles.Length / 3 > PhysicsMesh.MaxTriangleCount, nameof(triangles), "Mesh t |
| | | 349 | | |
| | 1552 | 350 | | for (int i = 0; i < triangles.Length; i++) |
| | | 351 | | { |
| | 732 | 352 | | int vertexIndex = triangles[i]; |
| | 732 | 353 | | SwiftThrowHelper.ThrowIfArgument( |
| | 732 | 354 | | vertexIndex < 0 || vertexIndex >= vertices.Length, |
| | 732 | 355 | | nameof(triangles), |
| | 732 | 356 | | "Triangle index references a vertex outside the source vertex array."); |
| | | 357 | | } |
| | 44 | 358 | | } |
| | | 359 | | |
| | | 360 | | /// <inheritdoc/> |
| | | 361 | | public bool Equals(ColliderShapeDefinition other) |
| | | 362 | | { |
| | 19 | 363 | | if (Kind != other.Kind |
| | 19 | 364 | | || Radius != other.Radius |
| | 19 | 365 | | || Height != other.Height |
| | 19 | 366 | | || Size != other.Size |
| | 19 | 367 | | || MeshInertiaPolicy != other.MeshInertiaPolicy |
| | 19 | 368 | | || _hasMaterial != other._hasMaterial |
| | 19 | 369 | | || (_hasMaterial && _material != other._material) |
| | 19 | 370 | | || MeshVertexCount != other.MeshVertexCount |
| | 19 | 371 | | || MeshTriangleIndexCount != other.MeshTriangleIndexCount) |
| | | 372 | | { |
| | 11 | 373 | | return false; |
| | | 374 | | } |
| | | 375 | | |
| | 46 | 376 | | for (int i = 0; i < MeshVertexCount; i++) |
| | | 377 | | { |
| | 16 | 378 | | if (_meshVertices![i] != other._meshVertices![i]) |
| | 1 | 379 | | return false; |
| | | 380 | | } |
| | | 381 | | |
| | 40 | 382 | | for (int i = 0; i < MeshTriangleIndexCount; i++) |
| | | 383 | | { |
| | 14 | 384 | | if (_meshTriangles![i] != other._meshTriangles![i]) |
| | 1 | 385 | | return false; |
| | | 386 | | } |
| | | 387 | | |
| | 6 | 388 | | return true; |
| | | 389 | | } |
| | | 390 | | |
| | | 391 | | /// <inheritdoc/> |
| | | 392 | | public override bool Equals(object? obj) => |
| | 19 | 393 | | obj is ColliderShapeDefinition other && Equals(other); |
| | | 394 | | |
| | | 395 | | /// <inheritdoc/> |
| | | 396 | | public override int GetHashCode() |
| | | 397 | | { |
| | | 398 | | unchecked |
| | | 399 | | { |
| | 4 | 400 | | int hash = 17; |
| | 4 | 401 | | hash = hash * 31 + Kind.GetHashCode(); |
| | 4 | 402 | | hash = hash * 31 + Radius.GetHashCode(); |
| | 4 | 403 | | hash = hash * 31 + Height.GetHashCode(); |
| | 4 | 404 | | hash = hash * 31 + Size.GetHashCode(); |
| | 4 | 405 | | hash = hash * 31 + MeshInertiaPolicy.GetHashCode(); |
| | 4 | 406 | | hash = hash * 31 + _hasMaterial.GetHashCode(); |
| | 4 | 407 | | if (_hasMaterial) |
| | 2 | 408 | | hash = hash * 31 + _material.GetHashCode(); |
| | | 409 | | |
| | 20 | 410 | | for (int i = 0; i < MeshVertexCount; i++) |
| | 6 | 411 | | hash = hash * 31 + _meshVertices![i].GetHashCode(); |
| | 20 | 412 | | for (int i = 0; i < MeshTriangleIndexCount; i++) |
| | 6 | 413 | | hash = hash * 31 + _meshTriangles![i].GetHashCode(); |
| | | 414 | | |
| | 4 | 415 | | return hash; |
| | | 416 | | } |
| | | 417 | | } |
| | | 418 | | |
| | | 419 | | /// <summary>Determines whether two authored 3D shape definitions are equal.</summary> |
| | | 420 | | public static bool operator ==(ColliderShapeDefinition left, ColliderShapeDefinition right) => |
| | 1 | 421 | | left.Equals(right); |
| | | 422 | | |
| | | 423 | | /// <summary>Determines whether two authored 3D shape definitions are not equal.</summary> |
| | | 424 | | public static bool operator !=(ColliderShapeDefinition left, ColliderShapeDefinition right) => |
| | 1 | 425 | | !left.Equals(right); |
| | | 426 | | } |