mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-01 08:18:20 -04:00
230 lines
7.2 KiB
C#
230 lines
7.2 KiB
C#
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// Provides ability to generate a physics body for a <see cref="Model"/> at runtime.
|
|
/// See <see cref="ModelBuilder.AddBody"/>
|
|
/// </summary>
|
|
public sealed class PhysicsBodyBuilder
|
|
{
|
|
/// <summary>
|
|
/// The mass of the body in kilograms.
|
|
/// Set to <c>0</c> to calculate automatically from its shapes and density.
|
|
/// </summary>
|
|
public float Mass { get; set; }
|
|
|
|
/// <summary>
|
|
/// The surface properties applied to this body.
|
|
/// </summary>
|
|
public Surface Surface { get; set; }
|
|
|
|
/// <summary>
|
|
/// The bind pose transform used when attaching this body to a bone.
|
|
/// </summary>
|
|
public Transform BindPose { get; set; }
|
|
|
|
/// <summary>
|
|
/// The name of the bone this body is attached to, or <c>null</c> if not attached.
|
|
/// </summary>
|
|
public string BoneName { get; set; }
|
|
|
|
internal struct SphereShape { public Sphere Sphere; }
|
|
internal struct CapsuleShape { public Capsule Capsule; }
|
|
internal struct HullShape { public Vector3[] Points; public Transform Transform; public HullSimplify? Simplify; }
|
|
internal struct MeshShape { public Vector3[] Vertices; public uint[] Indices; public byte[] Materials; }
|
|
|
|
internal List<SphereShape> Spheres = [];
|
|
internal List<CapsuleShape> Capsules = [];
|
|
internal List<HullShape> Hulls = [];
|
|
internal List<MeshShape> Meshes = [];
|
|
|
|
internal PhysicsBodyBuilder()
|
|
{
|
|
}
|
|
|
|
/// <inheritdoc cref="Mass"/>
|
|
public PhysicsBodyBuilder SetMass( float mass )
|
|
{
|
|
Mass = mass;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc cref="Surface"/>
|
|
public PhysicsBodyBuilder SetSurface( Surface surface )
|
|
{
|
|
Surface = surface;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc cref="BindPose"/>
|
|
public PhysicsBodyBuilder SetBindPose( Transform bindPose )
|
|
{
|
|
BindPose = bindPose;
|
|
return this;
|
|
}
|
|
|
|
/// <inheritdoc cref="BoneName"/>
|
|
public PhysicsBodyBuilder SetBoneName( string boneName )
|
|
{
|
|
BoneName = boneName;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a sphere shape.
|
|
/// </summary>
|
|
public PhysicsBodyBuilder AddSphere( Sphere sphere, Transform? transform = default )
|
|
{
|
|
if ( transform.HasValue )
|
|
{
|
|
sphere.Radius *= transform.Value.UniformScale;
|
|
sphere.Center += transform.Value.Position;
|
|
}
|
|
Spheres.Add( new SphereShape { Sphere = sphere } );
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a capsule shape.
|
|
/// </summary>
|
|
public PhysicsBodyBuilder AddCapsule( Capsule capsule, Transform? transform = default )
|
|
{
|
|
if ( transform.HasValue )
|
|
{
|
|
capsule.Radius *= transform.Value.UniformScale;
|
|
capsule.CenterA = transform.Value.PointToWorld( capsule.CenterA );
|
|
capsule.CenterB = transform.Value.PointToWorld( capsule.CenterB );
|
|
}
|
|
Capsules.Add( new CapsuleShape { Capsule = capsule } );
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The method used to simplify a hull.
|
|
/// </summary>
|
|
public enum SimplifyMethod
|
|
{
|
|
/// <summary>Quadratic Error Metric - prioritizes preserving shape accuracy.</summary>
|
|
QEM,
|
|
|
|
/// <summary>Iterative Vertex Removal - removes vertices gradually.</summary>
|
|
IVR,
|
|
|
|
/// <summary>No simplification - use the exact points provided.</summary>
|
|
None,
|
|
|
|
/// <summary>Iterative Face Removal - removes faces to reduce complexity.</summary>
|
|
IFR
|
|
}
|
|
|
|
/// <summary>
|
|
/// Settings for simplifying a hull shape.
|
|
/// </summary>
|
|
public struct HullSimplify
|
|
{
|
|
/// <summary>Maximum allowed angle change between faces, in degrees.</summary>
|
|
public float AngleTolerance;
|
|
|
|
/// <summary>Maximum distance a vertex can be moved during simplification.</summary>
|
|
public float DistanceTolerance;
|
|
|
|
/// <summary>Maximum number of faces allowed after simplification.</summary>
|
|
public int MaxFaces;
|
|
|
|
/// <summary>Maximum number of edges allowed after simplification.</summary>
|
|
public int MaxEdges;
|
|
|
|
/// <summary>Maximum number of vertices allowed after simplification.</summary>
|
|
public int MaxVerts;
|
|
|
|
/// <summary>The simplification method to use.</summary>
|
|
public SimplifyMethod Method;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a convex hull shape to this body.
|
|
/// </summary>
|
|
/// <param name="points">The points making up the hull.</param>
|
|
/// <param name="transform">Optional local transform of the hull relative to the body.</param>
|
|
/// <param name="simplify">Optional settings to reduce the complexity of the hull.</param>
|
|
/// <exception cref="ArgumentException">Thrown if less than 3 points are provided.</exception>
|
|
public PhysicsBodyBuilder AddHull( Span<Vector3> points, Transform? transform = default, HullSimplify? simplify = default )
|
|
{
|
|
if ( points.Length < 3 )
|
|
throw new ArgumentException( "Hull must have at least 3 points.", nameof( points ) );
|
|
|
|
Hulls.Add( new HullShape { Points = points.ToArray(), Transform = transform ?? Transform.Zero, Simplify = simplify } );
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a triangle mesh shape to this body.
|
|
/// </summary>
|
|
/// <param name="vertices">The mesh vertex positions.</param>
|
|
/// <param name="indices">
|
|
/// The mesh indices, grouped in triples to form triangles.
|
|
/// Must be a multiple of 3.
|
|
/// </param>
|
|
/// <param name="materials">
|
|
/// Optional per-vertex material IDs.
|
|
/// Length must match <paramref name="vertices"/> count or be empty.
|
|
/// </param>
|
|
/// <exception cref="ArgumentException">
|
|
/// Thrown if the mesh has fewer than 3 vertices,
|
|
/// if indices are not a multiple of 3,
|
|
/// or if material count does not match vertex count.
|
|
/// </exception>
|
|
/// <exception cref="ArgumentOutOfRangeException">
|
|
/// Thrown if any index refers to a vertex that does not exist.
|
|
/// </exception>
|
|
public PhysicsBodyBuilder AddMesh( Span<Vector3> vertices, Span<uint> indices, Span<byte> materials )
|
|
{
|
|
if ( vertices.Length < 3 )
|
|
throw new ArgumentException( "Mesh must have at least 3 vertices.", nameof( vertices ) );
|
|
|
|
if ( indices.Length < 3 || indices.Length % 3 != 0 )
|
|
throw new ArgumentException( "Mesh indices length must be at least 3 and a multiple of 3 (triangles).", nameof( indices ) );
|
|
|
|
if ( materials.Length > 0 && materials.Length != vertices.Length )
|
|
throw new ArgumentException( "Materials array length must match vertex count, or be empty.", nameof( materials ) );
|
|
|
|
for ( int i = 0; i < indices.Length; i++ )
|
|
{
|
|
if ( indices[i] >= (uint)vertices.Length )
|
|
throw new ArgumentOutOfRangeException( nameof( indices ), $"Index {indices[i]} is out of range for {vertices.Length} vertices." );
|
|
}
|
|
|
|
Meshes.Add( new MeshShape { Vertices = vertices.ToArray(), Indices = indices.ToArray(), Materials = materials.ToArray() } );
|
|
return this;
|
|
}
|
|
}
|
|
|
|
partial class ModelBuilder
|
|
{
|
|
private readonly List<PhysicsBodyBuilder> _bodies = [];
|
|
|
|
/// <summary>
|
|
/// Adds a new physics body to this object.
|
|
/// </summary>
|
|
/// <param name="mass">The mass of the body. Default is <c>0</c>.</param>
|
|
/// <param name="surface">The surface properties to apply. Default is <c>default</c>.</param>
|
|
/// <param name="boneName">
|
|
/// Optional name of the bone this body is attached to.
|
|
/// Leave empty for non-skeletal bodies.
|
|
/// </param>
|
|
/// <returns>
|
|
/// A new <see cref="PhysicsBodyBuilder"/> for configuring the body.
|
|
/// </returns>
|
|
public PhysicsBodyBuilder AddBody( float mass = default, Surface surface = default, string boneName = default )
|
|
{
|
|
var builder = new PhysicsBodyBuilder
|
|
{
|
|
Mass = mass,
|
|
Surface = surface,
|
|
BoneName = boneName,
|
|
BindPose = Transform.Zero
|
|
};
|
|
_bodies.Add( builder );
|
|
return builder;
|
|
}
|
|
}
|