1f9b284de2
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
231 lines
8.5 KiB
C++
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
|