Files
UnrealEngineUWP/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestMostOpposing.cpp
Andrew Davidson 3ddc3a4da3 Merge up from //UE5/Dev-LargeWorldCoordinates
#rb none

[CL 16211417 by Andrew Davidson in ue5-main branch]
2021-05-05 15:07:25 -04:00

267 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HeadlessChaosTestMostOpposing.h"
#include "HeadlessChaos.h"
#include "HeadlessChaosTestUtility.h"
#include "Chaos/TriangleMeshImplicitObject.h"
#include "Chaos/ImplicitObjectScaled.h"
#include "Chaos/Convex.h"
namespace ChaosTest
{
using namespace Chaos;
/*We want to test the following:
- Correct face index simple case
- Correct face on shared edge
*/
void TrimeshMostOpposing()
{
FReal Time;
FVec3 Position;
FVec3 Normal;
TArray<uint16> DummyMaterials;
int32 FaceIndex;
FParticles Particles;
Particles.AddParticles(6);
Particles.X(0) = FVec3(1, 1, 1);
Particles.X(1) = FVec3(5, 1, 1);
Particles.X(2) = FVec3(1, 5, 1);
Particles.X(3) = FVec3(1, 1, 1);
Particles.X(4) = FVec3(1, 5, 1);
Particles.X(5) = FVec3(1, 1, -5);
TArray<TVec3<int32>> Indices;
Indices.Emplace(0, 1, 2);
Indices.Emplace(3, 4, 5);
FTriangleMeshImplicitObject Tri(MoveTemp(Particles), MoveTemp(Indices), MoveTemp(DummyMaterials));
//simple into the triangle
bool bHit = Tri.Raycast(FVec3(3, 2, 2), FVec3(0, 0, -1), 2, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(Tri.FindMostOpposingFace(Position, FVec3(0, 0, -1), FaceIndex, 0.01), 0);
EXPECT_EQ(Tri.GetFaceNormal(0).X, Normal.X);
EXPECT_EQ(Tri.GetFaceNormal(0).Y, Normal.Y);
EXPECT_EQ(Tri.GetFaceNormal(0).Z, Normal.Z);
//simple into second triangle
bHit = Tri.Raycast(FVec3(0, 2, 0), FVec3(1, 0, 0), 2, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, 1);
EXPECT_EQ(Tri.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 1);
const FVec3 FaceNormal = Tri.GetFaceNormal(1);
EXPECT_EQ(FaceNormal.X, Normal.X);
EXPECT_EQ(FaceNormal.Y, Normal.Y);
EXPECT_EQ(FaceNormal.Z, Normal.Z);
//very close to edge, for now just return face hit regardless of direction because that's the implementation we currently rely on.
//todo: inconsistent with hulls, should make them the same, but may have significant impact on existing content
bHit = Tri.Raycast(FVec3(0, 2, 0.9), FVec3(1, 0, 0), 2, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, 1);
EXPECT_EQ(Tri.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 1);
EXPECT_EQ(Tri.FindMostOpposingFace(Position, FVec3(0, 0, -1), FaceIndex, 0.01), 1); //ignores direction completely as per current implementation
}
void ConvexMostOpposing()
{
FReal Time;
FVec3 Position;
FVec3 Normal;
int32 FaceIndex;
TArray<FVec3> Particles;
Particles.SetNum(6);
Particles[0] = FVec3(1, 1, 1);
Particles[1] = FVec3(5, 1, 1);
Particles[2] = FVec3(1, 5, 1);
Particles[3] = FVec3(1, 1, 1);
Particles[4] = FVec3(1, 5, 1);
Particles[5] = FVec3(1, 1, -5);
FConvex Convex(MoveTemp(Particles), 0.0f);
//simple into the triangle
bool bHit = Convex.Raycast(FVec3(3, 2, 2), FVec3(0, 0, -1), 2, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_FLOAT_EQ(Time, 1);
EXPECT_FLOAT_EQ(Position.X, 3);
EXPECT_FLOAT_EQ(Position.Y, 2);
EXPECT_FLOAT_EQ(Position.Z, 1);
EXPECT_EQ(FaceIndex, INDEX_NONE); //convex should not compute its own face index as this is too expensive
EXPECT_EQ(Convex.FindMostOpposingFace(Position, FVec3(0, 0, -1), FaceIndex, 0.01 + SMALL_NUMBER), 1); //front face, just so happens that convex hull generates the planes in this order
//simple into second triangle
bHit = Convex.Raycast(FVec3(0, 2, 0), FVec3(1, 0, 0), 2, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_FLOAT_EQ(Time, 1);
EXPECT_FLOAT_EQ(Position.X, 1);
EXPECT_FLOAT_EQ(Position.Y, 2);
EXPECT_FLOAT_EQ(Position.Z, 0);
EXPECT_EQ(FaceIndex, INDEX_NONE); //convex should not compute its own face index as this is too expensive
EXPECT_EQ(Convex.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 3); //side face, just so happens that convex hull generates the planes in this order
bHit = Convex.Raycast(FVec3(0, 2, 0.99), FVec3(1, 0, 0), 2, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, INDEX_NONE);
EXPECT_EQ(Convex.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01 + SMALL_NUMBER), 3);
EXPECT_EQ(Convex.FindMostOpposingFace(Position, FVec3(0, 0, -1), FaceIndex, 0.01 + SMALL_NUMBER), 1);
//again but far enough away from edge
bHit = Convex.Raycast(FVec3(0, 2, 0.9), FVec3(1, 0, 0), 2, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, INDEX_NONE);
EXPECT_EQ(Convex.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01 + SMALL_NUMBER), 3);
EXPECT_EQ(Convex.FindMostOpposingFace(Position, FVec3(0, 0, -1), FaceIndex, 0.01 + SMALL_NUMBER), 3); //too far to care about other face
}
void ScaledMostOpposing()
{
FReal Time;
FVec3 Position;
FVec3 Normal;
int32 FaceIndex;
TArray<FVec3> Particles;
Particles.SetNum(6);
Particles[0] = FVec3(0, -1, 1);
Particles[1] = FVec3(1, -1, -1);
Particles[2] = FVec3(0, 1, 1);
Particles[3] = FVec3(0, -1, 1);
Particles[4] = FVec3(0, 1, 1);
Particles[5] = FVec3(-1, -1, -1);
TUniquePtr<FImplicitObject> Convex = MakeUnique<FConvex>(MoveTemp(Particles), 0.0f);
//identity scale
{
TImplicitObjectScaledGeneric<FReal, 3> Scaled(MakeSerializable(Convex), FVec3(1, 1, 1));
//simple into the triangle
bool bHit = Scaled.Raycast(FVec3(0.5, 0, 2), FVec3(0, 0, -1), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(Position.X, 0.5);
EXPECT_EQ(Position.Y, 0);
EXPECT_EQ(Position.Z, 2 - Time);
EXPECT_EQ(FaceIndex, INDEX_NONE); //convex should not compute its own face index as this is too expensive
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(0, 0, -1), FaceIndex, 0.01), 2); //x+ face, just so happens that convex hull generates the planes in this order
//simple into second triangle
bHit = Scaled.Raycast(FVec3(-2, 0, 0.5), FVec3(1, 0, 0), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(Position.X, -2+Time);
EXPECT_EQ(Position.Y, 0);
EXPECT_EQ(Position.Z, 0.5);
EXPECT_EQ(FaceIndex, INDEX_NONE); //convex should not compute its own face index as this is too expensive
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 3); //x- face, just so happens that convex hull generates the planes in this order
bHit = Scaled.Raycast(FVec3(-0.001, 0, 2), FVec3(0, 0, -1), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, INDEX_NONE);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 3);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(-1, 0, 0), FaceIndex, 0.01), 2);
//again but far enough away from edge
bHit = Scaled.Raycast(FVec3(-0.1, 0, 2), FVec3(0, 0, -1), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, INDEX_NONE);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 3);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(-1, 0, 0), FaceIndex, 0.01), 3); //too far to care about other face
}
//non-uniform scale
{
TImplicitObjectScaledGeneric<FReal, 3> Scaled(MakeSerializable(Convex), FVec3(2, 1, 1));
//simple into the triangle
bool bHit = Scaled.Raycast(FVec3(0.5, 0, 2), FVec3(0, 0, -1), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(Position.X, 0.5);
EXPECT_EQ(Position.Y, 0);
EXPECT_EQ(Position.Z, 2 - Time);
EXPECT_EQ(FaceIndex, INDEX_NONE); //convex should not compute its own face index as this is too expensive
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(0, 0, -1), FaceIndex, 0.01), 2); //x+ face, just so happens that convex hull generates the planes in this order
//simple into second triangle
bHit = Scaled.Raycast(FVec3(-2, 0, 0.5), FVec3(1, 0, 0), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(Position.X, -2 + Time);
EXPECT_EQ(Position.Y, 0);
EXPECT_EQ(Position.Z, 0.5);
EXPECT_EQ(FaceIndex, INDEX_NONE); //convex should not compute its own face index as this is too expensive
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 3); //x- face, just so happens that convex hull generates the planes in this order
bHit = Scaled.Raycast(FVec3(-0.001, 0, 2), FVec3(0, 0, -1), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, INDEX_NONE);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 3);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(-1, 0, 0), FaceIndex, 0.01), 2);
//again but far enough away from edge
bHit = Scaled.Raycast(FVec3(-0.1, 0, 2), FVec3(0, 0, -1), 3, 0, Time, Position, Normal, FaceIndex);
EXPECT_TRUE(bHit);
EXPECT_EQ(FaceIndex, INDEX_NONE);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(1, 0, 0), FaceIndex, 0.01), 3);
EXPECT_EQ(Scaled.FindMostOpposingFace(Position, FVec3(-1, 0, 0), FaceIndex, 0.01), 3); //too far to care about other face
}
}
// Unscaled test to accompany TestConvexBox_Scaled. See comments on that function
// This test has always passed whereas TestConvexBox_Scaled used to fail.
GTEST_TEST(FindMostOpposingFaceTests, TestConvexBox_Unsccaled)
{
const FVec3 BoxScale = FVec3(10, 5, 1);
const FVec3 BoxSize = FVec3(100, 100, 100);
const FReal Margin = 0;
FImplicitConvex3 Box = CreateConvexBox(BoxSize * BoxScale, Margin);
// Position is not important in this test as all faces are within TestDistance of TestPosition
const FVec3 TestPos = FVec3(0, 0, 0);
const FReal TestDistance = 10000.0f;
// Pointing mostly left to select the right-facing face. If we transform this normal into unscaled space and
// call FindMostOpposingFace on the core convex, it would incorrectly select the up-facing face.
const FVec3 TestDir = FVec3(-1.0, 0, -0.5).GetSafeNormal();
int32 FaceIndex = Box.FindMostOpposingFace(TestPos, TestDir, INDEX_NONE, TestDistance);
const FVec3 FaceNormal = Box.GetPlane(FaceIndex).Normal();
EXPECT_NEAR(FaceNormal.X, 1.0f, KINDA_SMALL_NUMBER);
EXPECT_NEAR(FaceNormal.Z, 0.0f, KINDA_SMALL_NUMBER);
}
// FindMostOpposingFace on Scaled Convex Box
// NOTE: Box faces normals are not affected by scale though the face positions are
// This tests for a specific bug: TImplicitObjectScaled::FindMostOpposingFace was calling
// onto FConvex::FindMostOpposingFace with a corrected normal, but this does not work - we
// actually need to perform the most-opposing test in scaled space.
GTEST_TEST(FindMostOpposingFaceTests, TestConvexBox_Scaled)
{
const FVec3 BoxScale = FVec3(10, 5, 1);
const FVec3 BoxSize = FVec3(100, 100, 100);
const FReal Margin = 0;
TImplicitObjectScaled<FImplicitConvex3> Box = CreateScaledConvexBox(BoxSize, BoxScale, Margin);
// Position is not important in this test as all faces are within TestDistance of TestPosition
const FVec3 TestPos = FVec3(0, 0, 0);
const FReal TestDistance = 10000.0f;
// Pointing mostly left to select the right-facing face. If we transform this normal into unscaled space and
// call FindMostOpposingFace on the core convex, it would incorrectly select the up-facing face.
const FVec3 TestDir = FVec3(-1.0, 0, -0.5).GetSafeNormal();
int32 FaceIndex = Box.FindMostOpposingFace(TestPos, TestDir, INDEX_NONE, TestDistance);
const FVec3 FaceNormal = Box.GetPlane(FaceIndex).Normal();
EXPECT_NEAR(FaceNormal.X, 1.0f, KINDA_SMALL_NUMBER);
EXPECT_NEAR(FaceNormal.Z, 0.0f, KINDA_SMALL_NUMBER);
}
}