2566 lines
110 KiB
C++
2566 lines
110 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "HeadlessChaosTestGJK.h"
|
|
|
|
#include "HeadlessChaos.h"
|
|
#include "HeadlessChaosTestUtility.h"
|
|
#include "Chaos/GJK.h"
|
|
#include "Chaos/Capsule.h"
|
|
#include "Chaos/Convex.h"
|
|
#include "Chaos/GJK.h"
|
|
#include "Chaos/ImplicitObjectScaled.h"
|
|
#include "Chaos/Collision/PBDCollisionConstraint.h"
|
|
#include "Chaos/Triangle.h"
|
|
#include "Chaos/TriangleMeshImplicitObject.h"
|
|
#include "Chaos/TriangleRegister.h"
|
|
|
|
namespace ChaosTest
|
|
{
|
|
using namespace Chaos;
|
|
|
|
//for each simplex test:
|
|
//- points get removed
|
|
// - points off simplex return false
|
|
//- points in simplex return true
|
|
//- degenerate simplex
|
|
|
|
void SimplexLine()
|
|
{
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1}, {-1,-1,1} };
|
|
int32 Idxs[] = { 0,1 };
|
|
int32 NumVerts = 2;
|
|
const FVec3 ClosestPoint = LineSimplexFindOrigin(Simplex, Idxs, NumVerts, Barycentric);
|
|
EXPECT_EQ(NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
}
|
|
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1}, {1,1,1} };
|
|
int32 Idxs[] = { 0,1 };
|
|
int32 NumVerts = 2;
|
|
const FVec3 ClosestPoint = LineSimplexFindOrigin(Simplex, Idxs, NumVerts, Barycentric);
|
|
EXPECT_EQ(NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
}
|
|
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {1,1,1}, {1,2,3} };
|
|
int32 Idxs[] = { 0,1 };
|
|
int32 NumVerts = 2;
|
|
const FVec3 ClosestPoint = LineSimplexFindOrigin(Simplex, Idxs, NumVerts, Barycentric);
|
|
EXPECT_EQ(NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 1);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
}
|
|
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {10,11,12}, {1,2,3} };
|
|
int32 Idxs[] = { 0,1 };
|
|
int32 NumVerts = 2;
|
|
const FVec3 ClosestPoint = LineSimplexFindOrigin(Simplex, Idxs, NumVerts, Barycentric);
|
|
EXPECT_EQ(NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 3);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 1);
|
|
EXPECT_EQ(Idxs[0], 1);
|
|
}
|
|
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {1,1,1}, {1,1,1} };
|
|
int32 Idxs[] = { 0,1 };
|
|
int32 NumVerts = 2;
|
|
const FVec3 ClosestPoint = LineSimplexFindOrigin(Simplex, Idxs, NumVerts, Barycentric);
|
|
EXPECT_EQ(NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 1);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
}
|
|
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {1,-1e-16,1}, {1,1e-16,1} };
|
|
int32 Idxs[] = { 0,1 };
|
|
int32 NumVerts = 2;
|
|
const FVec3 ClosestPoint = LineSimplexFindOrigin(Simplex, Idxs, NumVerts, Barycentric);
|
|
EXPECT_EQ(NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 1);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
}
|
|
}
|
|
|
|
void SimplexTriangle()
|
|
{
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1}, {-1,1,-1}, {-2,1,-1} };
|
|
FSimplex Idxs = { 0,1, 2 };
|
|
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], -1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
}
|
|
|
|
{
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1},{-2,1,-1}, {-1,1,-1} };
|
|
FSimplex Idxs = { 0,1, 2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], -1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 2);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[2], 0.5);
|
|
}
|
|
|
|
{
|
|
//corner
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {1,1,1},{2,1,1}, {2,2,1} };
|
|
FSimplex Idxs = { 1,0, 2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 1);
|
|
}
|
|
|
|
{
|
|
//corner equal
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {0,0,0},{2,1,1}, {2,2,1} };
|
|
FSimplex Idxs = { 0,1, 2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 1);
|
|
}
|
|
|
|
{
|
|
//edge equal
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,0,0},{1,0,0}, {0,2,0} };
|
|
FSimplex Idxs = { 2,0, 1 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
}
|
|
|
|
{
|
|
//triangle equal
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,0,-1},{1,0,-1}, {0,0,1} };
|
|
FSimplex Idxs = { 0,1, 2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 3);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_EQ(Idxs[2], 2);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[2], 0.5);
|
|
}
|
|
|
|
{
|
|
//co-linear
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1},{-1,1,-1}, {-1,1.2,-1} };
|
|
FSimplex Idxs = { 0,1, 2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], -1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1); //degenerate triangle throws out newest point
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
}
|
|
|
|
{
|
|
//single point
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1},{-1,-1,-1}, {-1,-1,-1} };
|
|
FSimplex Idxs = { 0,2, 1 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], -1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 1);
|
|
}
|
|
|
|
{
|
|
//corner perfect split
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,0},{1,-1,0}, {0,-0.5,0} };
|
|
FSimplex Idxs = { 0,2, 1 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], -0.5);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 2);
|
|
EXPECT_FLOAT_EQ(Barycentric[2], 1);
|
|
}
|
|
|
|
{
|
|
//triangle face correct distance
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1},{1,-1,-1}, {0,1,-1} };
|
|
FSimplex Idxs = { 0,1,2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 3);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], -1);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_EQ(Idxs[2], 2);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[2], 0.5);
|
|
}
|
|
|
|
{
|
|
//tiny triangle middle point
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1e-9,-1e-9,-1e-9},{-1e-9,1e-9,-1e-9}, {-1e-9,0,1e-9} };
|
|
FSimplex Idxs = { 0,1,2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 3);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], -1e-9);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_EQ(Idxs[2], 2);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[2], 0.5);
|
|
}
|
|
|
|
{
|
|
//non cartesian triangle plane
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {2, 0, -1}, {0, 2, -1}, {1, 1, 1} };
|
|
FSimplex Idxs = { 0,1,2 };
|
|
const FVec3 ClosestPoint = TriangleSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 3);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_EQ(Idxs[2], 2);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.25);
|
|
EXPECT_FLOAT_EQ(Barycentric[2], 0.5);
|
|
}
|
|
}
|
|
|
|
void SimplexTetrahedron()
|
|
{
|
|
{
|
|
//top corner
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1}, {1,-1,-1}, {0,1,-1}, {0,0,-0.5} };
|
|
FSimplex Idxs = { 0,1,2,3 };
|
|
const FVec3 ClosestPoint = TetrahedronSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], -0.5);
|
|
EXPECT_EQ(Idxs[0], 3);
|
|
EXPECT_FLOAT_EQ(Barycentric[3], 1);
|
|
}
|
|
|
|
{
|
|
//inside
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,-1}, {1,-1,-1}, {0,1,-1}, {0,0,0.5} };
|
|
FSimplex Idxs = { 0,1,2,3 };
|
|
const FVec3 ClosestPoint = TetrahedronSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 4);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_EQ(Idxs[2], 2);
|
|
EXPECT_EQ(Idxs[3], 3);
|
|
EXPECT_FLOAT_EQ(Barycentric[0] + Barycentric[1] + Barycentric[2] + Barycentric[3], 1);
|
|
}
|
|
|
|
{
|
|
//face
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {0,0,-1.5}, {-1,-1,-1}, {1,-1,-1}, {0,1,-1} };
|
|
FSimplex Idxs = { 0,1,2,3 };
|
|
const FVec3 ClosestPoint = TetrahedronSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 3);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], -1);
|
|
EXPECT_EQ(Idxs[0], 1);
|
|
EXPECT_EQ(Idxs[1], 2);
|
|
EXPECT_EQ(Idxs[2], 3);
|
|
EXPECT_FLOAT_EQ(Barycentric[1] + Barycentric[2] + Barycentric[3], 1);
|
|
}
|
|
|
|
{
|
|
//edge
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,0}, {1,-1,0}, {0,-1,-1}, {0, -2, -1} };
|
|
FSimplex Idxs = { 0,1,2,3 };
|
|
const FVec3 ClosestPoint = TetrahedronSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
}
|
|
|
|
{
|
|
//degenerate
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-1,-1,0}, {1,-1,0}, {0,-1,-1}, {0, -1, -0.5} };
|
|
FSimplex Idxs = { 0,1,2,3 };
|
|
const FVec3 ClosestPoint = TetrahedronSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 2);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_FLOAT_EQ(Barycentric[0], 0.5);
|
|
EXPECT_FLOAT_EQ(Barycentric[1], 0.5);
|
|
}
|
|
|
|
{
|
|
//wide angle, bad implementation would return edge but it's really a face
|
|
FReal Barycentric[4];
|
|
const FVec3 Simplex[] = { {-10000,-1,10000}, {1,-1,10000}, {4,-3,10000}, {1, -1, -10000} };
|
|
FSimplex Idxs = { 0,1,2,3 };
|
|
const FVec3 ClosestPoint = TetrahedronSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 3);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[0], 0);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[1], -1);
|
|
EXPECT_FLOAT_EQ(ClosestPoint[2], 0);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_EQ(Idxs[2], 3);
|
|
EXPECT_FLOAT_EQ(Barycentric[0] + Barycentric[1] + Barycentric[3], 1);
|
|
}
|
|
|
|
// LWC-TODO : this is failing when using LWC, disabling it for now to avoid blocking builds
|
|
#if 0
|
|
{
|
|
// Previous failing case observed with Voronoi region implementation - Not quite degenerate (totally degenerate cases work)
|
|
FReal Barycentric[4];
|
|
FVec3 Simplex[] = { { -15.9112930, -15.2787428, 1.33070087 },
|
|
{ 1.90487099, 2.25161266, 0.439208984 },
|
|
{ -15.8914719, -15.2915068, 1.34186459 },
|
|
{ 1.90874290, 2.24025059, 0.444719315 } };
|
|
|
|
FSimplex Idxs = { 0,1,2,3 };
|
|
const FVec3 ClosestPoint = TetrahedronSimplexFindOrigin(Simplex, Idxs, Barycentric);
|
|
EXPECT_EQ(Idxs.NumVerts, 3);
|
|
EXPECT_EQ(Idxs[0], 0);
|
|
EXPECT_EQ(Idxs[1], 1);
|
|
EXPECT_EQ(Idxs[2], 2);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//For each gjk test we should test:
|
|
// - thickness
|
|
// - transformed geometry
|
|
// - rotated geometry
|
|
// - degenerate cases
|
|
// - near miss, near hit
|
|
// - multiple initial dir
|
|
|
|
void GJKSphereSphereTest()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
Chaos::FSphere B(FVec3(4, 0, 0), 2);
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3::Identity, 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(-1.1, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//hit from thickness
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(-1.1, 0, 0), FRotation3::Identity), 0.105, InitialDir));
|
|
|
|
//miss with thickness
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(-1.1, 0, 0), FRotation3::Identity), 0.095, InitialDir));
|
|
|
|
//hit with rotation
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(6.5, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 1, InitialDir));
|
|
|
|
//miss with rotation
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(6.5, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 0.01, InitialDir));
|
|
|
|
//hit tiny
|
|
Chaos::FSphere Tiny(FVec3(0), 1e-2);
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, Tiny, FRigidTransform3(FVec3(15, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//miss tiny
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, Tiny, FRigidTransform3(FVec3(15 + 1e-1, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
}
|
|
}
|
|
|
|
|
|
void GJKSphereBoxTest()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
FAABB3 B(FVec3(-4, -2, -4), FVec3(4,2,4));
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(0.9, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//rotate and hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(3.1, 0, 0), FRotation3::FromVector(FVec3(0,0,PI*0.5))), 0, InitialDir));
|
|
|
|
//rotate and miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(2.9, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0, InitialDir));
|
|
|
|
//rotate and hit from thickness
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(2.9, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0.1, InitialDir));
|
|
|
|
//hit thin
|
|
FAABB3 Thin(FVec3(4, -2, -4), FVec3(4, 2, 4));
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, Thin, FRigidTransform3(FVec3(1+1e-2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, Thin, FRigidTransform3(FVec3(1 - 1e-2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//hit line
|
|
FAABB3 Line(FVec3(4, -2, 0), FVec3(4, 2, 0));
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, Line, FRigidTransform3(FVec3(1 + 1e-2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, Line, FRigidTransform3(FVec3(1 - 1e-2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
}
|
|
}
|
|
|
|
|
|
void GJKSphereCapsuleTest()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
FCapsule B(FVec3(0, 0, -3), FVec3(0, 0, 3), 3);
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(2-1e-2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//thickness
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), 1.01, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), 0.99, InitialDir));
|
|
|
|
//rotation hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(-1+1e-2, 0, 0), FRotation3::FromVector(FVec3(0,PI*0.5,0))), 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(-1-1e-2, 0, 0), FRotation3::FromVector(FVec3(0, PI*0.5, 0))), 0, InitialDir));
|
|
|
|
//degenerate
|
|
FCapsule Line(FVec3(0, 0, -3), FVec3(0, 0, 3), 0);
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, Line, FRigidTransform3(FVec3(5+1e-2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, Line, FRigidTransform3(FVec3(5 - 1e-2, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
}
|
|
}
|
|
|
|
|
|
void GJKSphereConvexTest()
|
|
{
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
|
|
{
|
|
//Tetrahedron
|
|
TArray<FConvex::FVec3Type> HullParticles;
|
|
HullParticles.SetNum(4);
|
|
HullParticles[0] = { -1,-1,-1 };
|
|
HullParticles[1] = { 1,-1,-1 };
|
|
HullParticles[2] = { 0,1,-1 };
|
|
HullParticles[3] = { 0,0,1 };
|
|
FConvex B(HullParticles, 0.0f);
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
//hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(5, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//near hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 + 1e-4, 1, 1), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 - 1e-2, 1, 1), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//rotated hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 + 1e-4, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0, InitialDir));
|
|
|
|
//rotated miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 - 1e-2, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0, InitialDir));
|
|
|
|
//rotated and inflated hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(3.5, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0.5 + 1e-4, InitialDir));
|
|
|
|
//rotated and inflated miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(3.5, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0.5 - 1e-2, InitialDir));
|
|
}
|
|
}
|
|
|
|
{
|
|
//Triangle
|
|
TArray<FConvex::FVec3Type> TriangleParticles;
|
|
TriangleParticles.SetNum(3);
|
|
TriangleParticles[0] = { -1,-1,-1 };
|
|
TriangleParticles[1] = { 1,-1,-1 };
|
|
TriangleParticles[2] = { 0,1,-1 };
|
|
FConvex B(TriangleParticles, 0.0f);
|
|
|
|
//triangle
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
//hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(5, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//near hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 + 1e-2, 1, 1), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 - 1e-2, 1, 1), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//rotated hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 + 1e-2, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0, InitialDir));
|
|
|
|
//rotated miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(4 - 1e-2, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0, InitialDir));
|
|
|
|
//rotated and inflated hit
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(3.5, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0.5 + 1e-2, InitialDir));
|
|
|
|
//rotated and inflated miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, B, FRigidTransform3(FVec3(3.5, 0, 1), FRotation3::FromVector(FVec3(0, 0, PI*0.5))), 0.5 - 1e-2, InitialDir));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void GJKConvexCapsule()
|
|
{
|
|
// The following tests are coming form a bug found in EPAComputeVisibilityBorder. The fix was adding an epsilon test, instead of a hard zero comparison.
|
|
TArray<FConvex::FVec3Type> HullParticles;
|
|
HullParticles.SetNum(12);
|
|
HullParticles[0] = { 100.000000, -460.000000, -100.000000 };
|
|
HullParticles[1] = { 100.000000, -460.000000, 100.000000 };
|
|
HullParticles[2] = { 400.000000, 0.00000000, 100.000000 };
|
|
HullParticles[3] = { 400.000000, 0.00000000, -100.000000 };
|
|
HullParticles[4] = { -100.000000, 390.000000, 100.000000 };
|
|
HullParticles[5] = { -500.000000, 0.00000000, 100.000000 };
|
|
HullParticles[6] = { -500.000000, 0.00000000, -100.000000 };
|
|
HullParticles[7] = { -100.000000, 390.000000, -100.000000 };
|
|
HullParticles[8] = { -100.000000, -460.000000, 100.000000 };
|
|
HullParticles[9] = { 100.000000, 390.000000, 100.000000 };
|
|
HullParticles[10] = { 100.000000, 390.000000, -100.000000 };
|
|
HullParticles[11] = { -100.000000, -460.000000, -100.000000 };
|
|
FConvex Convex(HullParticles, 0.0);
|
|
TImplicitObjectScaled<FConvex> ImplicitScaledConvex(&Convex, FVec3(2.00000000, 2.00000000, 2.00000000));
|
|
FCapsule Capsule(FVec3(0.00000000, 0.00000000, -55.0000000), FVec3(0.00000000, 0.00000000, 55.00000000), 35.000000);
|
|
|
|
FReal Thickness = 0;
|
|
bool bComputeMTD = true;
|
|
|
|
FVec3 LocalPosition, LocalNormal;
|
|
FReal OutTime = -1;
|
|
{
|
|
FRigidTransform3 BToAFullTM(FVec3(-23.634607186206267, 18.980813439935446, 2.2124981121014571),
|
|
FRotation3(FQuat(0.0000000000000000, 0.0000000000000000, -0.57785636374940896, 0.81613848265739242)));
|
|
FVec3 LocalDir(0.39419760716989327, -0.91902570906429382, 0.0000000000000000);
|
|
FVec3 Offset(14.467136106672115, 2.3920753243291983, -2.2124981121014571);
|
|
FReal Length = 6.9428286552429199;
|
|
bool bIsHitting = GJKRaycast2<FReal>(ImplicitScaledConvex, Capsule, BToAFullTM, LocalDir, Length, OutTime, LocalPosition, LocalNormal, Thickness, bComputeMTD, Offset, Thickness);
|
|
EXPECT_TRUE(bIsHitting);
|
|
EXPECT_LT(OutTime, 0.0);
|
|
}
|
|
{
|
|
FRigidTransform3 BToAFullTM(FVec3(27.711238420990412, -2.1483584435627563, 2.0126037597656250),
|
|
FRotation3(FQuat(0.0000000000000000, 0.0000000000000000, 0.12793015790484025, 0.99178317927783100)));
|
|
FVec3 LocalDir(0.96726777078778936, 0.25375796307645249, 0.0000000000000000);
|
|
FVec3 Offset(-27.711238420990412, 2.1483584435627563, -2.0126037597656250);
|
|
FReal Length = 15.497749328613281;
|
|
bool bIsHitting = GJKRaycast2<FReal>(ImplicitScaledConvex, Capsule, BToAFullTM, LocalDir, Length, OutTime, LocalPosition, LocalNormal, Thickness, bComputeMTD, Offset, Thickness);
|
|
EXPECT_TRUE(bIsHitting);
|
|
EXPECT_LT(OutTime, 0.0);
|
|
}
|
|
{
|
|
FRigidTransform3 BToAFullTM(FVec3(-22.297271080315113, 30.859426572671509, 2.2125010768795619),
|
|
FRotation3(FQuat(0.0000000000000000, 0.0000000000000000, 0.076283974503952301, 0.99708613230446663)));
|
|
FVec3 LocalDir(0.59685429204739793, 0.091864661873730533, 0.79707334589105316);
|
|
FReal Length = 38.664058685302734;
|
|
FVec3 Offset(22.297271080315113, -30.859426572671509, -2.2125010768795619);
|
|
bool bIsHitting = GJKRaycast2<FReal>(ImplicitScaledConvex, Capsule, BToAFullTM, LocalDir, Length, OutTime, LocalPosition, LocalNormal, Thickness, bComputeMTD, Offset, Thickness);
|
|
EXPECT_TRUE(bIsHitting);
|
|
EXPECT_LT(OutTime, 0.0);
|
|
}
|
|
}
|
|
|
|
|
|
void GJKSphereScaledSphereTest()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
FSpherePtr Sphere( new Chaos::FSphere(FVec3(4, 0, 0), 2));
|
|
TImplicitObjectScaled<Chaos::FSphere> Unscaled(Sphere, FVec3(1));
|
|
TImplicitObjectScaled<Chaos::FSphere> UniformScaled(Sphere, FVec3(2));
|
|
TImplicitObjectScaled<Chaos::FSphere> NonUniformScaled(Sphere, FVec3(2,1,1));
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, Unscaled, FRigidTransform3::Identity, 0, InitialDir));
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, UniformScaled, FRigidTransform3::Identity, 0, InitialDir));
|
|
//EXPECT_TRUE(GJKIntersection<FReal>(A, NonUniformScaled, FRigidTransform3::Identity, 0, InitialDir));
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, Unscaled, FRigidTransform3(FVec3(-1.1, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, UniformScaled, FRigidTransform3(FVec3(-7.1, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
//EXPECT_FALSE(GJKIntersection<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(-7.1, 0, 0), FRotation3::Identity), 0, InitialDir));
|
|
|
|
//hit from thickness
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, Unscaled, FRigidTransform3(FVec3(-1.1, 0, 0), FRotation3::Identity), 0.105, InitialDir));
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, UniformScaled, FRigidTransform3(FVec3(-7.1, 0, 0), FRotation3::Identity), 0.105, InitialDir));
|
|
//EXPECT_TRUE(GJKIntersection<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(-7.1, 0, 0), FRotation3::Identity), 0.105, InitialDir));
|
|
|
|
//miss with thickness
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, Unscaled, FRigidTransform3(FVec3(-1.1, 0, 0), FRotation3::Identity), 0.095, InitialDir));
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, UniformScaled, FRigidTransform3(FVec3(-7.1, 0, 0), FRotation3::Identity), 0.095, InitialDir));
|
|
//EXPECT_FALSE(GJKIntersection<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(-7.1, 0, 0), FRotation3::Identity), 0.095, InitialDir));
|
|
|
|
//hit with rotation
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, Unscaled, FRigidTransform3(FVec3(6.5, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 1, InitialDir));
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A, UniformScaled, FRigidTransform3(FVec3(8.1, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 1, InitialDir));
|
|
//EXPECT_TRUE(GJKIntersection<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(8.1, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 1, InitialDir));
|
|
|
|
//miss with rotation
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, Unscaled, FRigidTransform3(FVec3(6.5, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 0.01, InitialDir));
|
|
EXPECT_FALSE(GJKIntersection<FReal>(A, UniformScaled, FRigidTransform3(FVec3(8.1, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 0.01, InitialDir));
|
|
//EXPECT_FALSE(GJKIntersection<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(8.1, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))), 0.01, InitialDir));
|
|
}
|
|
}
|
|
|
|
//For each gjkraycast test we should test:
|
|
// - thickness
|
|
// - initial overlap
|
|
// - transformed geometry
|
|
// - rotated geometry
|
|
// - offset transform
|
|
// - degenerate cases
|
|
// - near miss, near hit
|
|
// - multiple initial dir
|
|
|
|
void GJKSphereSphereSweep()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
Chaos::FSphere B(FVec3(1, 0, 0), 2);
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
constexpr FReal Eps = 1e-1;
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
FReal Time;
|
|
FVec3 Position;
|
|
FVec3 Normal;
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//hit offset
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(1,0,0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 1, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//initial overlap
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(7, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, false, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
|
|
//MTD
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(7, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -5);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5,0,0), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
|
|
//EPA
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(9, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -7); //perfect overlap, will default to 0,0,1 normal
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(10,0,5), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(0, 0, 1), Eps);
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//hit with thickness
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
|
|
//hit rotated
|
|
const FRotation3 RotatedDown(FRotation3::FromVector(FVec3(0, PI * 0.5, 0)));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//miss rotated
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 8.1), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//hit rotated with inflation
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
|
|
//near hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7 - 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//degenerate
|
|
Chaos::FSphere Tiny(FVec3(1, 0, 0), 1e-8);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Tiny, FRigidTransform3::Identity, FVec3(1, 0, 0), 8, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 4, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//right at end
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 2, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
|
|
// not far enough
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 2 - 1e-2, Time, Position, Normal, 0, InitialDir));
|
|
}
|
|
}
|
|
|
|
void GJKSphereBoxSweep()
|
|
{
|
|
FAABB3 A(FVec3(3, -1, 0), FVec3(4, 1, 4));
|
|
Chaos::FSphere B(FVec3(0, 0, 0), 1);
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
constexpr FReal Eps = 1e-1;
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
FReal Time(0.0);
|
|
FVec3 Position(0.0);
|
|
FVec3 Normal(0.0);
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3, 0, 0), Eps);
|
|
|
|
//hit offset
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(1.5, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 0.5, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3, 0, 0), Eps);
|
|
|
|
//initial overlap
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(4, 0, 4), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, false, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
|
|
//MTD without EPA
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(4.25, 0, 2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -0.75);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(4, 0, 2), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(1, 0, 0), Eps);
|
|
|
|
//MTD with EPA
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(4, 0, 2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -1);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(4, 0, 2), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(1, 0, 0), Eps);
|
|
|
|
//MTD with EPA
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(3.25, 0, 2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -1.25);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3, 0, 2), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
|
|
//MTD with EPA
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(3.4, 0, 3.75), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -1.25);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3.4, 0, 4), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(0, 0, 1), Eps);
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(1, 0, 6), FRotation3::Identity), FVec3(1, 0, -1).GetUnsafeNormal(), 4, Time, Position, Normal, 0, InitialDir));
|
|
const FReal ExpectedTime = ((FVec3(3, 0, 4) - FVec3(1, 0, 6)).Size() - 1);
|
|
EXPECT_NEAR(Time, ExpectedTime, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-sqrt(2) / 2, 0, sqrt(2) / 2), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3, 0, 4), Eps);
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 5+1e-2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near hit with inflation
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 5 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 2e-2, InitialDir));
|
|
const FReal DistanceFromCorner = (Position - FVec3(3, 0, 4)).Size();
|
|
EXPECT_LT(DistanceFromCorner, 1e-1);
|
|
|
|
//rotated box
|
|
const FRotation3 Rotated(FRotation3::FromVector(FVec3(0, 0, PI * 0.5)));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(B, A, FRigidTransform3(FVec3(0), Rotated), FVec3(0, -1, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(0, 1, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(0, 1, 0), Eps);
|
|
|
|
//degenerate box
|
|
FAABB3 Needle(FVec3(3, 0, 0), FVec3(4, 0, 0));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(B, Needle, FRigidTransform3(FVec3(0), Rotated), FVec3(0, -1, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(0, 1, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(0, 1, 0), Eps);
|
|
}
|
|
}
|
|
|
|
|
|
void GJKSphereCapsuleSweep()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
FCapsule B(FVec3(1, 0, 0), FVec3(-3, 0, 0), 2);
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
constexpr FReal Eps = 1e-1;
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
FReal Time;
|
|
FVec3 Position;
|
|
FVec3 Normal;
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//hit offset
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 1, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//initial overlap
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(7, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, false, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
|
|
//MTD
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(7, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -5);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//hit with thickness
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
|
|
//hit rotated
|
|
const FRotation3 RotatedDown(FRotation3::FromVector(FVec3(0, PI * 0.5, 0)));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//miss rotated
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 8.1), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//hit rotated with inflation
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
|
|
//near hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7 - 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//degenerate
|
|
Chaos::FSphere Tiny(FVec3(1, 0, 0), 1e-8);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Tiny, FRigidTransform3::Identity, FVec3(1, 0, 0), 8, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 4, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//right at end
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 2, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
|
|
// not far enough
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 2 - 1e-2, Time, Position, Normal, 0, InitialDir));
|
|
}
|
|
}
|
|
|
|
|
|
void GJKSphereConvexSweep()
|
|
{
|
|
//Tetrahedron
|
|
TArray<FConvex::FVec3Type> HullParticles;
|
|
HullParticles.SetNum(4);
|
|
HullParticles[0] = { 3,0,4 };
|
|
HullParticles[1] = { 3,1,0 };
|
|
HullParticles[2] = { 3,-1,0 };
|
|
HullParticles[3] = { 4,0,2 };
|
|
FConvex A(HullParticles, 0.0f);
|
|
Chaos::FSphere B(FVec3(0, 0, 0), 1);
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
constexpr FReal Eps = 1e-1;
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
FReal Time;
|
|
FVec3 Position(0);
|
|
FVec3 Normal;
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3, 0, 0), Eps);
|
|
|
|
//hit offset
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(1.5, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 0.5, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3, 0, 0), Eps);
|
|
|
|
//initial overlap
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(4, 0, 4), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, false, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
|
|
//MTD
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(2.5, 0, 2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -0.5);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0).GetUnsafeNormal(), Eps);
|
|
|
|
//MTD
|
|
FReal Penetration;
|
|
FVec3 ClosestA, ClosestB;
|
|
int32 ClosestVertexIndexA, ClosestVertexIndexB;
|
|
EXPECT_TRUE((GJKPenetration<false, FReal>(A, B, FRigidTransform3(FVec3(2.5, 0, 2), FRotation3::Identity), Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0, 0, InitialDir)));
|
|
EXPECT_FLOAT_EQ(Penetration, 0.5);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0).GetUnsafeNormal(), Eps);
|
|
EXPECT_NEAR(ClosestA[0], 3, Eps); //could be any point on face, but should have x == 3
|
|
EXPECT_VECTOR_NEAR(ClosestB, FVec3(3.5, 0, 2), Eps);
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(1, 0, 6), FRotation3::Identity), FVec3(1, 0, -1).GetUnsafeNormal(), 4, Time, Position, Normal, 0, InitialDir));
|
|
const FReal ExpectedTime = ((FVec3(3, 0, 4) - FVec3(1, 0, 6)).Size() - 1);
|
|
EXPECT_NEAR(Time, ExpectedTime, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-sqrt(2) / 2, 0, sqrt(2) / 2), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3, 0, 4), Eps);
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 5 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near hit with inflation
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 5 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 2e-2, InitialDir));
|
|
const FReal DistanceFromCorner = (Position - FVec3(3, 0, 4)).Size();
|
|
EXPECT_LT(DistanceFromCorner, 1e-1);
|
|
|
|
//rotated box
|
|
const FRotation3 Rotated(FRotation3::FromVector(FVec3(0, 0, PI * 0.5)));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(B, A, FRigidTransform3(FVec3(0), Rotated), FVec3(0, -1, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_NEAR(Normal.X, 0, Eps);
|
|
EXPECT_NEAR(Normal.Y, 1, Eps);
|
|
//EXPECT_NEAR(Normal.Z, 0, Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(0, 1, 0), Eps);
|
|
|
|
//degenerate box
|
|
FAABB3 Needle(FVec3(3, 0, 0), FVec3(4, 0, 0));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(B, Needle, FRigidTransform3(FVec3(0), Rotated), FVec3(0, -1, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(0, 1, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(0, 1, 0), Eps);
|
|
}
|
|
}
|
|
|
|
void GJKSphereScaledSphereSweep()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
FSpherePtr Sphere( new Chaos::FSphere(FVec3(0, 0, 0), 2));
|
|
TImplicitObjectScaled<Chaos::FSphere> Unscaled(Sphere, FVec3(1));
|
|
TImplicitObjectScaled<Chaos::FSphere> UniformScaled(Sphere, FVec3(2));
|
|
TImplicitObjectScaled<Chaos::FSphere> NonUniformScaled(Sphere, FVec3(2, 1, 1));
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
constexpr FReal Eps = 1e-1;
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
FReal Time;
|
|
FVec3 Position;
|
|
FVec3 Normal;
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 3, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, UniformScaled, FRigidTransform3::Identity, FVec3(1, 0, 0), 6, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 1, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, NonUniformScaled, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 1, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//hit offset
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, UniformScaled, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 0, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 0, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//initial overlap
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(8, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, UniformScaled, FRigidTransform3(FVec3(6, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(6, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, UniformScaled, FRigidTransform3(FVec3(0, 0, 9.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//hit with thickness
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, UniformScaled, FRigidTransform3(FVec3(0, 0, 9.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
|
|
//hit rotated
|
|
const FRotation3 RotatedInPlace(FRotation3::FromVector(FVec3(0, PI * 0.5, 0)));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(0, 0, 0), RotatedInPlace), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, UniformScaled, FRigidTransform3(FVec3(0, 0, 0), RotatedInPlace), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(0, 0, 0), RotatedInPlace), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//miss rotated
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(0, 0, 7.1), RotatedInPlace), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, UniformScaled, FRigidTransform3(FVec3(0, 0, 9.1), RotatedInPlace), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, NonUniformScaled, FRigidTransform3(FVec3(0, 0, 9.1), RotatedInPlace), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(0, 0, 7 - 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3(FVec3(0, 0, 7 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//degenerate
|
|
Chaos::FSphere Tiny(FVec3(1, 0, 0), 1e-8);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Tiny, FRigidTransform3::Identity, FVec3(1, 0, 0), 8, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 4, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//right at end
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3::Identity, FVec3(1, 0, 0), 3, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 3, Eps);
|
|
|
|
// not far enough
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Unscaled, FRigidTransform3::Identity, FVec3(1, 0, 0), 3 - 1e-2, Time, Position, Normal, 0, InitialDir));
|
|
}
|
|
}
|
|
|
|
|
|
void GJKSphereTransformedSphereSweep()
|
|
{
|
|
Chaos::FSphere A(FVec3(10, 0, 0), 5);
|
|
|
|
Chaos::FSphere Sphere(FVec3(0), 2);
|
|
Chaos::FSphere Translated(Sphere.GetCenterf() + FVec3f(1, 0, 0), Sphere.GetRadiusf());
|
|
Chaos::FSphere Transformed(FRigidTransform3(FVec3(1, 0, 0), FRotation3::FromVector(FVec3(0, 0, PI))).TransformPosition(FVec3(Sphere.GetCenterf())), Sphere.GetRadiusf());
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
constexpr FReal Eps = 1e-1;
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
FReal Time;
|
|
FVec3 Position;
|
|
FVec3 Normal;
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3::Identity, FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//hit offset
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 1, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(1, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 1, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(5, 0, 0), Eps);
|
|
|
|
//initial overlap
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(7, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(7, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
|
|
//miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//hit with thickness
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(0, 0, 7.1), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
|
|
//hit rotated
|
|
const FRotation3 RotatedDown(FRotation3::FromVector(FVec3(0, PI * 0.5, 0)));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//miss rotated
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(0, 0, 8.1), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(0, 0, 8.1), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//hit rotated with inflation
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(0, 0, 7.9), RotatedDown), FVec3(1, 0, 0), 20, Time, Position, Normal, 0.2, InitialDir));
|
|
|
|
//near hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(0, 0, 7 - 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(0, 0, 7 - 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Translated, FRigidTransform3(FVec3(0, 0, 7 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3(FVec3(0, 0, 7 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 20, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//right at end
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Translated, FRigidTransform3::Identity, FVec3(1, 0, 0), 2, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3::Identity, FVec3(1, 0, 0), 2, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 2, Eps);
|
|
|
|
// not far enough
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Translated, FRigidTransform3::Identity, FVec3(1, 0, 0), 2 - 1e-2, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, Transformed, FRigidTransform3::Identity, FVec3(1, 0, 0), 2 - 1e-2, Time, Position, Normal, 0, InitialDir));
|
|
}
|
|
}
|
|
|
|
|
|
void GJKBoxCapsuleSweep()
|
|
{
|
|
FAABB3 A(FVec3(3, -1, 0), FVec3(4, 1, 4));
|
|
FCapsule B(FVec3(0, 0, -1), FVec3(0, 0, 1), 2);
|
|
|
|
FVec3 InitialDirs[] = { FVec3(1,0,0), FVec3(-1,0,0), FVec3(0,1,0), FVec3(0,-1,0), FVec3(0,0,1), FVec3(0,0,-1) };
|
|
|
|
constexpr FReal Eps = 1e-1;
|
|
|
|
for (const FVec3& InitialDir : InitialDirs)
|
|
{
|
|
FReal Time;
|
|
FVec3 Position;
|
|
FVec3 Normal;
|
|
|
|
//hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3::Identity, FVec3(1, 0, 0), 2, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 1, Eps);
|
|
EXPECT_NEAR(Normal.X, -1, Eps);
|
|
EXPECT_NEAR(Normal.Y, 0, Eps);
|
|
EXPECT_NEAR(Normal.Z, 0, Eps);
|
|
EXPECT_NEAR(Position.X, 3, Eps);
|
|
//EXPECT_NEAR(Position.Y, 0, Eps); //todo: look into inaccuracy here (0.015) instead of <1e-2
|
|
EXPECT_LE(Position.Z, 1 + Eps);
|
|
EXPECT_GE(Position.Z, -1 - Eps);
|
|
|
|
//hit offset
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0.5, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 0.5, Eps);
|
|
EXPECT_NEAR(Normal.X, -1, Eps);
|
|
EXPECT_NEAR(Normal.Y, 0, Eps);
|
|
EXPECT_NEAR(Normal.Z, 0, Eps);
|
|
EXPECT_NEAR(Position.X, 3, Eps);
|
|
//EXPECT_NEAR(Position.Y, 0, Eps); //todo: look into inaccuracy here (0.015) instead of <1e-2
|
|
EXPECT_LE(Position.Z, 1 + Eps);
|
|
EXPECT_GE(Position.Z, -1 - Eps);
|
|
|
|
//initial overlap
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(3, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 2, Time, Position, Normal, 0, false, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, 0);
|
|
|
|
//MTD
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(2.5, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 2, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -1.5);
|
|
EXPECT_NEAR(Position[0], 3, Eps); //many possible, but x must be on 3
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
|
|
//MTD
|
|
FReal Penetration;
|
|
FVec3 ClosestA, ClosestB;
|
|
int32 ClosestVertexIndexA, ClosestVertexIndexB;
|
|
EXPECT_TRUE((GJKPenetration<false, FReal>(A, B, FRigidTransform3(FVec3(2.5, 0, 0), FRotation3::Identity), Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0, 0, InitialDir)));
|
|
EXPECT_FLOAT_EQ(Penetration, 1.5);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_NEAR(ClosestA[0], 3, Eps); //could be any point on face, but should have x == 3
|
|
EXPECT_NEAR(ClosestB[0], 4.5, Eps);
|
|
EXPECT_NEAR(ClosestB[1], 0, Eps);
|
|
|
|
//EPA
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(3, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 2, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -2);
|
|
EXPECT_NEAR(Position[0], 3, Eps); //many possible, but x must be on 3
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
|
|
//EPA
|
|
EXPECT_TRUE((GJKPenetration<false, FReal>(A, B, FRigidTransform3(FVec3(3, 0, 0), FRotation3::Identity), Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0, 0, InitialDir)));
|
|
EXPECT_NEAR(Penetration, 2, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_NEAR(ClosestA[0], 3, Eps); //could be any point on face, but should have x == 3
|
|
EXPECT_NEAR(ClosestB[0], 5, Eps);
|
|
EXPECT_NEAR(ClosestB[1], 0, Eps);
|
|
|
|
//EPA
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(3.25, 0, 0), FRotation3::Identity), FVec3(1, 0, 0), 2, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -2.25);
|
|
EXPECT_NEAR(Position[0], 3, Eps); //many possible, but x must be on 3
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
|
|
//EPA
|
|
EXPECT_TRUE((GJKPenetration<false, FReal>(A, B, FRigidTransform3(FVec3(3.25, 0, 0), FRotation3::Identity), Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0, 0, InitialDir)));
|
|
EXPECT_NEAR(Penetration, 2.25, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(-1, 0, 0), Eps);
|
|
EXPECT_NEAR(ClosestA[0], 3, Eps); //could be any point on face, but should have x == 3
|
|
EXPECT_NEAR(ClosestB[0], 5.25, Eps);
|
|
EXPECT_NEAR(ClosestB[1], 0, Eps);
|
|
|
|
//MTD
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A, B, FRigidTransform3(FVec3(3.25, 0, -2.875), FRotation3::Identity), FVec3(1, 0, 0), 2, Time, Position, Normal, 0, true, InitialDir));
|
|
EXPECT_FLOAT_EQ(Time, -0.125);
|
|
EXPECT_VECTOR_NEAR(Position, FVec3(3.25, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(0, 0, -1), Eps);
|
|
|
|
//MTD
|
|
EXPECT_TRUE((GJKPenetration<false, FReal>(A, B, FRigidTransform3(FVec3(3.25, 0, -2.875), FRotation3::Identity), Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0, 0, InitialDir)));
|
|
EXPECT_NEAR(Penetration, 0.125, Eps);
|
|
EXPECT_VECTOR_NEAR(Normal, FVec3(0, 0, -1), Eps);
|
|
EXPECT_VECTOR_NEAR(ClosestA, FVec3(3.25, 0, 0), Eps);
|
|
EXPECT_VECTOR_NEAR(ClosestB, FVec3(3.25, 0, 0.125), Eps);
|
|
|
|
//near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7 + 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//near hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7 - 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Position.X, 3, Eps);
|
|
EXPECT_NEAR(Position.Z, 4, 10 * Eps);
|
|
|
|
//near hit inflation
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 7 - 1e-2), FRotation3::Identity), FVec3(1, 0, 0), 4, Time, Position, Normal, 2e-2, InitialDir));
|
|
EXPECT_NEAR(Position.X, 3, Eps);
|
|
EXPECT_NEAR(Position.Z, 4, 10 * Eps);
|
|
|
|
//rotation hit
|
|
FRotation3 Rotated(FRotation3::FromVector(FVec3(0, -PI * 0.5, 0)));
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(-0.5, 0, 0), Rotated), FVec3(1, 0, 0), 1, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 0.5, Eps);
|
|
EXPECT_NEAR(Position.X, 3, Eps);
|
|
EXPECT_NEAR(Normal.X, -1, Eps);
|
|
EXPECT_NEAR(Normal.Y, 0, Eps);
|
|
EXPECT_NEAR(Normal.Z, 0, Eps);
|
|
|
|
//rotation near hit
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 6 - 1e-2), Rotated), FVec3(1, 0, 0), 10, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//rotation near miss
|
|
EXPECT_FALSE(GJKRaycast<FReal>(A, B, FRigidTransform3(FVec3(0, 0, 6 + 1e-2), Rotated), FVec3(1, 0, 0), 10, Time, Position, Normal, 0, InitialDir));
|
|
|
|
//degenerate capsule
|
|
FCapsule Needle(FVec3(0, 0, -1), FVec3(0, 0, 1), 1e-8);
|
|
EXPECT_TRUE(GJKRaycast<FReal>(A, Needle, FRigidTransform3::Identity, FVec3(1, 0, 0), 6, Time, Position, Normal, 0, InitialDir));
|
|
EXPECT_NEAR(Time, 3, Eps);
|
|
EXPECT_NEAR(Normal.X, -1, Eps);
|
|
EXPECT_NEAR(Normal.Y, 0, Eps);
|
|
EXPECT_NEAR(Normal.Z, 0, Eps);
|
|
EXPECT_NEAR(Position.X, 3, Eps);
|
|
//EXPECT_NEAR(Position.Y, 0, Eps); //todo: look into inaccuracy here (0.015) instead of <1e-2
|
|
EXPECT_LE(Position.Z, 1 + Eps);
|
|
EXPECT_GE(Position.Z, -1 - Eps);
|
|
}
|
|
}
|
|
|
|
void GJKBoxBoxSweep()
|
|
{
|
|
{
|
|
//based on real sweep from game
|
|
const FAABB3 A(FVec3(-2560.00000, -268.000031, -768.000122), FVec3(0.000000000, 3.99996948, 0.000000000));
|
|
const FAABB3 B(FVec3(-248.000000, -248.000000, -9.99999975e-05), FVec3(248.000000, 248.000000, 9.99999975e-05));
|
|
const FRigidTransform3 BToATM(FVec3(-2559.99780, -511.729492, -8.98901367), FRotation3::FromElements(1.51728955e-06, 1.51728318e-06, 0.707108259, 0.707105279));
|
|
const FVec3 LocalDir(-4.29153351e-06, 0.000000000, -1.00000000);
|
|
const FReal Length = 393.000000;
|
|
const FVec3 SearchDir(511.718750, -2560.00000, 9.00000000);
|
|
|
|
FReal Time;
|
|
FVec3 Pos, Normal;
|
|
GJKRaycast2<FReal>(A, B, BToATM, LocalDir, Length, Time, Pos, Normal, 0, true, SearchDir, 0);
|
|
}
|
|
|
|
{
|
|
//based on real sweep from game
|
|
TArray<FConvex::FVec3Type> ConvexParticles;
|
|
ConvexParticles.SetNum(10);
|
|
|
|
ConvexParticles[0] = { 51870.2305, 54369.6719, 19200.0000 };
|
|
ConvexParticles[1] = { -91008.5625, -59964.0000, -19199.9629 };
|
|
ConvexParticles[2] = { 51870.2305, 54369.6758, -19199.9668 };
|
|
ConvexParticles[3] = { 22164.4883, 124647.500, -19199.9961 };
|
|
ConvexParticles[4] = { 34478.5000, 123975.492, -19199.9961 };
|
|
ConvexParticles[5] = { -91008.5000, -59963.9375, 19200.0000 };
|
|
ConvexParticles[6] = { -91008.5000, 33715.5625, 19200.0000 };
|
|
ConvexParticles[7] = { 34478.4961, 123975.500, 19200.0000 };
|
|
ConvexParticles[8] = { 22164.4922, 124647.500, 19200.0000 };
|
|
ConvexParticles[9] = { -91008.5000, 33715.5625, -19199.9961 };
|
|
|
|
|
|
const FConvex A(ConvexParticles, 0.0f);
|
|
const FAABB3 B(FVec3{ -6.00000000, -248.000000, -9.99999975e-05 }, FVec3{ 6.00000000, 248.000000, 9.99999975e-05 });
|
|
const FRigidTransform3 BToATM(FVec3{33470.5000, 41570.5000, -1161.00000}, FRotation3::FromIdentity());
|
|
const FVec3 LocalDir(0, 0, -1);
|
|
const FReal Length = 393.000000;
|
|
const FVec3 SearchDir{ -33470.5000, -41570.5000, 1161.00000 };
|
|
|
|
FReal Time;
|
|
FVec3 Pos, Normal;
|
|
GJKRaycast2<FReal>(A, B, BToATM, LocalDir, Length, Time, Pos, Normal, 0, true, SearchDir, 0);
|
|
}
|
|
}
|
|
|
|
// When we have a capsule and box that are reported as initially-overlapping because they are within
|
|
// the GJK epsilon of each other (but actually positively separated), verify that we get a zero time of impact.
|
|
// Previously the slightly-positive separation would result in a negative penetration and a positive TOI.
|
|
// Bug fix: CL 10942094.
|
|
// NOTE: this issue no longer manifests with this example because GJK no longer reports this case as
|
|
// overlapping> The GJK epsilon no longer takes part in the distance calculation when the near point
|
|
// is on the face of the convex.
|
|
GTEST_TEST(GJKTests, DISABLED_TestGJKCapsuleConvexInitialOverlapSweep_Fixed)
|
|
{
|
|
{
|
|
TArray<FConvex::FVec3Type> ConvexParticles;
|
|
ConvexParticles.SetNum(8);
|
|
|
|
ConvexParticles[0] ={-256.000031,12.0000601,384.000061};
|
|
ConvexParticles[1] ={256.000031,12.0000601,384.000061};
|
|
ConvexParticles[2] ={256.000031,12.0000601,6.10351563e-05};
|
|
ConvexParticles[3] ={-256.000031,-11.9999399,6.10351563e-05};
|
|
ConvexParticles[4] ={-256.000031,12.0000601,6.10351563e-05};
|
|
ConvexParticles[5] ={-256.000031,-11.9999399,384.000061};
|
|
ConvexParticles[6] ={256.000031,-11.9999399,6.10351563e-05};
|
|
ConvexParticles[7] ={256.000031,-11.9999399,384.000061};
|
|
|
|
FConvexPtr UniqueConvex( new FConvex(ConvexParticles, 0.0f));
|
|
const TImplicitObjectScaled<FConvex> A(UniqueConvex, FVec3(1.0,1.0,1.0));
|
|
|
|
const FVec3 Pt0(0.0,0.0,-33.0);
|
|
FVec3 Pt1 = Pt0;
|
|
Pt1 += (FVec3(0.0,0.0,1.0) * 66.0);
|
|
|
|
const FCapsule B(Pt0,Pt1,42.0);
|
|
|
|
const FRigidTransform3 BToATM(FVec3(157.314758,-54.0000839,76.1436157), FRotation3::FromElements(0.0,0.0,0.704960823,0.709246278));
|
|
const FVec3 LocalDir(-0.00641351938,-0.999979556,0.0);
|
|
const FReal Length = 0.0886496082;
|
|
const FVec3 SearchDir(-3.06152344,166.296631,-76.1436157);
|
|
|
|
FReal Time;
|
|
FVec3 Position,Normal;
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(A,B,BToATM,LocalDir,Length,Time,Position,Normal,0,true,SearchDir,0));
|
|
EXPECT_FLOAT_EQ(Time,0.0);
|
|
}
|
|
}
|
|
|
|
void GJKCapsuleConvexInitialOverlapSweep()
|
|
{
|
|
{
|
|
TArray<FConvex::FVec3Type> ConvexParticles;
|
|
ConvexParticles.SetNum(16);
|
|
|
|
ConvexParticles[0] ={-127.216454,203.240234,124.726524};
|
|
ConvexParticles[1] ={125.708847,203.240295,124.726524};
|
|
ConvexParticles[2] ={-120.419685,207.124924,-0.386817127};
|
|
ConvexParticles[3] ={-32.9052734,91.5147095,199.922119};
|
|
ConvexParticles[4] ={118.912071,91.3693237,155.363205};
|
|
ConvexParticles[5] ={31.3977623,91.5147705,199.922150};
|
|
ConvexParticles[6] ={115.392204,91.6678925,162.647476};
|
|
ConvexParticles[7] ={-120.419701,91.1026840,-0.386809498};
|
|
ConvexParticles[8] ={118.912086,207.124985,-0.386806667};
|
|
ConvexParticles[9] ={118.912086,91.1027603,-0.386806667};
|
|
ConvexParticles[10] ={-120.419685,91.3692703,155.363174};
|
|
ConvexParticles[11] ={-110.103012,199.020554,160.910324};
|
|
ConvexParticles[12] ={-116.899742,91.6678467,162.647491};
|
|
ConvexParticles[13] ={31.3977337,194.240265,194.534988};
|
|
ConvexParticles[14] ={-32.9052925,194.240204,194.534958};
|
|
ConvexParticles[15] ={108.595482,199.020599,160.910309};
|
|
|
|
auto Convex = MakeShared<FConvex, ESPMode::ThreadSafe>(ConvexParticles, 0.0f);
|
|
const auto& A = *Convex;
|
|
//const TImplicitObjectInstanced<FConvex> A(Convex);
|
|
|
|
const FVec3 Pt0(0.0,0.0,-45);
|
|
FVec3 Pt1 = Pt0;
|
|
Pt1 += (FVec3(0.0,0.0,1.0) * 90);
|
|
|
|
const FCapsule B(Pt0,Pt1,33.8499985);
|
|
|
|
const FRigidTransform3 ATM(FVec3(2624.00024, -383.998962, 4.00000000), FRotation3::FromElements(-5.07916162e-08, -3.39378659e-08, 0.555569768, 0.831469893));
|
|
const FRigidTransform3 BTM(FVec3(2461.92749, -205.484283, 106.071632), FRotation3::FromElements(0,0,0,1));
|
|
const FRigidTransform3 BToATM(FVec3(102.903252, 218.050415, 102.071655), FRotation3::FromElements(5.07916162e-08, 3.39378659e-08, -0.555569768, 0.831469893));
|
|
|
|
FReal Penetration = 0;
|
|
FVec3 ClosestA = FVec3(0);
|
|
FVec3 ClosestB = FVec3(0);
|
|
FVec3 Normal = FVec3(0);
|
|
int32 ClosestVertexIndexA = INDEX_NONE;
|
|
int32 ClosestVertexIndexB = INDEX_NONE;
|
|
const FVec3 Offset ={162.072754,-178.514679,-102.071632};
|
|
EXPECT_TRUE((GJKPenetration<false, FReal>(A,B,BToATM,Penetration,ClosestA,ClosestB,Normal,ClosestVertexIndexA,ClosestVertexIndexB,0,0,Offset)));
|
|
|
|
const FRigidTransform3 NewAToBTM (BToATM.GetTranslation() + (0.01 + Penetration) * Normal,BToATM.GetRotation());;
|
|
|
|
EXPECT_FALSE((GJKPenetration<false, FReal>(A,B,NewAToBTM,Penetration,ClosestA,ClosestB,Normal,ClosestVertexIndexA,ClosestVertexIndexB,0,0,Offset)));
|
|
|
|
}
|
|
|
|
{
|
|
//capsule perfectly aligned with another capsule but a bit off on the z
|
|
const FVec3 Pt0(0.0,0.0,-45.0);
|
|
FVec3 Pt1 = Pt0;
|
|
Pt1 += (FVec3(0.0,0.0,1.0) * 90.0);
|
|
|
|
const FCapsule A(Pt0,Pt1,34.f);
|
|
const FCapsule B(Pt0,Pt1,33.8499985f);
|
|
|
|
const FRigidTransform3 BToATM(FVec3(0.0f,0.0f,-23.4092140f), FRotation3::FromElements(0.0,0.0,0.0,1.0));
|
|
|
|
EXPECT_TRUE(GJKIntersection<FReal>(A,B,BToATM,0.0,FVec3(0,0,23.4092140)));
|
|
|
|
FReal Penetration;
|
|
FVec3 ClosestA,ClosestB,Normal;
|
|
int32 ClosestVertexIndexA, ClosestVertexIndexB;
|
|
EXPECT_TRUE((GJKPenetration<false, FReal>(A,B,BToATM, Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0.0, 0.0, FVec3(0,0,23.4092140))));
|
|
EXPECT_FLOAT_EQ(Normal.Z,0);
|
|
EXPECT_FLOAT_EQ(Penetration,A.GetRadiusf() + B.GetRadiusf());
|
|
}
|
|
|
|
{
|
|
//capsule vs triangle as we make the sweep longer the world space point of impact should stay the same
|
|
TArray<FConvex::FVec3Type> ConvexParticles;
|
|
ConvexParticles.SetNum(3);
|
|
|
|
ConvexParticles[0] ={7400.00000, 12600.0000, 206.248123};
|
|
ConvexParticles[1] ={7500.00000, 12600.0000, 199.994904};
|
|
ConvexParticles[2] ={7500.00000, 12700.0000, 189.837433};
|
|
|
|
FConvexPtr UniqueConvex( new FConvex(ConvexParticles, 0.0f));
|
|
const TImplicitObjectScaled<FConvex> AConvScaled(UniqueConvex, FVec3(1.0,1.0,1.0));
|
|
|
|
FTriangle A(ConvexParticles[0],ConvexParticles[1],ConvexParticles[2]);
|
|
FTriangleRegister AReg(
|
|
MakeVectorRegisterFloat(ConvexParticles[0].X, ConvexParticles[0].Y, ConvexParticles[0].Z, 0.0f),
|
|
MakeVectorRegisterFloat(ConvexParticles[1].X, ConvexParticles[1].Y, ConvexParticles[1].Z, 0.0f),
|
|
MakeVectorRegisterFloat(ConvexParticles[2].X, ConvexParticles[2].Y, ConvexParticles[2].Z, 0.0f));
|
|
|
|
const FVec3 Pt0(0.0,0.0,-29.6999969);
|
|
FVec3 Pt1 = Pt0;
|
|
Pt1 += (FVec3(0.0,0.0,1.0) * 59.3999939);
|
|
|
|
const FCapsule B(Pt0,Pt1,42.0);
|
|
|
|
const FRigidTransform3 BToATM(FVec3(7475.74512, 12603.9082, 277.767120), FRotation3::FromElements(0,0,0,1));
|
|
const FVec3 LocalDir(0,0,-0.999999940);
|
|
const FReal Length = 49.9061584;
|
|
const FVec3 SearchDir(1,0,0);
|
|
|
|
FReal Time;
|
|
FVec3 Position,Normal;
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(AConvScaled,B,BToATM,LocalDir,Length,Time,Position,Normal,0,true,SearchDir,0));
|
|
|
|
const FRigidTransform3 BToATM2(FVec3(7475.74512, 12603.9082, 277.767120+100), FRotation3::FromElements(0,0,0,1));
|
|
|
|
FReal Time2;
|
|
FVec3 Position2,Normal2;
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(AConvScaled,B,BToATM2,LocalDir,Length+100,Time2,Position2,Normal2,0,true,SearchDir,0));
|
|
EXPECT_TRUE(GJKRaycast2<FReal>(AReg,B,BToATM2,LocalDir,Length+100,Time2,Position2,Normal2,0,true,SearchDir,0));
|
|
|
|
EXPECT_NEAR(Time+100,Time2, 1.0f); // TODO: Investigate: This used to be 0
|
|
EXPECT_VECTOR_NEAR(Normal,Normal2,1e-3); // TODO: Investigate: This used to be 1e-4
|
|
EXPECT_VECTOR_NEAR(Position,Position2,1e-1); // TODO: Investigate: This used to be 1e-3
|
|
}
|
|
|
|
|
|
{
|
|
// For this test we are clearly not penetrating
|
|
// but we had an actual bug (edge condition) that showed we are
|
|
|
|
const FVec3 Pt0(0.0, 0.0, 0.0);
|
|
FVec3 Pt1(100.0,0,0);
|
|
FVec3 Pt2(0, 1000000.0, 0);
|
|
|
|
|
|
const FCapsule A(Pt1, Pt2, 1.0);
|
|
const Chaos::FSphere B(Pt0, 1.0);
|
|
|
|
const FRigidTransform3 BToATM(FVec3(0, 0, 0), FRotation3::FromElements(0.0, 0.0, 0, 1)); // Unit transform
|
|
const FVec3 InitDir(0.1, 0.0, 0.0);
|
|
|
|
FReal Penetration;
|
|
FVec3 ClosestA, ClosestB, Normal;
|
|
int32 ClosestVertexIndexA, ClosestVertexIndexB;
|
|
|
|
// First demonstrate the distance between the shapes are more than 90cm.
|
|
bool IsValid = GJKPenetration<true, FReal>(A, B, BToATM, Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0, 0, InitDir);
|
|
EXPECT_TRUE(IsValid);
|
|
EXPECT_TRUE(Penetration < -90.0f);
|
|
|
|
// Since there is no penetration (by more than 90cm) this function should return false when negative penetration is not supported
|
|
bool IsPenetrating = GJKPenetration<false, FReal>(A, B, BToATM, Penetration, ClosestA, ClosestB, Normal, ClosestVertexIndexA, ClosestVertexIndexB, 0, 0, InitDir);
|
|
EXPECT_FALSE(IsPenetrating);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Tests a case where we have a reasonable query but the target shape is a very large distance away.
|
|
// This should result in a miss but currently doesn't - and gives an OutTime that is infinite.
|
|
// Detected when querying global payload object in the SQ system where we test each object without
|
|
// considering its bounds.
|
|
void GJKLargeDistanceCapsuleSweep()
|
|
{
|
|
// Data from repro case
|
|
Chaos::TBox<Chaos::FReal, 3> A({ -24.011219501495361, -7.4698066711425781, -0.83472084999084473 }, { 32.555774211883545, 10.860815048217773, 14.719563245773315 }, 0);
|
|
Chaos::FCapsule B({0.0000000000000000, 0.0000000000000000, -67.499992370605469}, {0.0000000000000000, 0.0000000000000000, -67.499992370605469 + 134.99998474121094 }, 67.274772644042969);
|
|
Chaos::TRigidTransform<Chaos::FReal, 3> BToA;
|
|
BToA.SetRotation({0.0000000000000000, 0.0000000000000000, -0.70710678118654757, 0.70710678118654746});
|
|
BToA.SetTranslation({0.0000000000000000, -3.3237259359872290e+32, 7460.1000976562500});
|
|
const Chaos::FVec3 LocalDir{0.93683970992769239, 0.040186153777059030, 0.34744278175123056};
|
|
const Chaos::FVec3 InitialDir{-3.3237259359872290e+32, 27121.400390625000, -7460.1000976562500};
|
|
const FReal Length = 13.27157020568847;
|
|
const FReal Thickness = 0;
|
|
const bool bComputeMtd = true;
|
|
|
|
FReal OutTime;
|
|
FVec3 OutLoc;
|
|
FVec3 OutNorm;
|
|
|
|
// Should fail and give a valid time
|
|
bool bHit = GJKRaycast2(A, B, BToA, LocalDir, Length, OutTime, OutLoc, OutNorm, Thickness, bComputeMtd, InitialDir, Thickness);
|
|
|
|
EXPECT_FALSE(bHit);
|
|
|
|
// Expect to receive a valid time.
|
|
EXPECT_TRUE(FMath::IsFinite(OutTime));
|
|
}
|
|
|
|
// Check that GJKPenetrationCore returns the correct result when two objects are within various distances
|
|
// of each other. When distance is less that GJKEpsilon, GJK will abort and call into EPA.
|
|
void GJKBoxBoxZeroMarginSeparationTest(FReal GJKEpsilon, FReal SeparationSize, int32 SeparationAxis)
|
|
{
|
|
FVec3 SeparationDir = FVec3(0);
|
|
SeparationDir[SeparationAxis] = 1.0f;
|
|
FVec3 Separation = SeparationSize * SeparationDir;
|
|
|
|
// Extents covering both boxes - we will split this in the middle using the separation axis
|
|
FVec3 MinExtent = FVec3(-100, -100, -100);
|
|
FVec3 MaxExtent = FVec3(100, 100, 100);
|
|
|
|
// A is most positive along separation axis and shifted by SeperationSize (e.g., the top is axis is Z)
|
|
FVec3 MinA = MinExtent;
|
|
FVec3 MaxA = MaxExtent;
|
|
MinA[SeparationAxis] = SeparationSize;
|
|
MaxA[SeparationAxis] = 100.0f + SeparationSize;
|
|
|
|
// B is most negative along separation axis and shifted by SeperationSize (e.g., the bottom if axis is Z)
|
|
FVec3 MinB = MinExtent;
|
|
FVec3 MaxB = MaxExtent;
|
|
MaxB[SeparationAxis] = 0.0f;
|
|
|
|
// Create the shapes
|
|
float MarginA = 0.0f;
|
|
float MarginB = 0.0f;
|
|
FImplicitBox3 ShapeA(MinA, MaxA, MarginA);
|
|
FImplicitBox3 ShapeB(MinB, MaxB, MarginB);
|
|
const FRigidTransform3 TransformA = FRigidTransform3::Identity;
|
|
const FRigidTransform3 TransformB = FRigidTransform3::Identity;
|
|
const FRigidTransform3 TransformBtoA = FRigidTransform3::Identity;
|
|
const FReal ThicknessA = 0.0f;
|
|
const FReal ThicknessB = 0.0f;
|
|
|
|
// Run GJK/EPA
|
|
FReal Penetration;
|
|
FVec3 ClosestA, ClosestBInA, Normal;
|
|
int32 ClosestVertexIndexA, ClosestVertexIndexB;
|
|
bool bSuccess = GJKPenetration<true>(ShapeA, ShapeB, TransformBtoA, Penetration, ClosestA, ClosestBInA, Normal, ClosestVertexIndexA, ClosestVertexIndexB, ThicknessA, ThicknessB, FVec3(1, 0, 0), GJKEpsilon);
|
|
EXPECT_TRUE(bSuccess);
|
|
|
|
if (bSuccess)
|
|
{
|
|
// Convert the contact data to world-space (not really necessary here)
|
|
const FVec3 ResultLocation = TransformA.TransformPosition(ClosestA + ThicknessA * Normal);
|
|
const FVec3 ResultNormal = -TransformA.TransformVectorNoScale(Normal);
|
|
const FReal ResultPhi = -Penetration;
|
|
|
|
const FReal ExpectedLocationI = SeparationSize;
|
|
const FReal ExpectedNormalI = 1.0f;
|
|
const FReal ExpectedPhi = SeparationSize;
|
|
|
|
EXPECT_NEAR(ResultLocation[SeparationAxis], ExpectedLocationI, 1.e-3f) << "Separation " << SeparationSize << " Axis " << SeparationAxis;
|
|
EXPECT_NEAR(ResultNormal[SeparationAxis], ExpectedNormalI, 1.e-4f) << "Separation " << SeparationSize << " Axis " << SeparationAxis;
|
|
EXPECT_NEAR(ResultPhi, ExpectedPhi, 1.e-3f) << "Separation " << SeparationSize << " Axis " << SeparationAxis;
|
|
}
|
|
}
|
|
|
|
const FReal BoxBoxGJKDistances[] =
|
|
{
|
|
1.0f,
|
|
1.0f / 2.0f,
|
|
1.0f / 4.0f,
|
|
1.0f / 8.0f,
|
|
1.0f / 16.0f,
|
|
1.0f / 32.0f,
|
|
1.0f / 64.0f,
|
|
1.0f / 128.0f,
|
|
1.0f / 256.0f,
|
|
1.0f / 512.0f,
|
|
1.0f / 1024.0f,
|
|
1.0f / 2048.0f,
|
|
1.0f / 4096.0f,
|
|
1.0f / 8192.0f,
|
|
1.0f / 16384.0f,
|
|
1.0f / 32768.0f,
|
|
1.e-4f,
|
|
1.e-5f,
|
|
1.e-6f,
|
|
1.e-7f,
|
|
1.e-8f,
|
|
0.0f,
|
|
};
|
|
const int32 NumBoxBoxGJKDistances = UE_ARRAY_COUNT(BoxBoxGJKDistances);
|
|
|
|
// These tests fails in EPA - we need to cover these cases with SAT
|
|
GTEST_TEST(GJKTests, TestGJKBoxBoxTestFails)
|
|
{
|
|
const FReal Epsilon = 1.e-3f;
|
|
|
|
// These are the cases that case EPA to fail out with a degenerate simplex
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, -0.125f, 0);
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, -0.03125, 0);
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, -0.015625, 0);
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, -0.0078125, 0);
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, -0.00390625, 0);
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, -0.001953125, 0);
|
|
}
|
|
|
|
// Disabled until we have SAT fallback (see DISABLED_TestGJKBoxBoxTestFails)
|
|
GTEST_TEST(GJKTests, TestGJKBoxBoxNegativeSeparation)
|
|
{
|
|
const FReal Epsilon = 1.e-3f;
|
|
|
|
for (int32 DistanceIndex = 0; DistanceIndex < NumBoxBoxGJKDistances; ++DistanceIndex)
|
|
{
|
|
for (int32 AxisIndex = 0; AxisIndex < 3; ++AxisIndex)
|
|
{
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, -BoxBoxGJKDistances[DistanceIndex], AxisIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, TestGJKBoxBoxPositiveSeparation)
|
|
{
|
|
const FReal Epsilon = 1.e-3f;
|
|
|
|
for (int32 DistanceIndex = 0; DistanceIndex < NumBoxBoxGJKDistances; ++DistanceIndex)
|
|
{
|
|
for (int32 AxisIndex = 0; AxisIndex < 3; ++AxisIndex)
|
|
{
|
|
GJKBoxBoxZeroMarginSeparationTest(Epsilon, BoxBoxGJKDistances[DistanceIndex], AxisIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a know regression test
|
|
// It is two boxes deeply overlapping in a T-shape
|
|
GTEST_TEST(GJKTests, TestGJKBoxBoxOverlapRegression1)
|
|
{
|
|
FVec3 MinBox = FVec3(-15.839999675750732, -31.840000152587891, -3.8146972691777137e-07);
|
|
FVec3 MaxBox = FVec3(15.840000629425049, 31.840000152587891, 19.200000381469728);
|
|
FVec3 Translation = FVec3(15.999999999999993, 1.9594348786357647e-15, 0.0000000000000000);
|
|
FRotation3 Rotation(UE::Math::TQuat<FReal>(0.0000000000000000, 0.0000000000000000, -0.70710678118654757, -0.70710678118654746));
|
|
|
|
FImplicitBox3 ShapeA(MinBox, MaxBox, 0);
|
|
FImplicitBox3 ShapeB(MinBox, MaxBox, 0);
|
|
|
|
const FRigidTransform3 TransformBtoA = FRigidTransform3(Translation , Rotation);
|
|
const FReal ThicknessA = 0.0f;
|
|
const FReal ThicknessB = 0.0f;
|
|
|
|
// Run GJK/EPA
|
|
FReal Penetration;
|
|
FVec3 ClosestA, ClosestBInA, Normal;
|
|
int32 ClosestVertexIndexA, ClosestVertexIndexB;
|
|
bool bSuccess = GJKPenetration<false>(ShapeA, ShapeB, TransformBtoA, Penetration, ClosestA, ClosestBInA, Normal, ClosestVertexIndexA, ClosestVertexIndexB, ThicknessA, ThicknessB, FVec3(1, 0, 0));
|
|
EXPECT_TRUE(bSuccess);
|
|
EXPECT_TRUE(Penetration > 19.0f); // Penetration is the Height of the box
|
|
}
|
|
|
|
// This is a know regression test
|
|
// Two boxes clearly overlapping
|
|
// --gtest_filter=*TestGJKBoxBoxOverlapRegression2*
|
|
GTEST_TEST(GJKTests, TestGJKBoxBoxOverlapRegression2)
|
|
{
|
|
|
|
FVec3 MinBox1 = FVec3(- 112.00000000000000, -256.00000000000000, -8.0000000000000000);
|
|
FVec3 MaxBox1 = FVec3(112.00000000000000, 256.00000000000000, 8.0000000000000000);
|
|
|
|
FVec3 MinBox2 = FVec3(-64.000000000000000, -64.000000000000000, -64.000000000000000);
|
|
FVec3 MaxBox2 = FVec3(64.000000000000000, 64.000000000000000, 64.000000000000000);
|
|
|
|
FVec3 Translation = FVec3(0.0044999999990977813, -255.99550000000090, -18.000000000000000);
|
|
FRotation3 Rotation(UE::Math::TQuat<FReal>(0.0000000000000000, 0.0000000000000000, 0.0000000000000000,1.0f));
|
|
|
|
FImplicitBox3 ShapeA(MinBox1, MaxBox1, 0);
|
|
FImplicitBox3 ShapeB(MinBox2, MaxBox2, 0);
|
|
|
|
const FRigidTransform3 TransformBtoA = FRigidTransform3(Translation, Rotation);
|
|
|
|
// Run GJK/EPA
|
|
bool bOverlap = GJKIntersection<FReal>(ShapeA, ShapeB, TransformBtoA, 0.00f, FVec3{ -0.0044999999990977813, 255.99550000000090, 18.000000000000000 });
|
|
EXPECT_TRUE(bOverlap);
|
|
}
|
|
|
|
// Two convex shapes, Shape A on top of Shape B and almost touching. ShapeA is rotated 90 degrees about Z.
|
|
// Check that the contact point lies between Shape A and Shape B with a near zero Phi.
|
|
// This reproduces a bug where GJKPenetrationCore returns points on top of A and at the bottom
|
|
// of B, with a Phi equal to the separation of those points. Resolving this contact
|
|
// would result in Shape B popping to the top of Shape A.
|
|
//
|
|
// The problem was in EPA where the possible set of simplex faces are added to the queue. Here it checks
|
|
// to see if the origin projects to within the face, since if it does not, it cannot the face that is nearest
|
|
// to the origin. However, without a tolerance, this could reject valid faces.
|
|
void GJKConvexConvexEPABoundaryCondition()
|
|
{
|
|
// These verts are those from a rectangular box with bevelled edges
|
|
TArray<FConvex::FVec3Type> CoreShapeVerts =
|
|
{
|
|
{3.54999995f, -1.04999995f, 0.750000000f},
|
|
{3.75000000f, 1.04999995f, 0.549999952f},
|
|
{3.54999995f, 1.04999995f, 0.750000000f},
|
|
{-3.54999995f, 1.04999995f, 0.750000000f},
|
|
{-3.54999995f, 1.25000000f, 0.549999952f},
|
|
{-3.54999995f, 1.25000000f, -0.550000012f},
|
|
{-3.75000000f, 1.04999995f, 0.549999952f},
|
|
{3.54999995f, 1.25000000f, 0.549999952f},
|
|
{3.54999995f, 1.04999995f, -0.750000000f},
|
|
{3.54999995f, 1.25000000f, -0.550000012f},
|
|
{-3.54999995f, 1.04999995f, -0.750000000f},
|
|
{-3.54999995f, -1.04999995f, -0.750000000f},
|
|
{-3.75000000f, 1.04999995f, -0.550000012f},
|
|
{3.54999995f, -1.25000000f, -0.550000012f},
|
|
{3.54999995f, -1.04999995f, -0.750000000f},
|
|
{-3.54999995f, -1.25000000f, 0.549999952f},
|
|
{-3.54999995f, -1.25000000f, -0.550000012f},
|
|
{-3.75000000f, -1.04999995f, -0.550000012f},
|
|
{3.54999995f, -1.25000000f, 0.549999952f},
|
|
{-3.54999995f, -1.04999995f, 0.750000000f},
|
|
{-3.75000000f, -1.04999995f, 0.549999952f},
|
|
{3.75000000f, -1.04999995f, 0.549999952f},
|
|
{3.75000000f, -1.04999995f, -0.550000012f},
|
|
{3.75000000f, 1.04999995f, -0.550000012f},
|
|
};
|
|
const FVec3 Scale = FVec3(50.0f);
|
|
const FReal Margin = 0.75f;
|
|
|
|
|
|
FConvexPtr CoreConvexShapePtr( new FImplicitConvex3(CoreShapeVerts, 0.0f, FConvexBuilder::EBuildMethod::Original));
|
|
const TImplicitObjectScaled<FImplicitConvex3> ShapeA(CoreConvexShapePtr, Scale, Margin);
|
|
const TImplicitObjectScaled<FImplicitConvex3> ShapeB(CoreConvexShapePtr, Scale, Margin);
|
|
const FRigidTransform3 TransformA(FVec3(0.000000000f, 0.000000000f, 182.378937f), FRotation3::FromElements(0.000000000f, 0.000000000f, 0.707106650f, 0.707106888f)); // Top
|
|
const FRigidTransform3 TransformB(FVec3(0.000000000f, 0.000000000f, 107.378944f), FRotation3::FromElements(0.000000000f, 0.000000000f, 0.000000000f, 1.00000000f)); // Bottom
|
|
|
|
// Shape Z extents = [50x-0.75, 50x0.75] = [-37.5, 37.5]
|
|
// Shape Z separation = 182.378937 - 107.378944 = 74.999993
|
|
// i.e., the shapes are touching to near float accuracy
|
|
// The top shape is rotated by 90degrees
|
|
|
|
const FRigidTransform3 TransformBtoA = TransformB.GetRelativeTransform(TransformA);
|
|
|
|
FReal Penetration;
|
|
FVec3 ClosestA, ClosestBInA, Normal;
|
|
int32 ClosestVertexIndexA, ClosestVertexIndexB;
|
|
const FReal Epsilon = 3.e-3f;
|
|
|
|
const FReal ThicknessA = 0.0f;
|
|
const FReal ThicknessB = 0.0f;
|
|
|
|
const bool bSuccess = GJKPenetration<true>(ShapeA, ShapeB, TransformBtoA, Penetration, ClosestA, ClosestBInA, Normal, ClosestVertexIndexA, ClosestVertexIndexB, ThicknessA, ThicknessB, FVec3(1,0,0), Epsilon);
|
|
EXPECT_TRUE(bSuccess);
|
|
|
|
if (bSuccess)
|
|
{
|
|
const FVec3 ContactLocation = TransformA.TransformPosition(ClosestA + ThicknessA * Normal);
|
|
const FVec3 ContactNormal = -TransformA.TransformVectorNoScale(Normal);
|
|
const FReal ContactPhi = -Penetration;
|
|
|
|
// Contact should be on bottom of A
|
|
// Normal should point upwards (from B to A)
|
|
// const FReal PreviousIncorrectLocationZ = TransformA.GetTranslation().Z + ShapeA.BoundingBox().Max().Z;
|
|
// const FReal PreviousIncorrectNormalZ = -1.0f;
|
|
const FReal ExpectedContactLocationZ = TransformA.GetTranslation().Z + ShapeA.BoundingBox().Min().Z;
|
|
const FReal ExpectedContactNormalZ = 1.0f;
|
|
const FReal ExpectedContactPhi = (TransformA.GetTranslation().Z + ShapeA.BoundingBox().Min().Z) - (TransformB.GetTranslation().Z + ShapeB.BoundingBox().Max().Z);
|
|
|
|
EXPECT_NEAR(ContactLocation.Z, ExpectedContactLocationZ, KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ContactNormal.Z, ExpectedContactNormalZ, KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ContactPhi, ExpectedContactPhi, KINDA_SMALL_NUMBER);
|
|
}
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, TestGJKConvexConvexEPABoundaryCondition)
|
|
{
|
|
GJKConvexConvexEPABoundaryCondition();
|
|
}
|
|
|
|
|
|
void NegativeScaleConvexTest()
|
|
{
|
|
TArray<FConvex::FVec3Type> ConvexVerts =
|
|
{
|
|
{512.000061, -1279.99988, -383.999939},
|
|
{511.999969, 6.81566016e-05, 2.23802308e-05},
|
|
{512.000000, -255.999939, 2.23802308e-05},
|
|
{-2.36513770e-05, -1.52587909e-05, -2.84217094e-14},
|
|
{1.80563184e-05, -256.000031, -2.84217094e-14},
|
|
{2.26354750e-05, -1024.00000, -383.999969},
|
|
{7.96019594e-05, -1280.00000, -383.999969},
|
|
{512.000061, -1023.99994, -383.999939}
|
|
};
|
|
TArray<FConvex::FVec3Type> ConvexVertices(MoveTemp(ConvexVerts));
|
|
FConvexPtr CoreConvex( new FImplicitConvex3(ConvexVertices, 0.0f));
|
|
const TImplicitObjectScaled<FImplicitConvex3> ScaledConvex(CoreConvex, FVec3(-1,1,1), 38.4000015);
|
|
const Chaos::FSphere Sphere(FVec3(0,0,0), 32);
|
|
const FRigidTransform3 StartTM(FVec3( -172.000000, -48.0000000, 52.0000000 ), FRotation3::FromIdentity());
|
|
|
|
|
|
FVec3 Dir(0, 0, -1);
|
|
FReal Length = 200;
|
|
FVec3 OutNormal;
|
|
FReal OutTime = -1;
|
|
FVec3 OutPos(0, 0, 0);
|
|
int32 OutFaceIdx = -1;
|
|
const bool bSuccess = GJKRaycast2(ScaledConvex, Sphere, StartTM, Dir, Length, OutTime, OutPos, OutNormal, (FReal)0., true);
|
|
EXPECT_TRUE(bSuccess);
|
|
}
|
|
|
|
void NegativeScaleConvexTest2()
|
|
{
|
|
//TArray<FVec3> ConvexVerts =
|
|
//{
|
|
// // subset of verts from above test.
|
|
// {512.000061, -1279.99988, -383.999939}, // Uncommenting this will cause sweep to miss. Why?
|
|
// {511.999969, 6.81566016e-05, 2.23802308e-05},
|
|
// {512.000000, -255.999939, 2.23802308e-05},
|
|
// {-2.36513770e-05, -1.52587909e-05, -2.84217094e-14},
|
|
// {1.80563184e-05, -256.000031, -2.84217094e-14},
|
|
//};
|
|
TArray<FConvex::FVec3Type> ConvexVerts =
|
|
{
|
|
// subset of verts from above test.
|
|
FVec3(-512, -1280, -384),
|
|
FVec3(-512, 0, 0),
|
|
FVec3(-512, -256, 0),
|
|
FVec3(0, 0, 0),
|
|
FVec3(0, -256, 0),
|
|
};
|
|
TArray<FConvex::FVec3Type> ConvexVertices(MoveTemp(ConvexVerts));
|
|
FImplicitConvex3 CoreConvex = FImplicitConvex3(ConvexVertices, 38.4000015);
|
|
const Chaos::FSphere Sphere(FVec3(0, 0, 0), 32);
|
|
const FRigidTransform3 StartTM(FVec3(-172.000000, -48.0000000, 52.0000000), FRotation3::FromIdentity());
|
|
|
|
|
|
FVec3 Dir(0, 0, -1);
|
|
FReal Length = 200;
|
|
FVec3 OutNormal;
|
|
FReal OutTime = -1;
|
|
FVec3 OutPos(0, 0, 0);
|
|
int32 OutFaceIdx = -1;
|
|
const bool bSuccess = GJKRaycast2(CoreConvex, Sphere, StartTM, Dir, Length, OutTime, OutPos, OutNormal, (FReal)0., true);
|
|
EXPECT_TRUE(bSuccess);
|
|
}
|
|
|
|
// Disabled until we use different margins for sweeping
|
|
GTEST_TEST(GJKTests, DISABLED_TestGJKConvexNegativeScale)
|
|
{
|
|
NegativeScaleConvexTest();
|
|
NegativeScaleConvexTest2();
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, BoxBoxWarmStartTest)
|
|
{
|
|
FAABB3 Box({ -50, -50, -50 }, { 50, 50, 50 });
|
|
|
|
FRigidTransform3 ATM = FRigidTransform3::Identity;
|
|
FRigidTransform3 BTM = FRigidTransform3(FVec3(0, 0, 105), FRotation3::FromIdentity());
|
|
FVec3 ClosestA, ClosestB, NormalA, NormalB;
|
|
FReal Penetration;
|
|
|
|
FGJKSimplexData WarmStartData;
|
|
FReal SupportDelta = FReal(0);
|
|
int32 VertexIndexA = INDEX_NONE;
|
|
int32 VertexIndexB = INDEX_NONE;
|
|
|
|
// Separated (GJK)
|
|
GJKPenetrationWarmStartable(Box, Box, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, -5.0f, KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestA.Z, (FReal)50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestB.Z, (FReal)-50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
|
|
GJKPenetrationWarmStartable(Box, Box, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, -5.0f, KINDA_SMALL_NUMBER);
|
|
|
|
BTM = FRigidTransform3(FVec3(0, 0, 145), FRotation3::FromIdentity());
|
|
GJKPenetrationWarmStartable(Box, Box, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, -45.0f, KINDA_SMALL_NUMBER);
|
|
|
|
BTM = FRigidTransform3(FVec3(0, 0, 145), FRotation3::FromAxisAngle(FVec3(1, 0, 0), FMath::DegreesToRadians(110.0f)));
|
|
GJKPenetrationWarmStartable(Box, Box, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, -30.9144f, KINDA_SMALL_NUMBER);
|
|
|
|
FReal Penetration2;
|
|
GJKPenetrationWarmStartable(Box, Box, BTM.GetRelativeTransformNoScale(ATM), Penetration2, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration2, Penetration, KINDA_SMALL_NUMBER);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, BoxBoxWarmStartDeepTest)
|
|
{
|
|
FAABB3 Box({ -50, -50, -50 }, { 50, 50, 50 });
|
|
|
|
FRigidTransform3 ATM = FRigidTransform3::Identity;
|
|
FRigidTransform3 BTM = FRigidTransform3(FVec3(0, 0, 60), FRotation3::FromIdentity());
|
|
FVec3 ClosestA, ClosestB, NormalA, NormalB;
|
|
FReal Penetration;
|
|
|
|
FGJKSimplexData WarmStartData;
|
|
FReal SupportDelta = FReal(0);
|
|
|
|
int32 VertexIndexA = INDEX_NONE;
|
|
int32 VertexIndexB = INDEX_NONE;
|
|
|
|
// Deep (EPA) No Margin
|
|
GJKPenetrationWarmStartable(Box, Box, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, 40.0f, KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestA.Z, (FReal)50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestB.Z, (FReal)-50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
|
|
// Deep (EPA) With Margin
|
|
WarmStartData = FGJKSimplexData();
|
|
TGJKCoreShape<FAABB3> MarginBox(Box, FReal(10));
|
|
GJKPenetrationWarmStartable(MarginBox, MarginBox, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, 40.0f, KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestA.Z, (FReal)50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestB.Z, (FReal)-50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
|
|
// Deep (EPA) With Margin and Relative Rotation
|
|
WarmStartData = FGJKSimplexData();
|
|
ATM = FRigidTransform3(FVec3(0, 0, 0), FRotation3::FromAxisAngle(FVec3(1, 0, 0), FMath::DegreesToRadians(180)));
|
|
GJKPenetrationWarmStartable(MarginBox, MarginBox, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, 40.0f, KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestA.Z, (FReal)-50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestB.Z, (FReal)-50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, SphereSphereWarmStartTest)
|
|
{
|
|
FImplicitSphere3 Sphere(FVec3(0), FReal(50));
|
|
|
|
FRigidTransform3 ATM = FRigidTransform3::Identity;
|
|
FRigidTransform3 BTM = FRigidTransform3(FVec3(0, 0, 105), FRotation3::FromIdentity());
|
|
FVec3 ClosestA, ClosestB, NormalA, NormalB;
|
|
FReal Penetration;
|
|
|
|
FGJKSimplexData WarmStartData;
|
|
FReal SupportDelta = FReal(0);
|
|
|
|
int32 VertexIndexA = INDEX_NONE;
|
|
int32 VertexIndexB = INDEX_NONE;
|
|
|
|
GJKPenetrationWarmStartable(Sphere, Sphere, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, (FReal)-5.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestA.Z, (FReal)50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestB.Z, (FReal)-50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
|
|
BTM = FRigidTransform3(FVec3(0, 0, 105), FRotation3::FromAxisAngle(FVec3(1,0,0), FMath::DegreesToRadians(180)));
|
|
GJKPenetrationWarmStartable(Sphere, Sphere, BTM.GetRelativeTransformNoScale(ATM), Penetration, ClosestA, ClosestB, NormalA, NormalB, VertexIndexA, VertexIndexB, WarmStartData, SupportDelta);
|
|
EXPECT_NEAR(Penetration, (FReal)-5.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestA.Z, (FReal)50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
EXPECT_NEAR(ClosestB.Z, (FReal)50.0f, (FReal)KINDA_SMALL_NUMBER);
|
|
|
|
}
|
|
|
|
|
|
GTEST_TEST(GJKTests, GJKBug_BadBarycentricCoords)
|
|
{
|
|
FImplicitBox3 BoxA({ -31.999998092651367, -0.73166346549987793, -47.015655517578125 },
|
|
{ 31.999998092651367, 0.73166346549987793, 47.015655517578125 });
|
|
FImplicitBox3 BoxB({ -64.202491760253906, -64.269241333007812, 0.27499961853027344 },
|
|
{ -0.20249176025390625, -0.18923950195312500, 38.524999618530273 });
|
|
|
|
const FRigidTransform3 Transform(FVec3(4.28251314, -16.3213539, 32.0828743), FQuat(0.360390902, 0.00000000, 0.00000000, 0.932801366));
|
|
|
|
const FVec3 RayDir(0.00000000, 0.672346294, -0.740236819);
|
|
const FRealDouble Length = 37.388404846191406;
|
|
FRealDouble OutTime = 0;
|
|
FVec3 OutPosition(0);
|
|
FVec3 OutNormal(0);
|
|
const FRealDouble Thickness = 0;
|
|
const bool bComputeMTD = true;
|
|
const FVec3 Offset(-4.2825129036202441, -9.4891323727604799, -34.722524278655456);
|
|
|
|
bool bResult = GJKRaycast2<FRealDouble, FImplicitBox3, FImplicitBox3>(BoxA, BoxB, Transform, RayDir, Length, OutTime, OutPosition, OutNormal, Thickness, bComputeMTD, Offset, Thickness);
|
|
EXPECT_TRUE(bResult);
|
|
EXPECT_NEAR(OutPosition.X, -13.95, 1e-1);
|
|
EXPECT_NEAR(OutPosition.Y, -0.73, 1e-1);
|
|
EXPECT_NEAR(OutPosition.Z, 14.63, 1e-1);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_LargeScaledBoxBoxTest)
|
|
{
|
|
|
|
TArray<FConvex::FVec3Type> ConvexParticles;
|
|
ConvexParticles.SetNum(8);
|
|
|
|
// This is a box with some small deviations
|
|
ConvexParticles[0] = { 500.000000, -500.000031, 2.84217094e-14 };
|
|
ConvexParticles[1] = { 500.000000, 499.999969, -50.0000153 };
|
|
ConvexParticles[2] = { 500.000000, -500.000031, -50.0000153 };
|
|
ConvexParticles[3] = { -500.000183, 499.999969, -50.0000153 };
|
|
ConvexParticles[4] = { -500.000183, -500.000031, 2.84217094e-14 };
|
|
ConvexParticles[5] = { -500.000183, -500.000031, -50.0000153 };
|
|
ConvexParticles[6] = { -500.000183, 499.999969, -2.84217094e-14 };
|
|
ConvexParticles[7] = { 500.000000, 499.999969, -2.84217094e-14 };
|
|
|
|
Chaos::FConvexPtr BigBox( new Chaos::FConvex(ConvexParticles, 0.0f));
|
|
|
|
// These two boxes are clearly intersecting each other
|
|
|
|
Chaos::TBox<Chaos::FReal, 3> SmallBox({ -3200, -3200, -3200 }, { 3200, 3200, 3200 }, 0);
|
|
|
|
TImplicitObjectScaled<Chaos::FConvex> BigBoxScaled(BigBox, FVec3(50, 50, 1));
|
|
const TVector<FReal, 3> Translation{16000, 16000, -500};
|
|
|
|
TRigidTransform<Chaos::FReal, 3> BToATM( Translation , TRotation<FReal, 3>::Identity);
|
|
EXPECT_TRUE(GJKIntersection(BigBoxScaled, SmallBox, BToATM, FReal(0), Chaos::TVector<FReal, 3>(-16000, -16000, 500)));
|
|
|
|
}
|
|
|
|
// Test known large triangle failure case
|
|
GTEST_TEST(GJKTests, GJK_LargeTriangleCase)
|
|
{
|
|
VectorRegister4Float TriMeshScaleVector = MakeVectorRegister(2000.0f, 2000.0f, 1.0f, 0.0f);
|
|
|
|
VectorRegister4Float A = MakeVectorRegister(100.0f, 0.0f, 100.0f, 0.0f);
|
|
VectorRegister4Float B = MakeVectorRegister(0.0f, 100.0f, 100.0f, 0.0f);
|
|
VectorRegister4Float C = MakeVectorRegister(0.0f, 0.0f, 100.0f, 0.0f);
|
|
|
|
A = VectorMultiply(A, TriMeshScaleVector);
|
|
B = VectorMultiply(B, TriMeshScaleVector);
|
|
C = VectorMultiply(C, TriMeshScaleVector);
|
|
|
|
FTriangleRegister Tri(A, B, C);
|
|
const VectorRegister4Float TriNormal = VectorCross(VectorSubtract(B, A), VectorSubtract(C, A));
|
|
|
|
FRealSingle Time;
|
|
VectorRegister4Float OutPositionSimd, OutNormalSimd;
|
|
Chaos::FSphere QueryGeom({ 0, 0, 0 }, 5.0f);
|
|
VectorRegister4Float RotationSimd = MakeVectorRegister(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
VectorRegister4Float TranslationSimd = MakeVectorRegister(100000.0f, 100022.820f, 213.002823f, 0.0f);
|
|
VectorRegister4Float RayDirSimd = MakeVectorRegister(-2.22044605e-16f, 0.0f, -1.0f, 0.0f);
|
|
float LengthScale = 1.0f;
|
|
float CurDataLength = 108.002823f;
|
|
const bool bComputeMTD = true;
|
|
bool bFoundIntersection;
|
|
if (Tri.IsTooBigForSinglePrecision())
|
|
{
|
|
VectorRegister4Double OutPositionDouble, OutNormalDouble;
|
|
bFoundIntersection = GJKRaycast2ImplSimd<VectorRegister4Double>(Tri, QueryGeom, VectorRegister4Double(RotationSimd), VectorRegister4Double(TranslationSimd), VectorRegister4Double(RayDirSimd), LengthScale * CurDataLength, Time, OutPositionDouble, OutNormalDouble, bComputeMTD, GlobalVectorConstants::Double1000);
|
|
OutPositionSimd = MakeVectorRegisterFloatFromDouble(OutPositionDouble);
|
|
OutNormalSimd = MakeVectorRegisterFloatFromDouble(OutNormalDouble);
|
|
}
|
|
else
|
|
{
|
|
bFoundIntersection = GJKRaycast2ImplSimd<VectorRegister4Float>(Tri, QueryGeom, RotationSimd, TranslationSimd, RayDirSimd, LengthScale * CurDataLength, Time, OutPositionSimd, OutNormalSimd, bComputeMTD, GlobalVectorConstants::FloatMinusOne);
|
|
}
|
|
|
|
EXPECT_FALSE(bFoundIntersection);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepConvexLargeTriangleInMesh)
|
|
{
|
|
using namespace Chaos;
|
|
TArray<FConvex::FVec3Type> ConvexSurface(
|
|
{
|
|
{5.0, -5.0, -5.0},
|
|
{-5.0, 5.0, -5.0},
|
|
{5.0, 5.0, -5.0},
|
|
{5.0, 5.0, 5.0},
|
|
{-5.0, -5.0, -5.0},
|
|
{-5.0, -5.0, 5.0},
|
|
{5.0, -5.0, 5.0},
|
|
{-5.0, 5.0, 5.0},
|
|
});
|
|
|
|
FConvex ConvexBox(MoveTemp(ConvexSurface), 0.0f);
|
|
|
|
FTriangleMeshImplicitObject::ParticlesType TrimeshParticles(
|
|
{
|
|
{500.0000000, 500.0000000, 0.0},
|
|
{500.0000000, -500.0000000, 0.0},
|
|
{-500.0000000, 500.0000000, 0.0},
|
|
{-500.0000000, -500.0000000, 0.0}
|
|
});
|
|
|
|
TArray<TVec3<int32>> Indices;
|
|
Indices.Emplace(1, 0, 2);
|
|
Indices.Emplace(1, 2, 3);
|
|
|
|
TArray<uint16> Materials;
|
|
Materials.Emplace(0);
|
|
Materials.Emplace(0);
|
|
FTriangleMeshImplicitObjectPtr TriangleMesh(new FTriangleMeshImplicitObject(MoveTemp(TrimeshParticles), MoveTemp(Indices), MoveTemp(Materials)));
|
|
TImplicitObjectScaled<FTriangleMeshImplicitObject> ScaledTriangleMesh = TImplicitObjectScaled<FTriangleMeshImplicitObject>(TriangleMesh, FVec3(10000.0, 10000.0, 1.0));
|
|
|
|
FQuat Rotation(0.00488796039, 0.00569311855, -0.000786740216, 0.999971569);
|
|
FVec3 Translation(10500.365723, -14500.4132690, 8.2289352);
|
|
|
|
TRigidTransform<FReal, 3> Transform(Translation, Rotation);
|
|
|
|
|
|
FVec3 Dir(-0.00339674903, 5.76980747e-05, -0.999994159);
|
|
FReal Length = 10.0;
|
|
|
|
FReal OutTime = -1;
|
|
FVec3 Normal(0.0f);
|
|
FVec3 Position(0.0f);
|
|
int32 FaceIndex = -1;
|
|
FVec3 FaceNormal(0.0);
|
|
bool bResult = ScaledTriangleMesh.LowLevelSweepGeom(ConvexBox, Transform, Dir, Length, OutTime, Position, Normal, FaceIndex, FaceNormal, 0.0f, true);
|
|
EXPECT_TRUE(bResult);
|
|
EXPECT_EQ(Position.Z, 0.0);
|
|
EXPECT_NEAR(Position.X, 10505.365723, 1);
|
|
EXPECT_NEAR(Position.Y, -14505.4132690, 1);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepBoxLargeTriangleInMesh)
|
|
{
|
|
Chaos::TBox<Chaos::FReal, 3> QueryGeom({ -5, -5, -5 }, { 5, 5, 5 });
|
|
|
|
FTriangleMeshImplicitObject::ParticlesType TrimeshParticles(
|
|
{
|
|
{500.0000000, 500.0000000, 0.0},
|
|
{500.0000000, -500.0000000, 0.0},
|
|
{-500.0000000, 500.0000000, 0.0},
|
|
{-500.0000000, -500.0000000, 0.0}
|
|
});
|
|
|
|
TArray<TVec3<int32>> Indices;
|
|
Indices.Emplace(1, 0, 2);
|
|
Indices.Emplace(1, 2, 3);
|
|
|
|
TArray<uint16> Materials;
|
|
Materials.Emplace(0);
|
|
Materials.Emplace(0);
|
|
FTriangleMeshImplicitObjectPtr TriangleMesh(new FTriangleMeshImplicitObject(MoveTemp(TrimeshParticles), MoveTemp(Indices), MoveTemp(Materials)));
|
|
TImplicitObjectScaled<FTriangleMeshImplicitObject> ScaledTriangleMesh = TImplicitObjectScaled<FTriangleMeshImplicitObject>(TriangleMesh, FVec3(10000.0, 10000.0, 1.0));
|
|
|
|
FQuat Rotation(0.00488796039, 0.00569311855, -0.000786740216, 0.999971569);
|
|
FVec3 Translation(10500.365723, -14500.4132690, 8.2289352);
|
|
|
|
TRigidTransform<FReal, 3> Transform(Translation, Rotation);
|
|
|
|
|
|
FVec3 Dir(-0.00339674903, 5.76980747e-05, -0.999994159);
|
|
FReal Length = 10.0;
|
|
|
|
FReal OutTime = -1;
|
|
FVec3 Normal(0.0f);
|
|
FVec3 Position(0.0f);
|
|
int32 FaceIndex = -1;
|
|
FVec3 FaceNormal(0.0);
|
|
bool bResult = ScaledTriangleMesh.LowLevelSweepGeom(QueryGeom, Transform, Dir, Length, OutTime, Position, Normal, FaceIndex, FaceNormal, 0.0f, true);
|
|
EXPECT_TRUE(bResult);
|
|
EXPECT_EQ(Position.Z, 0.0);
|
|
EXPECT_NEAR(Position.X, 10505.365723, 1);
|
|
EXPECT_NEAR(Position.Y, -14505.4132690, 1);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepSphereLargeTriangleInMesh)
|
|
{
|
|
Chaos::FSphere QueryGeom({ 0, 0, 0 }, 5.0f);
|
|
|
|
FTriangleMeshImplicitObject::ParticlesType TrimeshParticles(
|
|
{
|
|
{500.0000000, 500.0000000, 0.0},
|
|
{500.0000000, -500.0000000, 0.0},
|
|
{-500.0000000, 500.0000000, 0.0},
|
|
{-500.0000000, -500.0000000, 0.0}
|
|
});
|
|
|
|
TArray<TVec3<int32>> Indices;
|
|
Indices.Emplace(1, 0, 2);
|
|
Indices.Emplace(1, 2, 3);
|
|
|
|
TArray<uint16> Materials;
|
|
Materials.Emplace(0);
|
|
Materials.Emplace(0);
|
|
FTriangleMeshImplicitObjectPtr TriangleMesh(new FTriangleMeshImplicitObject(MoveTemp(TrimeshParticles), MoveTemp(Indices), MoveTemp(Materials)));
|
|
TImplicitObjectScaled<FTriangleMeshImplicitObject> ScaledTriangleMesh = TImplicitObjectScaled<FTriangleMeshImplicitObject>(TriangleMesh, FVec3(10000.0, 10000.0, 1.0));
|
|
|
|
FQuat Rotation(0.00488796039, 0.00569311855, -0.000786740216, 0.999971569);
|
|
FVec3 Translation(10500.365723, -14500.4132690, 8.2289352);
|
|
|
|
TRigidTransform<FReal, 3> Transform(Translation, Rotation);
|
|
|
|
|
|
FVec3 Dir(-0.00339674903, 5.76980747e-05, -0.999994159);
|
|
FReal Length = 10.0;
|
|
|
|
FReal OutTime = -1;
|
|
FVec3 Normal(0.0f);
|
|
FVec3 Position(0.0f);
|
|
int32 FaceIndex = -1;
|
|
FVec3 FaceNormal(0.0);
|
|
bool bResult = ScaledTriangleMesh.LowLevelSweepGeom(QueryGeom, Transform, Dir, Length, OutTime, Position, Normal, FaceIndex, FaceNormal, 0.0f, true);
|
|
EXPECT_TRUE(bResult);
|
|
EXPECT_EQ(Position.Z, 0.0);
|
|
EXPECT_NEAR(Position.X, 10500.365723, 1e-1);
|
|
EXPECT_NEAR(Position.Y, -14500.4132690, 1e-1);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepCapsuleLargeTriangleInMesh)
|
|
{
|
|
Chaos::FCapsule QueryGeom({ 0, 0, 1.0 }, { 0, 0, -1.0 }, 5.0f);
|
|
|
|
FTriangleMeshImplicitObject::ParticlesType TrimeshParticles(
|
|
{
|
|
{500.0000000, 500.0000000, 0.0},
|
|
{500.0000000, -500.0000000, 0.0},
|
|
{-500.0000000, 500.0000000, 0.0},
|
|
{-500.0000000, -500.0000000, 0.0}
|
|
});
|
|
|
|
TArray<TVec3<int32>> Indices;
|
|
Indices.Emplace(1, 0, 2);
|
|
Indices.Emplace(1, 2, 3);
|
|
|
|
TArray<uint16> Materials;
|
|
Materials.Emplace(0);
|
|
Materials.Emplace(0);
|
|
FTriangleMeshImplicitObjectPtr TriangleMesh(new FTriangleMeshImplicitObject(MoveTemp(TrimeshParticles), MoveTemp(Indices), MoveTemp(Materials)));
|
|
TImplicitObjectScaled<FTriangleMeshImplicitObject> ScaledTriangleMesh = TImplicitObjectScaled<FTriangleMeshImplicitObject>(TriangleMesh, FVec3(10000.0, 10000.0, 1.0));
|
|
|
|
FQuat Rotation(0.00488796039, 0.00569311855, -0.000786740216, 0.999971569);
|
|
FVec3 Translation(10500.365723, -14500.4132690, 8.2289352);
|
|
|
|
TRigidTransform<FReal, 3> Transform(Translation, Rotation);
|
|
|
|
|
|
FVec3 Dir(-0.00339674903, 5.76980747e-05, -0.999994159);
|
|
FReal Length = 10.0;
|
|
|
|
FReal OutTime = -1;
|
|
FVec3 Normal(0.0f);
|
|
FVec3 Position(0.0f);
|
|
int32 FaceIndex = -1;
|
|
FVec3 FaceNormal(0.0);
|
|
bool bResult = ScaledTriangleMesh.LowLevelSweepGeom(QueryGeom, Transform, Dir, Length, OutTime, Position, Normal, FaceIndex, FaceNormal, 0.0f, true);
|
|
EXPECT_TRUE(bResult);
|
|
EXPECT_EQ(Position.Z, 0.0);
|
|
EXPECT_NEAR(Position.X, 10500.365723, 1e-1);
|
|
EXPECT_NEAR(Position.Y, -14500.4132690, 1e-1);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepSphereBox)
|
|
{
|
|
Chaos::FSphere QueryGeom({ 0, 0, 0 }, 12.0f);
|
|
|
|
Chaos::TBox<Chaos::FReal, 3> Box(FVec3(-240.742279/2.0, -16.483643/2.0, -17.078735/2.0), FVec3(240.742279 / 2.0, 16.483643 / 2.0, 17.078735 / 2.0));
|
|
|
|
FVec3 BoxWorldTranslation(791.002986, -13679.957812, 7977.850960);
|
|
FQuat BoxWorldRotation = FQuat(0.006953, -0.013390, 0.887363, -0.460825);
|
|
TRigidTransform<FReal, 3> BoxWorldTransform(BoxWorldTranslation, BoxWorldRotation);
|
|
|
|
FVec3 Start(715.18582, -13666.121838, 7860.814454);
|
|
FVec3 End(727.595994, -13746.006452, 8140.664592);
|
|
|
|
TRigidTransform<FReal, 3> TransformSphere(Start, FQuat::Identity);
|
|
|
|
TRigidTransform<FReal, 3> FromBoxToSphere1 = TRigidTransform<FReal, 3>::MultiplyNoScale(BoxWorldTransform, TransformSphere.Inverse());
|
|
TRigidTransform<FReal, 3> FromBoxToSphere2 = BoxWorldTransform.GetRelativeTransform(TransformSphere);
|
|
|
|
|
|
FVec3 Dir(0.042604, -0.274241, 0.960717);
|
|
const FVec3 LocalDir = TransformSphere.InverseTransformVectorNoScale(Dir);
|
|
FVec3 Ray = End - Start;
|
|
FReal Length = Ray.Length();
|
|
|
|
FReal OutTime = -1;
|
|
FVec3 Normal(0.0f);
|
|
FVec3 Position(0.0f);
|
|
int32 FaceIndex = -1;
|
|
FVec3 FaceNormal(0.0);
|
|
bool bResult = GJKRaycast2(QueryGeom, Box, FromBoxToSphere2, Dir, Length, OutTime, Position, Normal, 0.0, true);
|
|
|
|
Position = TransformSphere.TransformPositionNoScale(Position);
|
|
EXPECT_FALSE(bResult);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepSphereTriangleMesh)
|
|
{
|
|
{
|
|
FImplicitSphere3 Sphere{ FVec3(0.0f, 0.0f, 0.0f), 8.16201973f };
|
|
VectorRegister4Float A = MakeVectorRegister(755.133118f, 69.0732956f, 184.725311f, 0.0f);
|
|
VectorRegister4Float B = MakeVectorRegister(713.519043f, 205.570648f, 172.950577f, 0.0f);
|
|
VectorRegister4Float C = MakeVectorRegister(686.582520f, 252.185165f, 365.669464f, 0.0f);
|
|
FTriangleRegister Tri{ A,B,C };
|
|
|
|
VectorRegister4Float StartRotation = MakeVectorRegister(0.0f, 0.0f, 0.0f, 1.0f);
|
|
VectorRegister4Float StartPosition = MakeVectorRegister(727.730835f, 221.037231f, 363.199402f, 0.0f);
|
|
VectorRegister4Float RayDirection = MakeVectorRegister(0.0423882306f, 0.0380843617f, -0.998375118f, 0.0f);
|
|
|
|
FRealSingle RayLength = 200.0;
|
|
FRealSingle OutTime;
|
|
VectorRegister4Float OutPosition;
|
|
VectorRegister4Float OutNormal;
|
|
|
|
const bool bDidHit = GJKRaycast2ImplSimd<VectorRegister4Float>(Tri, Sphere, StartRotation, StartPosition, RayDirection, RayLength, OutTime, OutPosition, OutNormal, true, GlobalVectorConstants::Float1000);
|
|
EXPECT_FALSE(bDidHit);
|
|
}
|
|
{
|
|
FImplicitSphere3 Sphere{ FVec3(0.0f, 0.0f, 0.0f), 8.16201973f };
|
|
VectorRegister4Float A = MakeVectorRegister(755.133118f, 69.0732956f, 184.725311f, 0.0f);
|
|
VectorRegister4Float B = MakeVectorRegister(713.519043f, 205.570648f, 172.950577f, 0.0f);
|
|
VectorRegister4Float C = MakeVectorRegister(686.582520f, 252.185165f, 365.669464f, 0.0f);
|
|
FTriangleRegister Tri{ A,B,C };
|
|
|
|
VectorRegister4Double StartRotation = MakeVectorRegister(0.0f, 0.0f, 0.0f, 1.0f);
|
|
VectorRegister4Double StartPosition = MakeVectorRegister(727.730835f, 221.037231f, 363.199402f, 0.0f);
|
|
VectorRegister4Double RayDirection = MakeVectorRegister(0.0423882306f, 0.0380843617f, -0.998375118f, 0.0f);
|
|
|
|
FRealSingle RayLength = 200.0;
|
|
FRealSingle OutTime;
|
|
VectorRegister4Double OutPosition;
|
|
VectorRegister4Double OutNormal;
|
|
|
|
const bool bDidHit = GJKRaycast2ImplSimd<VectorRegister4Double>(Tri, Sphere, StartRotation, StartPosition, RayDirection, RayLength, OutTime, OutPosition, OutNormal, true, GlobalVectorConstants::Float1000);
|
|
EXPECT_FALSE(bDidHit);
|
|
}
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepSphereConvexMesh)
|
|
{
|
|
// Test case bug that was not working on XSX, and maybe on Switch
|
|
const TArray<Chaos::FConvex::FVec3Type> ConvexVertices
|
|
{
|
|
{ -1320.50159, 461.919220, 18.5806618 },
|
|
{ -1321.64062, 464.953125, -81.3668213 },
|
|
{ -723.913879, 585.305603, -65.5020828 },
|
|
{ -1026.47400, -493.330170, -13.7665596 },
|
|
{ -1027.61304, -490.296295, -113.714035 },
|
|
{ -723.348694, 578.755981, 34.2816010 },
|
|
{ -668.640442, -417.582031, -31.4263611 },
|
|
{ -669.205688, -411.032410, -131.210037 }
|
|
};
|
|
|
|
const Chaos::FConvexPtr ConvexPtr(new Chaos::FConvex(ConvexVertices, 0.0f));
|
|
const Chaos::TImplicitObjectInstanced<Chaos::FConvex> Convex(ConvexPtr);
|
|
const Chaos::FSphere Sphere(Chaos::FVec3::ZeroVector, 40.0);
|
|
|
|
const UE::Math::TQuat BToARotation{ -0.0135237072, -0.149320737, 0.0850137547, 0.985034525 };
|
|
const Chaos::FVec3 StartPoint{ -2305.07544, -225.373749, 362.317993 };
|
|
const Chaos::FVec3 RayDir{ 0.958641946, 0.168052554, -0.229703903 };
|
|
const Chaos::FReal RayLength{ 1214.51257 };
|
|
const bool bComputeMTD{ true };
|
|
const Chaos::FVec3 InitialDir{ 270.538879, -2088.81055, -1029.13525 };
|
|
|
|
Chaos::FReal TimeOffImpact{ };
|
|
Chaos::FVec3 Position{ };
|
|
Chaos::FVec3 Normal{ };
|
|
bool bHit = Chaos::GJKRaycast2<Chaos::FReal>(Convex, Sphere, Chaos::FRigidTransform3(UE::Math::TTransform<double>(BToARotation, StartPoint)), RayDir, RayLength, TimeOffImpact, Position, Normal, 0, true, InitialDir, 0.0);
|
|
EXPECT_FALSE(bHit);
|
|
}
|
|
|
|
GTEST_TEST(GJKTests, GJK_SweepConvexMeshConvexMesh)
|
|
{
|
|
const TArray<Chaos::FConvex::FVec3Type> ConvexVerticesA
|
|
{
|
|
{ 372.876678, 1253.23181, 1937.96875 },
|
|
{ -516.649231, 1253.23181, 1937.96875 },
|
|
{ -516.649231, 1253.23181, 1873.81213 },
|
|
{ -516.649231, 1253.23181, 1873.81213 },
|
|
{ 372.876678, 1253.23181, 1873.81213 },
|
|
{ 372.876678, -2157.82104, 2122.00000 },
|
|
{ 372.876678, -2157.82104, 2057.84326 },
|
|
{ -516.649231, -2157.82104, 2057.84326 },
|
|
{ -516.649231, -2157.82104, 2122.00000 }
|
|
};
|
|
const Chaos::FConvexPtr ConvexPtrA(new Chaos::FConvex(ConvexVerticesA, 0.0f));
|
|
const Chaos::TImplicitObjectInstanced<Chaos::FConvex> ConvexA(ConvexPtrA);
|
|
|
|
const TArray<Chaos::FConvex::FVec3Type> ConvexVerticesB
|
|
{
|
|
{ 73.4703674, -61.1765442, -0.19269731 },
|
|
{ -58.2119789, -43.0196724, 150.211761 },
|
|
{ 56.7236938, -43.0196762, 150.211761 },
|
|
{ 73.4703674, 87.8292542, -0.19269731 },
|
|
{ -73.4703674, 87.8292542, -0.19269731 },
|
|
{ -73.4703751, -61.1765366, -0.19269731 },
|
|
{ -45.8896980, 52.8858337, 150.211761 },
|
|
{ 44.4014816, 52.8858109, 150.211761 }
|
|
};
|
|
const Chaos::FConvexPtr ConvexPtrB(new Chaos::FConvex(ConvexVerticesB, 0.0f));
|
|
const Chaos::TImplicitObjectInstanced<Chaos::FConvex> ConvexB(ConvexPtrB);
|
|
{
|
|
// Test case bug that was fixed on PS5
|
|
const UE::Math::TQuat BToARotation{ -5.54229738e-017, -3.12762759e-018, 0.02172332, 0.99976402 };
|
|
const Chaos::FVec3 StartPoint{ -57.6924553, -1033.34961, 2064.99536 };
|
|
const Chaos::FVec3 RayDir{ -0.00042043, 0.99854773, -0.05387306 };
|
|
const Chaos::FReal RayLength{ 5.3171567916870117 };
|
|
const Chaos::FVec3 InitialDir{ 173.583862, 1020.29828, -2064.99536 };
|
|
const bool bComputeMTD{ true };
|
|
|
|
Chaos::FReal Time{ };
|
|
Chaos::FVec3 Position{ };
|
|
Chaos::FVec3 Normal{ };
|
|
bool bHit{ Chaos::GJKRaycast2<Chaos::FReal>(ConvexA, ConvexB, Chaos::FRigidTransform3(UE::Math::TTransform<double>(BToARotation, StartPoint)), RayDir, RayLength, Time, Position, Normal, 0, true, InitialDir, 0.0) };
|
|
EXPECT_TRUE(bHit);
|
|
EXPECT_NEAR(-0.000022, Time, 1e-5);
|
|
}
|
|
{
|
|
// Test case falling on PS5
|
|
const UE::Math::TQuat BToARotation{ -5.54229738e-017, -3.12762759e-018, 0.02172332, 0.99976402 };
|
|
const Chaos::FVec3 StartPoint{ -57.4445953, -974.640625, 2061.82788 };
|
|
const Chaos::FVec3 RayDir{ -0.00025997, 0.99854779, -0.05387305 };
|
|
const Chaos::FReal RayLength{ 2.4696500301361084 };
|
|
const Chaos::FVec3 InitialDir{ 166.732468, 961.989868, -2061.82788 };
|
|
const bool bComputeMTD{ true };
|
|
|
|
Chaos::FReal Time{ };
|
|
Chaos::FVec3 Position{ };
|
|
Chaos::FVec3 Normal{ };
|
|
bool bHit{ Chaos::GJKRaycast2<Chaos::FReal>(ConvexA, ConvexB, Chaos::FRigidTransform3(UE::Math::TTransform<double>(BToARotation, StartPoint)), RayDir, RayLength, Time, Position, Normal, 0, true, InitialDir, 0.0) };
|
|
EXPECT_TRUE(bHit);
|
|
EXPECT_NEAR(0.0, Time, 1e-3);
|
|
}
|
|
}
|
|
} |