< Summary

Information
Class: Gravitas.Colliders.ColliderShapeDefinition
Assembly: Gravitas
File(s): /home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/Definitions/ColliderShapeDefinition.cs
Line coverage
100%
Covered lines: 194
Uncovered lines: 0
Coverable lines: 194
Total lines: 426
Line coverage: 100%
Branch coverage
100%
Covered branches: 63
Total branches: 63
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%
get_Material()100%22100%
get_HasMaterial()100%11100%
get_MeshVertexCount()100%22100%
get_MeshTriangleIndexCount()100%22100%
Sphere(...)100%11100%
Capsule(...)100%11100%
Cuboid(...)100%11100%
Cylinder(...)100%11100%
Cone(...)100%11100%
ConvexMesh(...)100%11100%
GetMeshVertex(...)100%11100%
GetMeshTriangleIndex(...)100%11100%
CreateCollider()100%11100%
CreateRuntimeCollider()100%77100%
GetMeshVerticesForRuntime()100%11100%
GetMeshTrianglesForRuntime()100%11100%
EnsureDefined()100%11100%
EnsureKind(...)100%22100%
EnsureMeshDefinition()100%11100%
ValidateRadius(...)100%11100%
ValidateHeight(...)100%11100%
ValidateSize(...)100%44100%
ValidateMeshInertiaPolicy(...)100%22100%
ValidateMeshInput(...)100%44100%
Equals(...)100%2828100%
Equals(...)100%22100%
GetHashCode()100%66100%
op_Equality(...)100%11100%
op_Inequality(...)100%11100%

File(s)

/home/runner/work/Gravitas/Gravitas/src/Gravitas/Colliders/Definitions/ColliderShapeDefinition.cs

#LineLine coverage
 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
 8using FixedMathSharp;
 9using Gravitas.Materials;
 10using System;
 11using System.Runtime.CompilerServices;
 12
 13namespace 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>
 19public 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    {
 35036        Kind = kind;
 35037        Radius = radius;
 35038        Height = height;
 35039        Size = size;
 35040        MeshInertiaPolicy = meshInertiaPolicy;
 35041        _meshVertices = meshVertices;
 35042        _meshTriangles = meshTriangles;
 35043        _material = material ?? PhysicsMaterial.Default;
 35044        _hasMaterial = material.HasValue;
 35045    }
 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>
 36978    public PhysicsMaterial Material => _hasMaterial ? _material : PhysicsMaterial.Default;
 79
 63780    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)]
 27488        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)]
 34197        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    {
 164105        ValidateRadius(radius);
 164106        Fixed64 diameter = radius * (Fixed64)2;
 164107        return new(
 164108            ColliderShapeDefinitionKind.Sphere,
 164109            radius,
 164110            diameter,
 164111            new Vector3d(diameter, diameter, diameter),
 164112            MeshInertiaPolicy.RequireClosedVolume,
 164113            null,
 164114            null,
 164115            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    {
 35123        ValidateRadius(radius);
 35124        ValidateHeight(height);
 35125        SwiftThrowHelper.ThrowIfArgument(
 35126            Fixed64.CompareProducts(
 35127                height,
 35128                Fixed64.One,
 35129                radius,
 35130                Fixed64.Two) < 0,
 35131            nameof(height),
 35132            "Capsule height must be at least the capsule diameter.");
 34133        Fixed64 diameter = radius * (Fixed64)2;
 34134        return new(
 34135            ColliderShapeDefinitionKind.Capsule,
 34136            radius,
 34137            height,
 34138            new Vector3d(diameter, height, diameter),
 34139            MeshInertiaPolicy.RequireClosedVolume,
 34140            null,
 34141            null,
 34142            material);
 143    }
 144
 145    /// <summary>
 146    /// Creates a cuboid shape definition.
 147    /// </summary>
 148    public static ColliderShapeDefinition Cuboid(Vector3d size, PhysicsMaterial? material = null)
 149    {
 47150        ValidateSize(size);
 44151        return new(
 44152            ColliderShapeDefinitionKind.Cuboid,
 44153            Fixed64.Zero,
 44154            size.Y,
 44155            size,
 44156            MeshInertiaPolicy.RequireClosedVolume,
 44157            null,
 44158            null,
 44159            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    {
 28167        ValidateRadius(radius);
 28168        ValidateHeight(height);
 28169        Fixed64 diameter = radius * (Fixed64)2;
 28170        return new(
 28171            ColliderShapeDefinitionKind.Cylinder,
 28172            radius,
 28173            height,
 28174            new Vector3d(diameter, height, diameter),
 28175            MeshInertiaPolicy.RequireClosedVolume,
 28176            null,
 28177            null,
 28178            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    {
 38187        ValidateRadius(radius);
 37188        ValidateHeight(height);
 36189        Fixed64 diameter = radius * (Fixed64)2;
 36190        return new(
 36191            ColliderShapeDefinitionKind.Cone,
 36192            radius,
 36193            height,
 36194            new Vector3d(diameter, height, diameter),
 36195            MeshInertiaPolicy.RequireClosedVolume,
 36196            null,
 36197            null,
 36198            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    {
 46210        ValidateMeshInput(vertices, triangles);
 44211        ValidateMeshInertiaPolicy(inertiaPolicy);
 212
 44213        var vertexSnapshot = new Vector3d[vertices.Length];
 44214        Array.Copy(vertices, vertexSnapshot, vertices.Length);
 44215        var triangleSnapshot = new int[triangles.Length];
 44216        Array.Copy(triangles, triangleSnapshot, triangles.Length);
 217
 44218        return new(
 44219            ColliderShapeDefinitionKind.ConvexMesh,
 44220            Fixed64.Zero,
 44221            Fixed64.Zero,
 44222            Vector3d.Zero,
 44223            inertiaPolicy,
 44224            vertexSnapshot,
 44225            triangleSnapshot,
 44226            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    {
 39235        EnsureMeshDefinition();
 37236        SwiftThrowHelper.ThrowIfArrayIndexInvalid(index, _meshVertices!.Length, nameof(index));
 37237        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    {
 110246        EnsureMeshDefinition();
 109247        SwiftThrowHelper.ThrowIfArrayIndexInvalid(index, _meshTriangles!.Length, nameof(index));
 109248        return _meshTriangles[index];
 249    }
 250
 251    /// <summary>
 252    /// Creates a new unbound runtime collider from this shape definition.
 253    /// </summary>
 254    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 14255    public LSCollider CreateCollider() => CreateRuntimeCollider();
 256
 257    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 258    internal LSCollider CreateRuntimeCollider()
 259    {
 301260        return Kind switch
 301261        {
 153262            ColliderShapeDefinitionKind.Sphere => new LSSphereCollider(this),
 27263            ColliderShapeDefinitionKind.Capsule => new LSCapsuleCollider(this),
 34264            ColliderShapeDefinitionKind.Cuboid => new LSCuboidCollider(this),
 25265            ColliderShapeDefinitionKind.Cylinder => new LSCylinderCollider(this),
 30266            ColliderShapeDefinitionKind.Cone => new LSConeCollider(this),
 31267            ColliderShapeDefinitionKind.ConvexMesh => new LSMeshCollider(this),
 1268            _ => throw new ArgumentException(
 1269                "Collider shape definition cannot be default.",
 1270                nameof(ColliderShapeDefinition))
 301271        };
 272    }
 273
 274    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 275    internal Vector3d[] GetMeshVerticesForRuntime()
 276    {
 31277        EnsureMeshDefinition();
 31278        return _meshVertices!;
 279    }
 280
 281    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 282    internal int[] GetMeshTrianglesForRuntime()
 283    {
 31284        EnsureMeshDefinition();
 31285        return _meshTriangles!;
 286    }
 287
 288    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 289    internal void EnsureDefined()
 290    {
 884291        SwiftThrowHelper.ThrowIfArgument(
 884292            Kind == ColliderShapeDefinitionKind.Undefined,
 884293            nameof(ColliderShapeDefinition),
 884294            "Collider shape definition cannot be default.");
 883295    }
 296
 297    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 298    internal void EnsureKind(ColliderShapeDefinitionKind expectedKind)
 299    {
 580300        EnsureDefined();
 579301        SwiftThrowHelper.ThrowIfArgument(
 579302            Kind != expectedKind,
 579303            nameof(ColliderShapeDefinition),
 579304            $"Collider shape definition must be {expectedKind}.");
 576305    }
 306
 307    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 308    private void EnsureMeshDefinition() =>
 211309        EnsureKind(ColliderShapeDefinitionKind.ConvexMesh);
 310
 311    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 312    private static void ValidateRadius(Fixed64 radius) =>
 265313        SwiftThrowHelper.ThrowIfArgument(
 265314            radius <= Fixed64.Zero,
 265315            nameof(radius),
 265316            "Collider radius must be greater than zero.");
 317
 318    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 319    private static void ValidateHeight(Fixed64 height) =>
 100320        SwiftThrowHelper.ThrowIfArgument(
 100321            height <= Fixed64.Zero,
 100322            nameof(height),
 100323            "Collider height must be greater than zero.");
 324
 325    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 326    private static void ValidateSize(Vector3d size) =>
 47327        SwiftThrowHelper.ThrowIfArgument(
 47328            size.X <= Fixed64.Zero || size.Y <= Fixed64.Zero || size.Z <= Fixed64.Zero,
 47329            nameof(size),
 47330            "Collider size components must be greater than zero.");
 331
 332    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 333    private static void ValidateMeshInertiaPolicy(MeshInertiaPolicy inertiaPolicy) =>
 44334        SwiftThrowHelper.ThrowIfArgument(
 44335            inertiaPolicy != MeshInertiaPolicy.RequireClosedVolume &&
 44336            inertiaPolicy != MeshInertiaPolicy.SurfaceApproximation,
 44337            nameof(inertiaPolicy),
 44338            "Unsupported mesh inertia policy.");
 339
 340    private static void ValidateMeshInput(Vector3d[] vertices, int[] triangles)
 341    {
 46342        SwiftThrowHelper.ThrowIfNull(vertices, nameof(vertices));
 46343        SwiftThrowHelper.ThrowIfNull(triangles, nameof(triangles));
 46344        SwiftThrowHelper.ThrowIfArgument(vertices.Length < 3, nameof(vertices), "Mesh must contain at least three vertic
 46345        SwiftThrowHelper.ThrowIfArgument(triangles.Length < 3, nameof(triangles), "Mesh must contain at least one triang
 46346        SwiftThrowHelper.ThrowIfArgument(triangles.Length % 3 != 0, nameof(triangles), "Triangle index count must be div
 46347        SwiftThrowHelper.ThrowIfArgument(vertices.Length > PhysicsMesh.MaxVertexCount, nameof(vertices), "Mesh vertex co
 46348        SwiftThrowHelper.ThrowIfArgument(triangles.Length / 3 > PhysicsMesh.MaxTriangleCount, nameof(triangles), "Mesh t
 349
 1552350        for (int i = 0; i < triangles.Length; i++)
 351        {
 732352            int vertexIndex = triangles[i];
 732353            SwiftThrowHelper.ThrowIfArgument(
 732354                vertexIndex < 0 || vertexIndex >= vertices.Length,
 732355                nameof(triangles),
 732356                "Triangle index references a vertex outside the source vertex array.");
 357        }
 44358    }
 359
 360    /// <inheritdoc/>
 361    public bool Equals(ColliderShapeDefinition other)
 362    {
 19363        if (Kind != other.Kind
 19364            || Radius != other.Radius
 19365            || Height != other.Height
 19366            || Size != other.Size
 19367            || MeshInertiaPolicy != other.MeshInertiaPolicy
 19368            || _hasMaterial != other._hasMaterial
 19369            || (_hasMaterial && _material != other._material)
 19370            || MeshVertexCount != other.MeshVertexCount
 19371            || MeshTriangleIndexCount != other.MeshTriangleIndexCount)
 372        {
 11373            return false;
 374        }
 375
 46376        for (int i = 0; i < MeshVertexCount; i++)
 377        {
 16378            if (_meshVertices![i] != other._meshVertices![i])
 1379                return false;
 380        }
 381
 40382        for (int i = 0; i < MeshTriangleIndexCount; i++)
 383        {
 14384            if (_meshTriangles![i] != other._meshTriangles![i])
 1385                return false;
 386        }
 387
 6388        return true;
 389    }
 390
 391    /// <inheritdoc/>
 392    public override bool Equals(object? obj) =>
 19393        obj is ColliderShapeDefinition other && Equals(other);
 394
 395    /// <inheritdoc/>
 396    public override int GetHashCode()
 397    {
 398        unchecked
 399        {
 4400            int hash = 17;
 4401            hash = hash * 31 + Kind.GetHashCode();
 4402            hash = hash * 31 + Radius.GetHashCode();
 4403            hash = hash * 31 + Height.GetHashCode();
 4404            hash = hash * 31 + Size.GetHashCode();
 4405            hash = hash * 31 + MeshInertiaPolicy.GetHashCode();
 4406            hash = hash * 31 + _hasMaterial.GetHashCode();
 4407            if (_hasMaterial)
 2408                hash = hash * 31 + _material.GetHashCode();
 409
 20410            for (int i = 0; i < MeshVertexCount; i++)
 6411                hash = hash * 31 + _meshVertices![i].GetHashCode();
 20412            for (int i = 0; i < MeshTriangleIndexCount; i++)
 6413                hash = hash * 31 + _meshTriangles![i].GetHashCode();
 414
 4415            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) =>
 1421        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) =>
 1425        !left.Equals(right);
 426}

Methods/Properties

.ctor(Gravitas.Colliders.ColliderShapeDefinitionKind,FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,FixedMathSharp.Vector3d,Gravitas.Colliders.MeshInertiaPolicy,FixedMathSharp.Vector3d[],System.Int32[],System.Nullable`1<Gravitas.Materials.PhysicsMaterial>)
get_Material()
get_HasMaterial()
get_MeshVertexCount()
get_MeshTriangleIndexCount()
Sphere(FixedMathSharp.Fixed64,System.Nullable`1<Gravitas.Materials.PhysicsMaterial>)
Capsule(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Nullable`1<Gravitas.Materials.PhysicsMaterial>)
Cuboid(FixedMathSharp.Vector3d,System.Nullable`1<Gravitas.Materials.PhysicsMaterial>)
Cylinder(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Nullable`1<Gravitas.Materials.PhysicsMaterial>)
Cone(FixedMathSharp.Fixed64,FixedMathSharp.Fixed64,System.Nullable`1<Gravitas.Materials.PhysicsMaterial>)
ConvexMesh(FixedMathSharp.Vector3d[],System.Int32[],Gravitas.Colliders.MeshInertiaPolicy,System.Nullable`1<Gravitas.Materials.PhysicsMaterial>)
GetMeshVertex(System.Int32)
GetMeshTriangleIndex(System.Int32)
CreateCollider()
CreateRuntimeCollider()
GetMeshVerticesForRuntime()
GetMeshTrianglesForRuntime()
EnsureDefined()
EnsureKind(Gravitas.Colliders.ColliderShapeDefinitionKind)
EnsureMeshDefinition()
ValidateRadius(FixedMathSharp.Fixed64)
ValidateHeight(FixedMathSharp.Fixed64)
ValidateSize(FixedMathSharp.Vector3d)
ValidateMeshInertiaPolicy(Gravitas.Colliders.MeshInertiaPolicy)
ValidateMeshInput(FixedMathSharp.Vector3d[],System.Int32[])
Equals(Gravitas.Colliders.ColliderShapeDefinition)
Equals(System.Object)
GetHashCode()
op_Equality(Gravitas.Colliders.ColliderShapeDefinition,Gravitas.Colliders.ColliderShapeDefinition)
op_Inequality(Gravitas.Colliders.ColliderShapeDefinition,Gravitas.Colliders.ColliderShapeDefinition)