NormalTolerance
NormalTolerance
#Overview
name: NormalTolerance
The value of this variable can be defined or overridden in .ini config files. 1
.ini config file referencing this setting variable.
It is referenced in 47
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of NormalTolerance is to set a threshold for comparing normal vectors in various geometric calculations within Unreal Engine 5. It is primarily used in collision detection, mesh generation, and other 3D geometry-related operations.
Key points about NormalTolerance:
-
It’s used in multiple Unreal Engine subsystems, including the rendering system, physics engine, and CAD import tools.
-
The value is typically set as a small floating-point number, often around 1e-6 to 1e-3, depending on the specific use case and required precision.
-
It’s used in comparisons to determine if two normal vectors are considered equal or nearly parallel.
-
The variable is often used in conjunction with other tolerance variables like PositionTolerance or DistanceTolerance.
-
It’s particularly important in collision detection algorithms, where it helps determine if surfaces are facing the same direction or if edges are colinear.
-
The value may need to be adjusted based on the scale of the objects being compared, as seen in some implementations where it’s multiplied by object dimensions.
Best practices when using NormalTolerance:
-
Choose an appropriate value based on the scale of your objects and the required precision of your calculations.
-
Be consistent in its usage throughout related code to ensure uniform behavior.
-
Consider making it configurable or scale-dependent for more robust implementations.
-
Use it in conjunction with appropriate vector normalization to ensure consistent results.
-
Be aware that too small a value may lead to floating-point precision issues, while too large a value may cause incorrect results in geometric calculations.
Developers should be aware that changing NormalTolerance can have significant effects on collision detection, mesh generation, and other geometric operations. It’s important to thoroughly test any changes to this value to ensure it doesn’t introduce unexpected behavior in your game or application.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Plugins/Enterprise/DatasmithImporter/Config/BaseDatasmithImporter.ini:8, section: [FileProducerTessellationOptions]
- INI Section:
FileProducerTessellationOptions
- Raw value:
30.0
- Is Array:
False
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/CADKernelSurface/Private/CADKernelSurfaceExtension.cpp:37
Scope (from outer to inner):
file
function bool UCADKernelParametricSurfaceData::Tessellate
Source code excerpt:
CADLibrary::FImportParameters ImportParameters;
ImportParameters.SetModelCoordinateSystem((FDatasmithUtils::EModelCoordSystem) SceneParameters.ModelCoordSys);
ImportParameters.SetTesselationParameters(RetessellateOptions.ChordTolerance, RetessellateOptions.MaxEdgeLength, RetessellateOptions.NormalTolerance, (CADLibrary::EStitchingTechnique) RetessellateOptions.StitchingTechnique);
CADLibrary::FMeshParameters CadMeshParameters;
CadMeshParameters.bNeedSwapOrientation = MeshParameters.bNeedSwapOrientation;
CadMeshParameters.bIsSymmetric = MeshParameters.bIsSymmetric;
CadMeshParameters.SymmetricNormal = (FVector3f) MeshParameters.SymmetricNormal;
CadMeshParameters.SymmetricOrigin = (FVector3f) MeshParameters.SymmetricOrigin;
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/CADKernelSurface/Public/CADModelToCADKernelConverterBase.h:71
Scope (from outer to inner):
file
class class FCADModelToCADKernelConverterBase : public CADLibrary::ICADModelConverter
function virtual void SetImportParameters
Source code excerpt:
}
virtual void SetImportParameters(double ChordTolerance, double MaxEdgeLength, double NormalTolerance, CADLibrary::EStitchingTechnique StitchingTechnique) override
{
ImportParameters.SetTesselationParameters(ChordTolerance, MaxEdgeLength, NormalTolerance, StitchingTechnique);
}
virtual bool IsSessionValid() override
{
return true;
}
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/DatasmithCADTranslator/Private/DatasmithCADTranslator.cpp:177
Scope (from outer to inner):
file
function bool FDatasmithCADTranslator::LoadScene
Source code excerpt:
ImportParameters.SetTesselationParameters(CheckParameterValue(TesselationOptions.ChordTolerance, UE::DatasmithTessellation::MinTessellationChord, TEXT("Chord tolerance")),
FMath::IsNearlyZero(TesselationOptions.MaxEdgeLength) ? 0. : CheckParameterValue(TesselationOptions.MaxEdgeLength, UE::DatasmithTessellation::MinTessellationEdgeLength, TEXT("Max Edge Length")),
CheckParameterValue(TesselationOptions.NormalTolerance, UE::DatasmithTessellation::MinTessellationAngle, TEXT("Max Angle")),
(EStitchingTechnique)TesselationOptions.StitchingTechnique);
ImportParameters.SetModelCoordinateSystem(FDatasmithUtils::EModelCoordSystem::ZUp_RightHanded);
UE_LOG(LogCADTranslator, Display, TEXT(" - Import parameters:"));
UE_LOG(LogCADTranslator, Display, TEXT(" - ChordTolerance: %lf"), ImportParameters.GetChordTolerance());
UE_LOG(LogCADTranslator, Display, TEXT(" - MaxEdgeLength: %lf"), ImportParameters.GetMaxEdgeLength());
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/DatasmithOpenNurbsTranslator/Private/DatasmithOpenNurbsImportOptions.cpp:30
Scope (from outer to inner):
file
function bool UDatasmithOpenNurbsImportOptions::CanEditChange
Source code excerpt:
else if (PropertyFName == GET_MEMBER_NAME_CHECKED(FDatasmithOpenNurbsOptions, ChordTolerance)
|| PropertyFName == GET_MEMBER_NAME_CHECKED(FDatasmithOpenNurbsOptions, MaxEdgeLength)
|| PropertyFName == GET_MEMBER_NAME_CHECKED(FDatasmithOpenNurbsOptions, NormalTolerance)
|| PropertyFName == GET_MEMBER_NAME_CHECKED(FDatasmithOpenNurbsOptions, StitchingTechnique)
)
{
// Enable tessellation options only when using CAD library to tessellate
return Options.Geometry == EDatasmithOpenNurbsBrepTessellatedSource::UseUnrealNurbsTessellation;
}
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/DatasmithOpenNurbsTranslator/Private/DatasmithOpenNurbsTranslator.cpp:2979
Scope (from outer to inner):
file
function bool FOpenNurbsTranslatorImpl::TranslateBRep
Source code excerpt:
{
// Ref. visitBRep
CADModelConverter->SetImportParameters(OpenNurbsOptions.ChordTolerance, OpenNurbsOptions.MaxEdgeLength, OpenNurbsOptions.NormalTolerance, (CADLibrary::EStitchingTechnique)OpenNurbsOptions.StitchingTechnique);
CADModelConverter->InitializeProcess();
OpenNurbsBRepConverter->AddBRep(*Brep, Offset);
CADModelConverter->RepairTopology();
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/DatasmithPLMXMLTranslator/Private/DatasmithPlmXmlImporter.cpp:251
Scope (from outer to inner):
file
namespace PlmXml
class class FPlmXmlMeshLoaderWithDatasmithDispatcher
function FPlmXmlMeshLoaderWithDatasmithDispatcher
Source code excerpt:
// Setup of import parameters for DatasmithDispatcher copied from FDatasmithCADTranslator's setup
ImportParameters.SetTesselationParameters(TessellationOptions.ChordTolerance, TessellationOptions.MaxEdgeLength, TessellationOptions.NormalTolerance, (CADLibrary::EStitchingTechnique) TessellationOptions.StitchingTechnique);
DatasmithDispatcher = MakeUnique<DatasmithDispatcher::FDatasmithDispatcher>(ImportParameters, CacheDir, CADFileToUEFileMap, CADFileToUEGeomMap);
}
// Adds geom file to load and returns Id to use in InstantiateMesh later(after all is loaded)
int32 AddMeshToLoad(const FString& FullPath)
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/DatasmithPLMXMLTranslator/Private/DatasmithPlmXmlTranslator.cpp:86
Scope (from outer to inner):
file
function bool FDatasmithPlmXmlTranslator::LoadScene
Source code excerpt:
CommonTessellationOptions.ChordTolerance = CheckParameterValue(CommonTessellationOptions.ChordTolerance, UE::DatasmithTessellation::MinTessellationChord, TEXT("Chord tolerance"));
CommonTessellationOptions.MaxEdgeLength = FMath::IsNearlyZero(CommonTessellationOptions.MaxEdgeLength) ? 0. : CheckParameterValue(CommonTessellationOptions.MaxEdgeLength, UE::DatasmithTessellation::MinTessellationEdgeLength, TEXT("Max Edge Length"));
CommonTessellationOptions.NormalTolerance = CheckParameterValue(CommonTessellationOptions.NormalTolerance, UE::DatasmithTessellation::MinTessellationAngle, TEXT("Max Angle"));
UE_LOG(LogDatasmithXMLPLMTranslator, Display, TEXT(" - Import parameters:"));
UE_LOG(LogDatasmithXMLPLMTranslator, Display, TEXT(" - ChordTolerance: %f"), CommonTessellationOptions.ChordTolerance);
UE_LOG(LogDatasmithXMLPLMTranslator, Display, TEXT(" - MaxEdgeLength: %f"), CommonTessellationOptions.MaxEdgeLength);
UE_LOG(LogDatasmithXMLPLMTranslator, Display, TEXT(" - MaxNormalAngle: %f"), CommonTessellationOptions.NormalTolerance);
FString StitchingTechnique;
switch ((EStitchingTechnique)CommonTessellationOptions.StitchingTechnique)
{
case EStitchingTechnique::StitchingHeal:
StitchingTechnique = TEXT("Heal");
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/DatasmithWireTranslator/Private/DatasmithWireTranslator.cpp:480
Scope (from outer to inner):
file
namespace UE_DATASMITHWIRETRANSLATOR_NAMESPACE
function void FWireTranslatorImpl::SetTessellationOptions
Source code excerpt:
{
TessellationOptions = Options;
CADModelConverter->SetImportParameters(Options.ChordTolerance, Options.MaxEdgeLength, Options.NormalTolerance, (CADLibrary::EStitchingTechnique)Options.StitchingTechnique);
SceneFileHash = HashCombine(Options.GetHash(), GetSceneFileHash(SceneFullPath, SceneName));
}
bool FWireTranslatorImpl::Read()
{
// Initialize Alias.
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/DatasmithWireTranslator/Private/DatasmithWireTranslator.cpp:2436
Scope (from outer to inner):
file
namespace UE_DATASMITHWIRETRANSLATOR_NAMESPACE
function bool FDatasmithWireTranslator::LoadScene
Source code excerpt:
UE_LOG(LogDatasmithWireTranslator, Display, TEXT(" - ChordTolerance: %lf"), TessellationOptions.ChordTolerance);
UE_LOG(LogDatasmithWireTranslator, Display, TEXT(" - MaxEdgeLength: %lf"), TessellationOptions.MaxEdgeLength);
UE_LOG(LogDatasmithWireTranslator, Display, TEXT(" - MaxNormalAngle: %lf"), TessellationOptions.NormalTolerance);
FString StitchingTechnique;
switch (TessellationOptions.StitchingTechnique)
{
case EDatasmithCADStitchingTechnique::StitchingHeal:
StitchingTechnique = TEXT("Heal");
break;
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/ParametricSurface/Private/TechSoft/TechSoftParametricSurface.cpp:36
Scope (from outer to inner):
file
function bool UTechSoftParametricSurfaceData::Tessellate
Source code excerpt:
{
CADLibrary::FImportParameters ImportParameters((FDatasmithUtils::EModelCoordSystem)SceneParameters.ModelCoordSys);
ImportParameters.SetTesselationParameters(RetessellateOptions.ChordTolerance, RetessellateOptions.MaxEdgeLength, RetessellateOptions.NormalTolerance, (CADLibrary::EStitchingTechnique)RetessellateOptions.StitchingTechnique);
FMeshConversionContext Context(ImportParameters, MeshParameters);
CADLibrary::FTechSoftInterface& TechSoftInterface = CADLibrary::FTechSoftInterface::Get();
bSuccessfulTessellation = TechSoftInterface.InitializeKernel(*FPaths::EnginePluginsDir());
if (bSuccessfulTessellation)
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/ParametricSurface/Public/CADModelConverter.h:48
Scope (from outer to inner):
file
namespace CADLibrary
class class ICADModelConverter
Source code excerpt:
* @param ChordTolerance : SAG
* @param MaxEdgeLength : max length of element's edge
* @param NormalTolerance : Angle between two adjacent triangles
* @param StitchingTechnique : CAD topology correction technique
* @param bScaleUVMap : Scale the UV map to a world unit.
*/
virtual void SetImportParameters(double ChordTolerance, double MaxEdgeLength, double NormalTolerance, CADLibrary::EStitchingTechnique StitchingTechnique) = 0;
virtual bool IsSessionValid() = 0;
virtual void AddSurfaceDataForMesh(const TCHAR* InFilePath, const FMeshParameters& InMeshParameters, const FDatasmithTessellationOptions& InTessellationOptions, FDatasmithMeshElementPayload& OutMeshPayload) const = 0;
};
}
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/ParametricSurface/Public/CADModelToTechSoftConverterBase.h:37
Scope (from outer to inner):
file
class class FCADModelToTechSoftConverterBase : public CADLibrary::ICADModelConverter
function virtual void SetImportParameters
Source code excerpt:
virtual bool Tessellate(const CADLibrary::FMeshParameters& InMeshParameters, FMeshDescription& OutMeshDescription) override;
virtual void SetImportParameters(double ChordTolerance, double MaxEdgeLength, double NormalTolerance, CADLibrary::EStitchingTechnique StitchingTechnique) override
{
ImportParameters.SetTesselationParameters(ChordTolerance, MaxEdgeLength, NormalTolerance, StitchingTechnique);
}
virtual bool IsSessionValid() override
{
return true;
}
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/ParametricSurfaceExtension/Private/DataprepTessellationOperation.cpp:28
Scope (from outer to inner):
file
function void UDataprepTessellationOperation::PostLoad
Source code excerpt:
ChordTolerance = TessellationSettings_DEPRECATED.ChordTolerance;
MaxEdgeLength = TessellationSettings_DEPRECATED.MaxEdgeLength;
NormalTolerance = TessellationSettings_DEPRECATED.NormalTolerance;
// Mark TessellationSettings_DEPRECATED as non usable
TessellationSettings_DEPRECATED.ChordTolerance = -MAX_FLT;
}
Super::PostLoad();
}
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/ParametricSurfaceExtension/Private/DataprepTessellationOperation.h:26
Scope (from outer to inner):
file
class class UDataprepTessellationOperation : public UDataprepOperation
function UDataprepTessellationOperation
Source code excerpt:
: ChordTolerance(0.2f)
, MaxEdgeLength(0.0f)
, NormalTolerance(20.0f)
// Mark TessellationSettings_DEPRECATED as non usable
, TessellationSettings_DEPRECATED(-MAX_FLT)
{
}
// UObject interface
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithCADImporter/Source/ParametricSurfaceExtension/Private/DataprepTessellationOperation.h:61
Scope (from outer to inner):
file
class class UDataprepTessellationOperation : public UDataprepOperation
Source code excerpt:
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Tessellation Options", meta = (Units = deg, ToolTip = "Maximum angle between adjacent triangles. Smaller values make more triangles.", ClampMin = "0.0", ClampMax = "90.0"))
float NormalTolerance;
/** Version 4.24 : Deprecated - not serialized anymore */
UPROPERTY()
FDatasmithTessellationOptions TessellationSettings_DEPRECATED;
//~ Begin UDataprepOperation Interface
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/DatasmithImportOptions.h:226
Scope (from outer to inner):
file
function FDatasmithTessellationOptions
Source code excerpt:
: ChordTolerance(InChordTolerance)
, MaxEdgeLength(InMaxEdgeLength)
, NormalTolerance(InNormalTolerance)
, StitchingTechnique(InStitchingTechnique)
{
}
/**
* Maximum distance between any point on a triangle generated by the tessellation process and the actual surface.
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/DatasmithImportOptions.h:254
Scope: file
Source code excerpt:
*/
UPROPERTY(config, EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "Geometry & Tessellation Options", meta = (Units = deg, ToolTip = "Maximum angle between adjacent triangles. Smaller values make more triangles.", ClampMin = "5.0", ClampMax = "90.0"))
float NormalTolerance;
/**
* Stitching technique applied on neighbouring surfaces before tessellation.
* None : No stitching applied. This is the default.
* Sewing : Connects surfaces which physically share a boundary but not topologically within a set of objects.
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/DatasmithImportOptions.h:275
Scope (from outer to inner):
file
function bool operator ==
Source code excerpt:
return FMath::IsNearlyEqual(ChordTolerance, Other.ChordTolerance)
&& FMath::IsNearlyEqual(MaxEdgeLength, Other.MaxEdgeLength)
&& FMath::IsNearlyEqual(NormalTolerance, Other.NormalTolerance)
&& StitchingTechnique == Other.StitchingTechnique;
}
uint32 GetHash() const
{
uint32 Hash = uint32(StitchingTechnique);
for (float Param : {ChordTolerance, MaxEdgeLength, NormalTolerance})
{
Hash = HashCombine(Hash, GetTypeHash(Param));
}
return Hash;
}
};
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithContent/Source/DatasmithContent/Public/DatasmithImportOptions.h:309
Scope (from outer to inner):
file
function void operator =
Source code excerpt:
ChordTolerance = Other.ChordTolerance;
MaxEdgeLength = Other.MaxEdgeLength;
NormalTolerance = Other.NormalTolerance;
StitchingTechnique = Other.StitchingTechnique;
}
};
/**
* Base class for all import options in datasmith.
#Loc: <Workspace>/Engine/Plugins/Enterprise/DatasmithImporter/Source/DatasmithImporter/Private/DatasmithFileProducer.cpp:1929
Scope (from outer to inner):
file
function void UDatasmithFileProducer::LoadDefaultSettings
Source code excerpt:
GConfig->GetFloat( TessellationSectionName, TEXT("ChordTolerance"), DefaultTessellationOptions.ChordTolerance, DatasmithImporterIni);
GConfig->GetFloat( TessellationSectionName, TEXT("MaxEdgeLength"), DefaultTessellationOptions.MaxEdgeLength, DatasmithImporterIni);
GConfig->GetFloat( TessellationSectionName, TEXT("NormalTolerance"), DefaultTessellationOptions.NormalTolerance, DatasmithImporterIni);
FString StitchingTechnique = GConfig->GetStr( TessellationSectionName, TEXT("StitchingTechnique"), DatasmithImporterIni);
if(StitchingTechnique == TEXT("StitchingHeal"))
{
DefaultTessellationOptions.StitchingTechnique = EDatasmithCADStitchingTechnique::StitchingHeal;
}
#Loc: <Workspace>/Engine/Plugins/Runtime/MeshModelingToolset/Source/ModelingComponentsEditorOnly/Private/ConversionUtils/DynamicMeshToVolume.cpp:866
Scope (from outer to inner):
file
namespace UE
namespace Conversion
function void GetPolygonFaces
Source code excerpt:
Normals.SetDegenerateTriangleNormalsToNeighborNormal(); // See comment above
double NormalTolerance = FMathf::ZeroTolerance;
auto TrianglesShouldBeInSameComponent = [&InputMesh, &Normals, NormalTolerance, bRespectGroupBoundaries](int32 Triangle0, int32 Triangle1)
{
return (!bRespectGroupBoundaries || InputMesh.GetTriangleGroup(Triangle0) == InputMesh.GetTriangleGroup(Triangle1))
// This test is only performed if triangles share an edge, so checking the normal is
// sufficient for coplanarity.
&& Normals[Triangle0].Dot(Normals[Triangle1]) >= 1 - NormalTolerance;
};
FMeshConnectedComponents Components(&InputMesh);
Components.FindConnectedTriangles(TrianglesShouldBeInSameComponent);
// Used for removing colinear verts in the loop ahead
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/UnrealEdSrv.cpp:2041
Scope (from outer to inner):
file
function bool UUnrealEdEngine::Exec_Actor
Source code excerpt:
// The rejection tolerance. When figuring out which planes to cut the blocking volume cube with
// the code will reject any planes that are less than "NormalTolerance" different in their normals.
//
// This cuts down on the number of planes that will be used for generating the cutting planes and,
// as a side effect, eliminates duplicates.
float NormalTolerance = 0.25f;
FParse::Value( Str, TEXT("NORMALTOLERANCE="), NormalTolerance );
FVector3f NormalLimits( 1.0f, 1.0f, 1.0f );
FParse::Value( Str, TEXT("NLIMITX="), NormalLimits.X );
FParse::Value( Str, TEXT("NLIMITY="), NormalLimits.Y );
FParse::Value( Str, TEXT("NLIMITZ="), NormalLimits.Z );
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/UnrealEdSrv.cpp:2111
Scope (from outer to inner):
file
function bool UUnrealEdEngine::Exec_Actor
Source code excerpt:
FPlane* plane = SplitterPlanes[x];
if( plane->GetSafeNormal().Equals( SplittingPlane->GetSafeNormal(), NormalTolerance ) )
{
bAddPlaneToList = false;
break;
}
}
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestCapsuleTriangleCollision.cpp:153
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
{
const FReal PositionTolerance = UE_KINDA_SMALL_NUMBER;
const FReal NormalTolerance = UE_KINDA_SMALL_NUMBER;
const FReal CullDistance = FReal(3);
const FReal Sin10 = FMath::Sin(FMath::DegreesToRadians(10));
const FReal Cos10 = FMath::Cos(FMath::DegreesToRadians(10));
const FTriangle Triangle(FVec3(0, 0, 0), FVec3(100, -100, 0), FVec3(100, 0, 0));
const FImplicitCapsule3 Capsule(FVec3(50, -10, 10 + 50 * Sin10), FVec3(150, -10, 10 - 50 * Sin10), FReal(20));
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestCapsuleTriangleCollision.cpp:172
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
// The normal should be vertical because we are within the face angle threshold
EXPECT_NEAR(ContactPoints[1].ShapeContactNormal.Z, FReal(1), NormalTolerance);
// Make sure both points are on the capsule surface
EXPECT_TRUE(IsPointOnCapsuleSurface(Capsule, ContactPoints[0].ShapeContactPoints[0], PositionTolerance));
EXPECT_TRUE(IsPointOnCapsuleSurface(Capsule, ContactPoints[1].ShapeContactPoints[0], PositionTolerance));
}
}
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestCapsuleTriangleCollision.cpp:191
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
{
const FReal PositionTolerance = UE_KINDA_SMALL_NUMBER;
const FReal NormalTolerance = UE_KINDA_SMALL_NUMBER;
const FReal CullDistance = FReal(3);
FTriangle Triangle(FVec3(0, 0, 0), FVec3(100, -100, 0), FVec3(100, 0, 0));
FImplicitCapsule3 Capsule(FVec3(50, -10, 50), FVec3(150, -10, -50), FReal(10));
FContactPointManifold ContactPoints;
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestCapsuleTriangleCollision.cpp:204
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
{
EXPECT_NEAR(ContactPoints[0].Phi, -Capsule.GetRadius(), PositionTolerance);
EXPECT_NEAR(FVec3::DotProduct(ContactPoints[0].ShapeContactNormal, FVec3(0,0,1)), FMath::Cos(FMath::DegreesToRadians(45)), NormalTolerance);
}
}
// Capsule at a 45 degree angle to the triangle, and the capsule axis passes right through an edge.
// This time we have an end cap within the edge planes, so we should get a face contact at that location.
// The edge contact will be ignored because it would generate an inward-facing normal, and we are
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestCapsuleTriangleCollision.cpp:222
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
{
const FReal PositionTolerance = UE_KINDA_SMALL_NUMBER;
const FReal NormalTolerance = UE_KINDA_SMALL_NUMBER;
const FReal CullDistance = FReal(3);
FTriangle Triangle(FVec3(0, 0, 0), FVec3(100, -100, 0), FVec3(100, 0, 0));
FImplicitCapsule3 Capsule(FVec3(50, -10, -50), FVec3(150, -10, 50), FReal(10));
FContactPointManifold ContactPoints;
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestCapsuleTriangleCollision.cpp:238
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
EXPECT_NEAR(ContactPoints[0].ShapeContactPoints[0].Z, Capsule.GetX1().Z - Capsule.GetRadius(), PositionTolerance);
EXPECT_NEAR(ContactPoints[0].ShapeContactNormal.Z, FReal(1), NormalTolerance);
}
}
}
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestConvex.cpp:449
Scope (from outer to inner):
file
namespace ChaosTest
function void TestConvexPlaneVertices
Source code excerpt:
void TestConvexPlaneVertices(const ConvexType& Convex)
{
const FReal NormalTolerance = UE_SMALL_NUMBER;
const FReal PositionTolerance = UE_KINDA_SMALL_NUMBER;
for (int32 PlaneIndex = 0; PlaneIndex < Convex.NumPlanes(); ++PlaneIndex)
{
const FVec3 PlaneN = Convex.GetPlane(PlaneIndex).Normal();
const FVec3 PlaneX = Convex.GetPlane(PlaneIndex).X();
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestConvex.cpp:568
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
};
const FReal NormalTolerance = UE_SMALL_NUMBER;
const FReal PositionTolerance = UE_KINDA_SMALL_NUMBER;
const FVec3 Center = FVec3(0, 0, 0);
const FVec3 HalfExtent = FVec3(100, 200, 300);
const FReal Margin = FReal(0);
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestConvex.cpp:597
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
// Normals are in the expected direction
EXPECT_NEAR(Plane.Normal().X, PlaneNormals[PlaneIndex].X, NormalTolerance) << "PlaneIndex=" << PlaneIndex;
EXPECT_NEAR(Plane.Normal().Y, PlaneNormals[PlaneIndex].Y, NormalTolerance) << "PlaneIndex=" << PlaneIndex;
EXPECT_NEAR(Plane.Normal().Z, PlaneNormals[PlaneIndex].Z, NormalTolerance) << "PlaneIndex=" << PlaneIndex;
// Positions are in the correct plane
const FReal PlaneDistance = FVec3::DotProduct(Plane.Normal(), Plane.X());
const FReal ExpectedPlaneDistance = FVec3::DotProduct(PlaneNormals[PlaneIndex], PlaneNormals[PlaneIndex] * HalfExtent);
EXPECT_NEAR(PlaneDistance, ExpectedPlaneDistance, PositionTolerance);
}
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestConvex.cpp:705
Scope (from outer to inner):
file
namespace ChaosTest
function GTEST_TEST
Source code excerpt:
// All planes normals should be...normalized
const FReal NormalTolerance = 1.e-4;
for (int32 PlaneIndex = 0; PlaneIndex < Convex.NumPlanes(); ++PlaneIndex)
{
const FVec3 PlaneN = Convex.GetPlane(PlaneIndex).Normal();
EXPECT_NEAR(PlaneN.Size(), FReal(1), NormalTolerance);
}
}
// Create a tet with an extra degenerate triangle in it. Verify that MergeColinearEdges handles this case
// and does not leave an invalid 2-vertex face behind.
#Loc: <Workspace>/Engine/Source/Programs/HeadlessChaos/Private/HeadlessChaosTestDetectCollision.cpp:19
Scope (from outer to inner):
file
namespace ChaosTest
function void CheckContactPoint
Source code excerpt:
{
const FReal DistanceTolerance = 1.e-3f;
const FReal NormalTolerance = 1.e-3f;
EXPECT_NEAR(ContactPoint.ShapeContactPoints[0].X, ShapeContactPos0.X, DistanceTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactPoints[0].Y, ShapeContactPos0.Y, DistanceTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactPoints[0].Z, ShapeContactPos0.Z, DistanceTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactPoints[1].X, ShapeContactPos1.X, DistanceTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactPoints[1].Y, ShapeContactPos1.Y, DistanceTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactPoints[1].Z, ShapeContactPos1.Z, DistanceTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactNormal.X, ShapeContactNormal.X, NormalTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactNormal.Y, ShapeContactNormal.Y, NormalTolerance);
EXPECT_NEAR(ContactPoint.ShapeContactNormal.Z, ShapeContactNormal.Z, NormalTolerance);
EXPECT_NEAR(ContactPoint.Phi, Phi, DistanceTolerance);
}
//
//
// SPHERE - CONVEX TESTS
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Particles/Location/ParticleModuleLocationSkelVertSurface.h:121
Scope (from outer to inner):
file
class class UParticleModuleLocationSkelVertSurface : public UParticleModuleLocationBase
Source code excerpt:
TArray<FName> ValidAssociatedBones;
/** When true use the RestrictToNormal and NormalTolerance values to check surface normals */
UPROPERTY(EditAnywhere, Category=VertSurface)
uint32 bEnforceNormalCheck:1;
/** Use this normal to restrict spawning locations */
UPROPERTY(EditAnywhere, Category=VertSurface)
FVector NormalToCompare;
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/CapsuleTriangleContactPoint.cpp:69
Scope (from outer to inner):
file
namespace Chaos
function void ConstructCapsuleTriangleOneShotManifold2
Source code excerpt:
// need to cope with very larger or small segments and triangles). This will probably not be good enough...(add some tests)
const FReal DistanceTolerance = FReal(1.e-5) * Capsule.GetHeight();
const FReal NormalTolerance = FReal(1.e-5);
const FReal NormalToleranceSq = NormalTolerance * NormalTolerance;
const FReal FaceContactSinAngleThreshold = FReal(0.34); // ~Sin(20deg)
// Face plane
const FVec3& FaceP = Triangle.GetVertex(0); // Use centroid?
FVec3 FaceN = FVec3::CrossProduct(Triangle.GetVertex(1) - Triangle.GetVertex(0), Triangle.GetVertex(2) - Triangle.GetVertex(0));
if (!FaceN.Normalize(NormalToleranceSq))
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/CapsuleTriangleContactPoint.cpp:180
Scope (from outer to inner):
file
namespace Chaos
function void ConstructCapsuleTriangleOneShotManifold2
Source code excerpt:
// Treat CullDistance as zero when colliding with the underneath of a triangle
FReal SeparationAxisCullDistance = RejectDistance;
if (DotFace < -NormalTolerance)
{
SeparationAxisCullDistance = R;
}
if (SegmentEdgeDistSq > FMath::Square(SeparationAxisCullDistance))
{
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/CapsuleTriangleContactPoint.cpp:280
Scope (from outer to inner):
file
namespace Chaos
function void ConstructCapsuleTriangleOneShotManifold2
Source code excerpt:
{
FVec3 RadialAxis = FVec3::CrossProduct(FVec3::CrossProduct(Axis, FaceN), Axis);
if (RadialAxis.Normalize(NormalTolerance))
{
// We want Radial axis to point against the normal
if (FVec3::DotProduct(RadialAxis, FaceN) > FReal(0))
{
RadialAxis = -RadialAxis;
}
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/CapsuleTriangleContactPoint.cpp:395
Scope (from outer to inner):
file
namespace Chaos
function void ConstructCapsuleTriangleOneShotManifold2
Source code excerpt:
// @todo(chaos): size dependence issue?
const FReal DotCentroid = FVec3::DotProduct(EdgeP - Centroid, SegmentEdgeN);
if (DotCentroid < -NormalTolerance)
{
continue;
}
// For Vertex contacts, the normal needs to be outside both planes
if ((EdgeT == FReal(0)) && ((SegmentT == FReal(0)) || (SegmentT == FReal(1))))
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/CapsuleTriangleContactPoint.cpp:405
Scope (from outer to inner):
file
namespace Chaos
function void ConstructCapsuleTriangleOneShotManifold2
Source code excerpt:
const int32 PrevEdgeIndex = (EdgeIndex > 0) ? EdgeIndex - 1 : 2;
const FReal PrevDotEdge = FVec3::DotProduct(EdgeNs[PrevEdgeIndex], SegmentEdgeN);
if (PrevDotEdge < -NormalTolerance)
{
continue;
}
}
if ((EdgeT == FReal(1)) && ((SegmentT == FReal(0)) || (SegmentT == FReal(1))))
{
const int32 NextEdgeIndex = (EdgeIndex < 2) ? EdgeIndex + 1 : 0;
const FReal NextDotEdge = FVec3::DotProduct(EdgeNs[NextEdgeIndex], SegmentEdgeN);
if (NextDotEdge < -NormalTolerance)
{
continue;
}
}
}
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/ConvexTriangleContactPoint.cpp:142
Scope (from outer to inner):
file
namespace Chaos
function void ConstructConvexTriangleOneShotManifold2
Source code excerpt:
void ConstructConvexTriangleOneShotManifold2(const ConvexType& Convex, const FTriangle& Triangle, const FReal CullDistance, FContactPointManifold& OutContactPoints)
{
const FReal NormalTolerance = FReal(1.e-8);
const FReal NormalToleranceSq = NormalTolerance * NormalTolerance;
const FReal InvalidPhi = std::numeric_limits<FReal>::lowest();
// Triangle (same space as convex)
const FVec3 TriN = Triangle.GetNormal();
const FVec3 TriC = Triangle.GetCentroid();
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/ConvexTriangleContactPoint.cpp:853
Scope (from outer to inner):
file
namespace Chaos::Private
function bool GetConvexFeature
Source code excerpt:
bool GetConvexFeature(const ConvexType& Convex, const FVec3& Position, const FVec3& Normal, Private::FConvexFeature& OutFeature)
{
const FReal NormalTolerance = FReal(1.e-6);
const FReal PositionTolerance = FReal(1.e-4);
const FReal ToleranceSizeMultiplier = Convex.BoundingBox().Extents().GetAbsMax();
const FReal EdgeNormalTolerance = ToleranceSizeMultiplier * FReal(1.e-3);
int32 BestPlaneIndex = INDEX_NONE;
FReal BestPlaneDotNormal = FReal(-1);
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/ConvexTriangleContactPoint.cpp:876
Scope (from outer to inner):
file
namespace Chaos::Private
function bool GetConvexFeature
Source code excerpt:
Convex.GetPlaneNX(PlaneIndex, PlaneN, PlaneX);
const FReal PlaneDotNormal = FVec3::DotProduct(PlaneN, Normal);
if (FMath::IsNearlyEqual(PlaneDotNormal, FReal(1), NormalTolerance))
{
OutFeature.FeatureType = Private::EConvexFeatureType::Plane;
OutFeature.PlaneIndex = PlaneIndex;
OutFeature.PlaneFeatureIndex = 0;
return true;
}
#Loc: <Workspace>/Engine/Source/Runtime/Experimental/Chaos/Private/Chaos/Collision/PBDCollisionConstraint.cpp:595
Scope (from outer to inner):
file
namespace Chaos
function bool FPBDCollisionConstraint::AreMatchingContactPoints
Source code excerpt:
return false;
}
const FReal NormalTolerance = Chaos_Manifold_MatchNormalTolerance;
// If normal has changed a lot, it is a different contact
// (This was only here to detect bad normals - it is not right for edge-edge contact tracking, but we don't do a good job of that yet anyway!)
FReal NormalDot = FVec3::DotProduct(A.ShapeContactNormal, B.ShapeContactNormal);
if (NormalDot < 1.0f - NormalTolerance)
{
return false;
}
// If either point in local space is the same, it is the same contact
if (DistanceTolerance > 0.0f)
#Loc: <Workspace>/Engine/Source/Runtime/PhysicsCore/Public/SQVerifier.h:97
Scope (from outer to inner):
file
function bool SQComparisonHelper
Source code excerpt:
bool bTestPassed = true;
const float DistanceTolerance = 1e-1f;
const float NormalTolerance = 1e-2f;
FPendingSpatialDataQueue Empty;
const FSQCapture& CapturedSQ = *Serializer.GetSQCapture();
switch (CapturedSQ.SQType)
{
case FSQCapture::ESQType::Raycast:
#Loc: <Workspace>/Engine/Source/Runtime/PhysicsCore/Public/SQVerifier.h:145
Scope (from outer to inner):
file
function bool SQComparisonHelper
Source code excerpt:
bTestPassed &= SQ_REPLAY_TEST(FMath::IsNearlyEqual(ChaosHitBuffer->GetBlock()->WorldPosition.Z, CapturedSQ.PhysXRaycastBuffer.block.position.z, DistanceTolerance));
bTestPassed &= SQ_REPLAY_TEST(FMath::IsNearlyEqual(ChaosHitBuffer->GetBlock()->WorldNormal.X, CapturedSQ.PhysXRaycastBuffer.block.normal.x, NormalTolerance));
bTestPassed &= SQ_REPLAY_TEST(FMath::IsNearlyEqual(ChaosHitBuffer->GetBlock()->WorldNormal.Y, CapturedSQ.PhysXRaycastBuffer.block.normal.y, NormalTolerance));
bTestPassed &= SQ_REPLAY_TEST(FMath::IsNearlyEqual(ChaosHitBuffer->GetBlock()->WorldNormal.Z, CapturedSQ.PhysXRaycastBuffer.block.normal.z, NormalTolerance));
}
break;
}
case FSQCapture::ESQType::Sweep:
{
//For sweep there are many solutions (many contacts possible) so we only bother testing for Distance
#Loc: <Workspace>/Engine/Source/Runtime/PhysicsCore/Public/SQVerifier.h:216
Scope (from outer to inner):
file
function bool SQValidityHelper
Source code excerpt:
bool bTestPassed = true;
const float DistanceTolerance = 1e-1f;
const float NormalTolerance = 1e-2f;
FPendingSpatialDataQueue Empty;
const FSQCapture& CapturedSQ = *Serializer.GetSQCapture();
switch (CapturedSQ.SQType)
{
case FSQCapture::ESQType::Raycast: