Files
sbox-public/engine/Sandbox.Engine/Scene/Components/Collider/HullCollider.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

203 lines
5.4 KiB
C#

using System.Runtime.InteropServices;
namespace Sandbox;
/// <summary>
/// Defines a box, cone, or cylinder hull collider.
/// </summary>
[Expose]
[Title( "Collider - Hull" )]
[Category( "Physics" )]
[Icon( "grain" )]
[Alias( "ColliderHullComponent" )]
public sealed class HullCollider : Collider
{
public enum PrimitiveType
{
Box,
Cone,
Cylinder,
[Hide]
Points,
}
/// <summary>
/// The type of primitive.
/// </summary>
[Property, Group( "Hull" ), Resize]
public PrimitiveType Type { get; set; }
/// <summary>
/// The center of the primitive relative to this GameObject.
/// </summary>
[Property, Group( "Hull" ), Resize]
public Vector3 Center { get; set; } = 0.0f;
/// <summary>
/// The size of the box, from corner to corner.
/// </summary>
[Property, Title( "Size" ), Group( "Hull" ), Resize]
[ShowIf( nameof( Type ), PrimitiveType.Box )]
public Vector3 BoxSize { get; set; } = 50.0f;
[Property, Title( "Height" ), Group( "Hull" ), Resize]
[ShowIf( nameof( Type ), PrimitiveType.Cone )]
[ShowIf( nameof( Type ), PrimitiveType.Cylinder )]
public float Height { get; set; } = 50.0f;
[Property, Title( "Radius" ), Group( "Hull" ), Resize]
[ShowIf( nameof( Type ), PrimitiveType.Cone )]
[ShowIf( nameof( Type ), PrimitiveType.Cylinder )]
public float Radius { get; set; } = 25.0f;
[Property, Title( "Tip Radius" ), Group( "Hull" ), Resize]
[ShowIf( nameof( Type ), PrimitiveType.Cone )]
public float Radius2 { get; set; } = 0.0f;
[Property, Title( "Slices" ), Group( "Hull" ), Resize, Range( 4, 32 )]
[ShowIf( nameof( Type ), PrimitiveType.Cone )]
[ShowIf( nameof( Type ), PrimitiveType.Cylinder )]
public int Slices { get; set; } = 16;
[Property, Hide, Resize]
public List<Vector3> Points { get; set; } = new();
private PhysicsShape Shape;
protected override void DrawGizmos()
{
if ( !Gizmo.IsSelected && !Gizmo.IsHovered )
return;
Gizmo.Draw.LineThickness = 1;
Gizmo.Draw.Color = Gizmo.Colors.Green.WithAlpha( Gizmo.IsSelected ? 1.0f : 0.2f );
if ( Type == PrimitiveType.Box )
{
var box = BBox.FromPositionAndSize( Center, BoxSize );
Gizmo.Draw.LineBBox( box );
}
else if ( Type == PrimitiveType.Cylinder )
{
var halfHeight = Height * 0.5f;
Gizmo.Draw.LineCylinder(
Center + Vector3.Down * halfHeight,
Center + Vector3.Up * halfHeight,
Radius, Radius, Slices );
}
else if ( Type == PrimitiveType.Cone )
{
var halfHeight = Height * 0.5f;
Gizmo.Draw.LineCylinder(
Center + Vector3.Down * halfHeight,
Center + Vector3.Up * halfHeight,
Radius, Radius2, Slices );
}
}
private Vector3[] GetVertices( float height, float radius1, float radius2, int slices, Vector3 center, Vector3 scale )
{
slices = slices.Clamp( 4, 128 );
var vertexCount = 2 * slices;
var points = new Vector3[vertexCount];
var alpha = 0.0f;
var deltaAlpha = MathF.PI * 2 / slices;
var halfHeight = height * 0.5f;
for ( int i = 0; i < slices; ++i )
{
var sinAlpha = MathF.Sin( alpha );
var cosAlpha = MathF.Cos( alpha );
var a = center + new Vector3( radius1 * cosAlpha, radius1 * sinAlpha, -halfHeight );
var b = center + new Vector3( radius2 * cosAlpha, radius2 * sinAlpha, halfHeight );
points[2 * i + 0] = a * scale;
points[2 * i + 1] = b * scale;
alpha += deltaAlpha;
}
return points;
}
internal override void UpdateShape()
{
if ( !Shape.IsValid() )
return;
var body = Rigidbody;
var world = Transform.TargetWorld;
var local = body.IsValid() ? body.Transform.TargetWorld.ToLocal( world ) : global::Transform.Zero;
if ( Type == PrimitiveType.Box )
{
var box = BBox.FromPositionAndSize( Center, BoxSize );
box.Mins *= world.Scale;
box.Maxs *= world.Scale;
box.Mins += local.Position;
box.Maxs += local.Position;
Shape.UpdateBoxShape( box.Center, local.Rotation, box.Size * 0.5f );
}
else if ( Type == PrimitiveType.Cone )
{
var vertices = GetVertices( Height, Radius, Radius2, Slices, Center, world.Scale );
Shape.UpdateHull( local.Position, local.Rotation, vertices );
}
else if ( Type == PrimitiveType.Cylinder )
{
var vertices = GetVertices( Height, Radius, Radius, Slices, Center, world.Scale );
Shape.UpdateHull( local.Position, local.Rotation, vertices );
}
else if ( Type == PrimitiveType.Points )
{
Shape.UpdateHull( local.Position, local.Rotation, Points.Select( x => x * world.Scale ).ToArray() );
}
CalculateLocalBounds();
}
protected override IEnumerable<PhysicsShape> CreatePhysicsShapes( PhysicsBody body, Transform local )
{
var scale = WorldScale;
Shape = null;
if ( Type == PrimitiveType.Box )
{
var box = BBox.FromPositionAndSize( Center, BoxSize );
box.Mins *= scale;
box.Maxs *= scale;
box.Mins += local.Position;
box.Maxs += local.Position;
Shape = body.AddBoxShape( box, local.Rotation );
}
else if ( Type == PrimitiveType.Cone )
{
var vertices = GetVertices( Height, Radius, Radius2, Slices, Center, scale );
Shape = body.AddHullShape( local.Position, local.Rotation, vertices );
}
else if ( Type == PrimitiveType.Cylinder )
{
var vertices = GetVertices( Height, Radius, Radius, Slices, Center, scale );
Shape = body.AddHullShape( local.Position, local.Rotation, vertices );
}
else if ( Type == PrimitiveType.Points )
{
Shape = body.AddHullShape( local.Position, local.Rotation, Points.Select( x => x * scale ).ToArray() );
}
else
{
yield break;
}
yield return Shape;
}
}