mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-01 00:08:05 -04:00
220 lines
6.7 KiB
C#
220 lines
6.7 KiB
C#
namespace SceneTests.GameObjects;
|
|
|
|
[TestClass]
|
|
public class ComputePenetrationTest
|
|
{
|
|
const float Tolerance = 1.0f;
|
|
|
|
static BoxCollider CreateBox( Scene scene, Vector3 center, float size = 50f )
|
|
{
|
|
var go = scene.CreateObject();
|
|
go.WorldPosition = center;
|
|
|
|
var collider = go.Components.Create<BoxCollider>();
|
|
collider.Scale = new Vector3( size );
|
|
|
|
return collider;
|
|
}
|
|
|
|
static SphereCollider CreateSphere( Scene scene, Vector3 center, float radius )
|
|
{
|
|
var go = scene.CreateObject();
|
|
go.WorldPosition = center;
|
|
|
|
var collider = go.Components.Create<SphereCollider>();
|
|
collider.Radius = radius;
|
|
|
|
return collider;
|
|
}
|
|
|
|
static PhysicsBody CreateBoxBody( Scene scene, Vector3 center, float size = 50f )
|
|
{
|
|
var go = scene.CreateObject();
|
|
go.WorldPosition = center;
|
|
|
|
var rigidbody = go.Components.Create<Rigidbody>();
|
|
var collider = go.Components.Create<BoxCollider>();
|
|
collider.Scale = new Vector3( size );
|
|
|
|
return rigidbody.PhysicsBody;
|
|
}
|
|
|
|
static void AssertDirection( Vector3 expected, Vector3 actual )
|
|
{
|
|
Assert.AreEqual( expected.x, actual.x, 0.05f );
|
|
Assert.AreEqual( expected.y, actual.y, 0.05f );
|
|
Assert.AreEqual( expected.z, actual.z, 0.05f );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Two overlapping boxes report a penetration along the overlapping axis, with the
|
|
/// distance equal to the overlap and the direction pushing the first box clear.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void OverlappingBoxesReturnsTrue()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateBox( scene, Vector3.Zero );
|
|
var b = CreateBox( scene, new Vector3( 40, 0, 0 ) );
|
|
|
|
Assert.IsTrue( a.ComputePenetration( b, out var direction, out var distance ) );
|
|
Assert.AreEqual( 10f, distance, Tolerance );
|
|
AssertDirection( new Vector3( -1, 0, 0 ), direction );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Separated boxes don't overlap, so the result is false and the outputs are left empty.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void SeparatedBoxesReturnsFalse()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateBox( scene, Vector3.Zero );
|
|
var b = CreateBox( scene, new Vector3( 100, 0, 0 ) );
|
|
|
|
Assert.IsFalse( a.ComputePenetration( b, out var direction, out var distance ) );
|
|
Assert.AreEqual( Vector3.Zero, direction );
|
|
Assert.AreEqual( 0f, distance );
|
|
}
|
|
|
|
/// <summary>
|
|
/// When boxes overlap on multiple axes the result is the smallest move out, along the
|
|
/// axis with the least overlap.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void PicksShortestAxis()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateBox( scene, Vector3.Zero );
|
|
var b = CreateBox( scene, new Vector3( 45, 10, 0 ) );
|
|
|
|
Assert.IsTrue( a.ComputePenetration( b, out var direction, out var distance ) );
|
|
Assert.AreEqual( 5f, distance, Tolerance );
|
|
AssertDirection( new Vector3( -1, 0, 0 ), direction );
|
|
}
|
|
|
|
/// <summary>
|
|
/// A deep overlap reports the full overlap distance on the shortest axis.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void DeepOverlap()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateBox( scene, Vector3.Zero );
|
|
var b = CreateBox( scene, new Vector3( 5, 0, 0 ) );
|
|
|
|
Assert.IsTrue( a.ComputePenetration( b, out var direction, out var distance ) );
|
|
Assert.AreEqual( 45f, distance, Tolerance );
|
|
AssertDirection( new Vector3( -1, 0, 0 ), direction );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Two overlapping spheres report the overlap of their radii along the line between them.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void OverlappingSpheres()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateSphere( scene, Vector3.Zero, 32f );
|
|
var b = CreateSphere( scene, new Vector3( 40, 0, 0 ), 32f );
|
|
|
|
Assert.IsTrue( a.ComputePenetration( b, out var direction, out var distance ) );
|
|
Assert.AreEqual( 24f, distance, Tolerance );
|
|
AssertDirection( new Vector3( -1, 0, 0 ), direction );
|
|
}
|
|
|
|
/// <summary>
|
|
/// A box and a sphere of different shape types collide correctly, and querying from either
|
|
/// side returns the same distance with opposite directions.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void BoxAndSphereMixedTypes()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var box = CreateBox( scene, Vector3.Zero );
|
|
var sphere = CreateSphere( scene, new Vector3( 50, 0, 0 ), 32f );
|
|
|
|
Assert.IsTrue( box.ComputePenetration( sphere, out var boxDirection, out var boxDistance ) );
|
|
Assert.AreEqual( 7f, boxDistance, Tolerance );
|
|
AssertDirection( new Vector3( -1, 0, 0 ), boxDirection );
|
|
|
|
Assert.IsTrue( sphere.ComputePenetration( box, out var sphereDirection, out var sphereDistance ) );
|
|
Assert.AreEqual( 7f, sphereDistance, Tolerance );
|
|
AssertDirection( new Vector3( 1, 0, 0 ), sphereDirection );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Querying the penetration from either body gives the same distance and opposite directions.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void DirectionIsSymmetric()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateBox( scene, Vector3.Zero );
|
|
var b = CreateBox( scene, new Vector3( 0, 30, 0 ) );
|
|
|
|
Assert.IsTrue( a.ComputePenetration( b, out var forward, out var forwardDistance ) );
|
|
Assert.IsTrue( b.ComputePenetration( a, out var backward, out var backwardDistance ) );
|
|
|
|
Assert.AreEqual( forwardDistance, backwardDistance, Tolerance );
|
|
AssertDirection( -forward, backward );
|
|
}
|
|
|
|
/// <summary>
|
|
/// The transform overload tests against a supplied transform rather than the body's real
|
|
/// position, and doesn't move anything.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void TransformOverrideUsesPassedTransform()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateBoxBody( scene, Vector3.Zero );
|
|
var b = CreateBoxBody( scene, new Vector3( 200, 0, 0 ) );
|
|
|
|
// At its real position b is far away, so there's no penetration.
|
|
Assert.IsFalse( a.ComputePenetration( b, out _, out _ ) );
|
|
|
|
// Placing b at an overlapping transform reports a penetration without moving b.
|
|
Assert.IsTrue( a.ComputePenetration( b, new Transform( new Vector3( 40, 0, 0 ) ), out var direction, out var distance ) );
|
|
Assert.AreEqual( 10f, distance, Tolerance );
|
|
AssertDirection( new Vector3( -1, 0, 0 ), direction );
|
|
|
|
Assert.AreEqual( 200f, b.Position.x, Tolerance );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Translating the first body by direction * distance separates the two bodies.
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void TranslatingByResultSeparates()
|
|
{
|
|
var scene = new Scene();
|
|
using var sceneScope = scene.Push();
|
|
|
|
var a = CreateBoxBody( scene, Vector3.Zero );
|
|
var b = CreateBoxBody( scene, new Vector3( 40, 0, 0 ) );
|
|
|
|
Assert.IsTrue( a.ComputePenetration( b, out var direction, out var distance ) );
|
|
|
|
a.Position += direction * (distance + 1f);
|
|
|
|
Assert.IsFalse( a.ComputePenetration( b, out _, out _ ) );
|
|
}
|
|
}
|