Files
o3de/Gems/PhysX/Code/Tests/PhysXWindTest.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

383 lines
14 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 <AzCore/Component/TickBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/UnitTest/Helpers.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzFramework/Physics/WindBus.h>
#include <AzFramework/Physics/World.h>
#include <AzTest/AzTest.h>
#include <LmbrCentral/Scripting/TagComponentBus.h>
#include <BoxColliderComponent.h>
#include <ForceRegionComponent.h>
#include "PhysXTestFixtures.h"
namespace PhysX
{
class PhysXWindTest
: public PhysXDefaultWorldTest
{
public:
AZ::Vector3 GetGlobalWind() const
{
return AZ::Interface<Physics::WindRequests>::Get()->GetGlobalWind();
}
AZ::Vector3 GetWind(const AZ::Vector3& position) const
{
return AZ::Interface<Physics::WindRequests>::Get()->GetWind(position);
}
AZ::Vector3 GetWind(const AZ::Aabb& aabb) const
{
return AZ::Interface<Physics::WindRequests>::Get()->GetWind(aabb);
}
};
class MockWindNotificationsBusHandler
: public Physics::WindNotificationsBus::Handler
{
public:
MockWindNotificationsBusHandler()
{
Physics::WindNotificationsBus::Handler::BusConnect();
}
~MockWindNotificationsBusHandler()
{
Physics::WindNotificationsBus::Handler::BusDisconnect();
}
MOCK_METHOD1(OnWindChanged, void(const AZ::Aabb&));
};
enum class WindType : bool
{
Local,
Global
};
AZStd::unique_ptr<AZ::Entity> AddWindRegion(
const AZ::Aabb& aabb, const AZ::Vector3& windDirection, float windMagnitude, WindType windType)
{
auto forceRegionEntity = AZStd::make_unique<AZ::Entity>("WindForceRegion");
AZ::TransformConfig transformConfig;
transformConfig.m_worldTransform = AZ::Transform::CreateTranslation(aabb.GetCenter());
forceRegionEntity->CreateComponent<AzFramework::TransformComponent>()->SetConfiguration(transformConfig);
auto colliderConfiguration = AZStd::make_shared<Physics::ColliderConfiguration>();
colliderConfiguration->m_isTrigger = true;
auto shapeConfiguration = AZStd::make_shared<BoxColliderComponent::Configuration>();
shapeConfiguration->m_dimensions = AZ::Vector3(aabb.GetWidth(), aabb.GetHeight(), aabb.GetDepth());
auto colliderComponent = forceRegionEntity->CreateComponent<BoxColliderComponent>();
colliderComponent->SetShapeConfigurationList({ AZStd::make_pair(colliderConfiguration, shapeConfiguration) });
forceRegionEntity->CreateComponent<ForceRegionComponent>();
forceRegionEntity->CreateComponent("{0F16A377-EAA0-47D2-8472-9EAAA680B169}"); //TagComponent
forceRegionEntity->Init();
forceRegionEntity->Activate();
PhysX::ForceRegionRequestBus::Event(forceRegionEntity->GetId()
, &PhysX::ForceRegionRequests::AddForceWorldSpace
, windDirection
, windMagnitude);
AZ::Crc32 windTag;
switch (windType)
{
case WindType::Global:
{
windTag = AZ::Crc32("global_wind");
break;
}
case WindType::Local:
{
windTag = AZ::Crc32("wind");
break;
}
};
LmbrCentral::TagComponentRequestBus::Event(forceRegionEntity->GetId()
, &LmbrCentral::TagComponentRequests::AddTag
, windTag);
return forceRegionEntity;
}
AZStd::unique_ptr<AZ::Entity> AddWindRegion(
const AZ::Vector3& position, const AZ::Vector3& windDirection, float windMagnitude, WindType windType)
{
const AZ::Aabb aabb = AZ::Aabb::CreateCenterRadius(position, 0.5f);
return AddWindRegion(aabb, windDirection, windMagnitude, windType);
}
AZ::Vector3 GetWindValue(const AZ::Vector3& windDirection, float windMagnitude)
{
if (!windDirection.IsZero())
{
return windDirection.GetNormalized() * windMagnitude;
}
return AZ::Vector3::CreateZero();
}
TEST_F(PhysXWindTest, GlobalWind_SingleForceRegion_WindBusReturnsCorrectValue)
{
const AZ::Vector3 position = AZ::Vector3::CreateZero();
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 1.0f;
auto forceRegionEntity = AddWindRegion(position, windDirection, windMagnitude, WindType::Global);
const AZ::Vector3 globalWindValue = GetGlobalWind();
const AZ::Vector3 expectedWindValue = GetWindValue(windDirection, windMagnitude);
EXPECT_THAT(globalWindValue, UnitTest::IsClose(expectedWindValue));
}
TEST_F(PhysXWindTest, GlobalWind_TwoForceRegions_WindBusReturnsCorrectValue)
{
// First entity
const AZ::Vector3 positionA = AZ::Vector3(100.0f, 0.0f, 0.0f);
const AZ::Vector3 windDirectionA = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitudeA = 2.0f;
auto forceRegionEntityA = AddWindRegion(positionA, windDirectionA, windMagnitudeA, WindType::Global);
// Seconds entity
const AZ::Vector3 positionB = AZ::Vector3(0.0f, 100.0f, 0.0f);
const AZ::Vector3 windDirectionB = AZ::Vector3(0.0f, 1.0f, 0.0f);
const float windMagnitudeB = 3.0f;
auto forceRegionEntityB = AddWindRegion(positionB, windDirectionB, windMagnitudeB, WindType::Global);
const AZ::Vector3 globalWindValue = GetGlobalWind();
// We expect result to be combined value from both entities
const AZ::Vector3 expectedWindValue =
GetWindValue(windDirectionA, windMagnitudeA) + GetWindValue(windDirectionB, windMagnitudeB);
EXPECT_THAT(globalWindValue, UnitTest::IsClose(expectedWindValue));
}
TEST_F(PhysXWindTest, GlobalWind_SingleForceRegionDeactivated_WindBusReturnsZero)
{
const AZ::Vector3 position = AZ::Vector3::CreateZero();
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 1.0f;
auto forceRegionEntity = AddWindRegion(position, windDirection, windMagnitude, WindType::Global);
// Deactivate entity, it is not expected to contribute to wind values anymore
forceRegionEntity->Deactivate();
const AZ::Vector3 globalWindValue = GetGlobalWind();
EXPECT_TRUE(globalWindValue.IsZero());
}
TEST_F(PhysXWindTest, LocalWind_SingleForceRegion_WindBusReturnsCorrectValueAtPosition)
{
const AZ::Vector3 position = AZ::Vector3::CreateZero();
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 3.0f;
auto forceRegionEntity = AddWindRegion(position, windDirection, windMagnitude, WindType::Local);
const AZ::Vector3 localWindValue = GetWind(position);
const AZ::Vector3 expectedWindValue = GetWindValue(windDirection, windMagnitude);
EXPECT_THAT(localWindValue, UnitTest::IsClose(expectedWindValue));
}
TEST_F(PhysXWindTest, LocalWind_SingleForceRegion_WindBusReturnsCorrectValueForPositionInsideWindVolume)
{
const AZ::Aabb aabb = AZ::Aabb::CreateCenterRadius(
AZ::Vector3::CreateZero(),
10.0f
);
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 10.0f;
auto forceRegionEntity = AddWindRegion(aabb, windDirection, windMagnitude, WindType::Local);
const AZ::Vector3 position = AZ::Vector3(5.0f, 5.0f, 0.0f);
const AZ::Vector3 localWindValue = GetWind(position);
const AZ::Vector3 expectedWindValue = GetWindValue(windDirection, windMagnitude);
EXPECT_THAT(localWindValue, UnitTest::IsClose(expectedWindValue));
}
TEST_F(PhysXWindTest, LocalWind_SingleForceRegion_WindBusReturnsZeroForPositionOutsideWindVolume)
{
const AZ::Aabb aabb = AZ::Aabb::CreateCenterRadius(
AZ::Vector3::CreateZero(),
10.0f
);
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 10.0f;
auto forceRegionEntity = AddWindRegion(aabb, windDirection, windMagnitude, WindType::Local);
// Using position outside of wind entity bounding box
const AZ::Vector3 position = AZ::Vector3(100.0f, 0.0f, 0.0f);
const AZ::Vector3 localWindValue = GetWind(position);
EXPECT_TRUE(localWindValue.IsZero());
}
TEST_F(PhysXWindTest, LocalWind_SingleForceRegion_WindBusReturnsCorrectValueForAabbOverlappingWindVolume)
{
const AZ::Aabb aabb = AZ::Aabb::CreateCenterRadius(
AZ::Vector3::CreateZero(),
10.0f
);
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 10.0f;
auto forceRegionEntity = AddWindRegion(aabb, windDirection, windMagnitude, WindType::Local);
// Using bounding box that overlaps with wind one
const AZ::Aabb testAabb = AZ::Aabb::CreateCenterRadius(
AZ::Vector3(1.0f, 2.0f, 3.0f),
15.0f
);
const AZ::Vector3 localWindValue = GetWind(testAabb);
const AZ::Vector3 expectedWindValue = GetWindValue(windDirection, windMagnitude);
EXPECT_THAT(localWindValue, UnitTest::IsClose(expectedWindValue));
}
TEST_F(PhysXWindTest, LocalWind_SingleForceRegion_WindBusReturnsZeroForAabbNotOverlappingWindVolume)
{
const AZ::Aabb aabb = AZ::Aabb::CreateCenterRadius(
AZ::Vector3::CreateZero(),
10.0f
);
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 10.0f;
auto forceRegionEntity = AddWindRegion(aabb, windDirection, windMagnitude, WindType::Local);
const AZ::Aabb testAabb = AZ::Aabb::CreateCenterRadius(
AZ::Vector3(100.0f, 200.0f, 300.0f),
15.0f
);
const AZ::Vector3 localWindValue = GetWind(testAabb);
EXPECT_TRUE(localWindValue.IsZero());
}
TEST_F(PhysXWindTest, LocalWind_TwoForceRegions_WindBusReturnsCombinedValueForPositionInsideBothWindVolumes)
{
// First entity
const AZ::Aabb aabbA = AZ::Aabb::CreateCenterRadius(
AZ::Vector3::CreateZero(),
10.0f
);
const AZ::Vector3 windDirectionA = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitudeA = 10.0f;
auto forceRegionEntityA = AddWindRegion(aabbA, windDirectionA, windMagnitudeA, WindType::Local);
// Second entity
const AZ::Aabb aabbB = AZ::Aabb::CreateCenterRadius(
AZ::Vector3(5.0f, 0.0f, 0.0f),
10.0f
);
const AZ::Vector3 windDirectionB = AZ::Vector3(0.0f, 5.0f, 0.0f);
const float windMagnitudeB = 20.0f;
auto forceRegionEntityB = AddWindRegion(aabbB, windDirectionB, windMagnitudeB, WindType::Local);
// Sampling position that is inside bounding boxes of both entities should
// give back combined wind value
const AZ::Vector3 position = AZ::Vector3(5.0f, 0.0f, 0.0f);
const AZ::Vector3 localWindValue = GetWind(position);
const AZ::Vector3 expectedWindValue =
GetWindValue(windDirectionA, windMagnitudeA) + GetWindValue(windDirectionB, windMagnitudeB);
EXPECT_THAT(localWindValue, UnitTest::IsClose(expectedWindValue));
}
TEST_F(PhysXWindTest, LocalWind_SingleForceRegionMoved_WindBusReturnsCorrectValuesForOldAndNewPositions)
{
const AZ::Vector3 originalPosition = AZ::Vector3::CreateZero();
const AZ::Aabb aabb = AZ::Aabb::CreateCenterRadius(
originalPosition,
10.0f
);
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 20.0f;
auto forceRegionEntity = AddWindRegion(aabb, windDirection, windMagnitude, WindType::Local);
// Move entity to new position
const AZ::Vector3 newPosition = AZ::Vector3(500.0f, 100.0f, 0.0f);
AZ::TransformBus::Event(forceRegionEntity->GetId()
, &AZ::TransformBus::Events::SetWorldTranslation
, newPosition
);
// Wind value at old position should be zero
{
const AZ::Vector3 localWindValue = GetWind(originalPosition);
EXPECT_TRUE(localWindValue.IsZero());
}
// Sampling new position should return original wind value
{
const AZ::Vector3 localWindValue = GetWind(newPosition);
const AZ::Vector3 expectedWindValue = GetWindValue(windDirection, windMagnitude);
EXPECT_THAT(localWindValue, UnitTest::IsClose(expectedWindValue));
}
}
TEST_F(PhysXWindTest, LocalWind_SingleForceRegionMoved_OnWindChangedNotificationCalledForOldAndNewPosition)
{
const AZ::Vector3 originalPosition = AZ::Vector3::CreateZero();
const AZ::Aabb originalAabb = AZ::Aabb::CreateCenterRadius(
originalPosition,
10.0f
);
const AZ::Vector3 windDirection = AZ::Vector3(1.0f, 0.0f, 0.0f);
const float windMagnitude = 20.0f;
auto forceRegionEntity = AddWindRegion(originalAabb, windDirection, windMagnitude, WindType::Local);
// Tick to flush any existing updates pending in wind system
AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick, 0.01f, AZ::ScriptTimePoint(AZStd::chrono::system_clock::now()));
// Move entity to new position
const AZ::Vector3 newPosition = AZ::Vector3(500.0f, 100.0f, 0.0f);
AZ::TransformBus::Event(forceRegionEntity->GetId()
, &AZ::TransformBus::Events::SetWorldTranslation
, newPosition
);
AZ::Aabb newAabb = originalAabb;
newAabb.Translate(newPosition - originalPosition);
// We expect OnWindChanged to be called exactly twice - for old and new bounding boxes
{
testing::StrictMock<MockWindNotificationsBusHandler> mockHandler;
EXPECT_CALL(mockHandler, OnWindChanged(originalAabb)).Times(1);
EXPECT_CALL(mockHandler, OnWindChanged(newAabb)).Times(1);
AZ::TickBus::Broadcast(&AZ::TickEvents::OnTick, 0.01f, AZ::ScriptTimePoint(AZStd::chrono::system_clock::now()));
}
}
} // namespace PhysX