Files
o3de/Gems/PhysX/Code/Tests/ShapeGeometryTests.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

231 lines
8.5 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "PhysXTestFixtures.h"
#include "EditorTestUtilities.h"
#include "PhysXTestCommon.h"
#include <AzFramework/Physics/SimulatedBodies/StaticRigidBody.h>
#include <Shape.h>
#include <Utils.h>
namespace PhysXEditorTests
{
bool NormalPointsAwayFromPosition(const AZ::Vector3& vertexA, const AZ::Vector3& vertexB, const AZ::Vector3& vertexC, const AZ::Vector3& position)
{
const AZ::Vector3 edge1 = (vertexB - vertexA).GetNormalized();
const AZ::Vector3 edge2 = (vertexC - vertexA).GetNormalized();
const AZ::Vector3 normal = edge1.Cross(edge2);
return normal.Dot(vertexA - position) >= 0.f;
}
bool TriangleWindingOrderIsValid(const AZStd::vector<AZ::Vector3>& vertices, const AZStd::vector<AZ::u32>& indices, const AZ::Vector3& insidePosition)
{
if (!vertices.empty() && !indices.empty())
{
// use indices to construct triangles
for (auto i = 0; i < indices.size(); i+=3)
{
const AZ::Vector3 vertexA = vertices[indices[i + 0]];
const AZ::Vector3 vertexB = vertices[indices[i + 1]];
const AZ::Vector3 vertexC = vertices[indices[i + 2]];
if (!NormalPointsAwayFromPosition(vertexA, vertexB, vertexC, insidePosition))
{
return false;
}
}
}
else if (!vertices.empty())
{
// assume a triangle list order
for (auto i = 0; i < vertices.size(); i+=3)
{
const AZ::Vector3 vertexA = vertices[i + 0];
const AZ::Vector3 vertexB = vertices[i + 1];
const AZ::Vector3 vertexC = vertices[i + 2];
if (!NormalPointsAwayFromPosition(vertexA, vertexB, vertexC, insidePosition))
{
return false;
}
}
}
return true;
}
TEST_F(PhysXEditorFixture, BoxShape_Geometry_IsValid_FT)
{
AZStd::vector<AZ::Vector3> vertices;
AZStd::vector<AZ::u32> indices;
const AZ::Vector3 boxDimensions(1.f,1.f,1.f);
// Given there is a box shape
auto physicsSystem = AZ::Interface<Physics::System>::Get();
AZ_Assert(physicsSystem, "Physics System required");
auto shape = physicsSystem->CreateShape(Physics::ColliderConfiguration(),
Physics::BoxShapeConfiguration(boxDimensions));
// When geometry is requested
shape->GetGeometry(vertices, indices);
// Then valid geometry is returned.
// valid number of vertices and indices
EXPECT_TRUE(vertices.size() == 8);
// 6 sides, 2 triangles per side, 3 indices per triangle
EXPECT_TRUE(indices.size() == 6 * 2 * 3);
// all vertices are inside dimensions AABB
AZ::Aabb bounds = AZ::Aabb::CreateNull();
for (auto vertex : vertices)
{
bounds.AddPoint(vertex);
}
EXPECT_NEAR(bounds.GetXExtent(), boxDimensions.GetX(), AZ::Constants::Tolerance);
EXPECT_NEAR(bounds.GetYExtent(), boxDimensions.GetY(), AZ::Constants::Tolerance);
EXPECT_NEAR(bounds.GetZExtent(), boxDimensions.GetZ(), AZ::Constants::Tolerance);
// valid winding order
const AZ::Vector3 insidePosition = AZ::Vector3::CreateZero();
EXPECT_TRUE(TriangleWindingOrderIsValid(vertices, indices, insidePosition));
}
TEST_F(PhysXEditorFixture, SphereShape_Geometry_IsValid_FT)
{
AZStd::vector<AZ::Vector3> vertices;
AZStd::vector<AZ::u32> indices;
// Given there is a sphere shape
constexpr float radius = 1.f;
auto shape = AZ::Interface<Physics::System>::Get()->CreateShape(Physics::ColliderConfiguration(),
Physics::SphereShapeConfiguration(radius));
// When geometry is requested
shape->GetGeometry(vertices, indices);
// Then valid geometry is returned.
EXPECT_FALSE(vertices.empty());
// valid radius from center
bool vertexRadiusIsValid = true;
for (auto vertex : vertices)
{
if (!AZ::IsClose(vertex.GetLength(), radius))
{
vertexRadiusIsValid = false;
break;
}
}
EXPECT_TRUE(vertexRadiusIsValid);
// valid winding order
const AZ::Vector3 insidePosition = AZ::Vector3::CreateZero();
EXPECT_TRUE(TriangleWindingOrderIsValid(vertices, indices, insidePosition));
}
TEST_F(PhysXEditorFixture, CapsuleShape_Geometry_IsValid_FT)
{
AZStd::vector<AZ::Vector3> vertices;
AZStd::vector<AZ::u32> indices;
// Given there is a shape
constexpr float height = 1.f;
constexpr float radius = .25f;
auto shape = AZ::Interface<Physics::System>::Get()->CreateShape(Physics::ColliderConfiguration(),
Physics::CapsuleShapeConfiguration(height, radius));
// When geometry is requested
shape->GetGeometry(vertices, indices);
// Then valid geometry is returned.
EXPECT_FALSE(vertices.empty());
// all vertices are inside dimensions AABB
AZ::Aabb bounds = AZ::Aabb::CreateNull();
for (auto vertex : vertices)
{
bounds.AddPoint(vertex);
}
constexpr float halfHeight = height * .5f;
const AZ::Vector3 min(-radius,-radius,-halfHeight);
const AZ::Vector3 max(radius,radius,halfHeight);
const AZ::Aabb expectedBounds = AZ::Aabb::CreateFromMinMax(min, max);
EXPECT_TRUE(expectedBounds.Contains(bounds));
// valid winding order
const AZ::Vector3 insidePosition = AZ::Vector3::CreateZero();
EXPECT_TRUE(TriangleWindingOrderIsValid(vertices, indices, insidePosition));
}
TEST_F(PhysXEditorFixture, ConvexHullShape_Geometry_IsValid_FT)
{
AZStd::vector<AZ::Vector3> vertices;
AZStd::vector<AZ::u32> indices;
auto physicsSystem = AZ::Interface<Physics::System>::Get();
// Given there is a shape
const PhysX::PointList testPoints = PhysX::TestUtils::GeneratePyramidPoints(1.0f);
AZStd::vector<AZ::u8> cookedData;
EXPECT_TRUE(physicsSystem->CookConvexMeshToMemory(testPoints.data(),
static_cast<AZ::u32>(testPoints.size()), cookedData));
Physics::CookedMeshShapeConfiguration shapeConfig;
shapeConfig.SetCookedMeshData(cookedData.data(), cookedData.size(),
Physics::CookedMeshShapeConfiguration::MeshType::Convex);
auto shape = physicsSystem->CreateShape(Physics::ColliderConfiguration(), shapeConfig);
// When geometry is requested
shape->GetGeometry(vertices, indices);
// Then valid geometry is returned.
EXPECT_FALSE(vertices.empty());
// valid winding order
const AZ::Vector3 insidePosition = AZ::Vector3::CreateZero();
EXPECT_TRUE(TriangleWindingOrderIsValid(vertices, indices, insidePosition));
}
TEST_F(PhysXEditorFixture, TriangleMeshShape_Geometry_IsValid_FT)
{
AZStd::vector<AZ::Vector3> vertices;
AZStd::vector<AZ::u32> indices;
auto physicsSystem = AZ::Interface<Physics::System>::Get();
// Given there is a shape
PhysX::VertexIndexData cubeMeshData = PhysX::TestUtils::GenerateCubeMeshData(3.0f);
AZStd::vector<AZ::u8> cookedData;
physicsSystem->CookTriangleMeshToMemory(
cubeMeshData.first.data(), static_cast<AZ::u32>(cubeMeshData.first.size()),
cubeMeshData.second.data(), static_cast<AZ::u32>(cubeMeshData.second.size()),
cookedData);
// Setup shape & collider configurations
Physics::CookedMeshShapeConfiguration shapeConfig;
shapeConfig.SetCookedMeshData(cookedData.data(), cookedData.size(),
Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh);
// we need a valid triangle mesh shape
auto shape = physicsSystem->CreateShape(Physics::ColliderConfiguration(), shapeConfig);
// When geometry is requested
shape->GetGeometry(vertices, indices);
// Then valid geometry is returned.
EXPECT_FALSE(vertices.empty());
// valid winding order
const AZ::Vector3 insidePosition = AZ::Vector3::CreateZero();
EXPECT_TRUE(TriangleWindingOrderIsValid(vertices, indices, insidePosition));
}
} // namespace UnitTest