merge from development

Signed-off-by: greerdv <greerdv@amazon.com>
This commit is contained in:
greerdv
2021-10-15 09:58:36 +01:00
122 changed files with 3114 additions and 982 deletions
@@ -9,7 +9,7 @@
#include "Skin_Common.azsli"
// SRGs
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/Features/Skin/SkinObjectSrg.azsli>
#include <Atom/Features/PBR/ForwardPassSrg.azsli>
// Pass Output
@@ -973,15 +973,15 @@
"tag": "ForwardPass"
},
{
"file": "Shaders/Shadow/Shadowmap.shader",
"file": "Shaders/Shadow/ShadowmapSkin.shader",
"tag": "Shadowmap"
},
{
"file": "Shaders/Depth/DepthPass.shader",
"file": "Shaders/Depth/DepthPassSkin.shader",
"tag": "DepthPass"
},
{
"file": "Shaders/MotionVector/MeshMotionVector.shader",
"file": "Shaders/MotionVector/MeshMotionVectorSkin.shader",
"tag": "MeshMotionVector"
}
],
@@ -27,16 +27,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId);
}
//[GFX TODO][ATOM-15280] Move wrinkle mask data from the default object srg into something specific to the Skin shader
uint m_wrinkle_mask_count;
float4 m_wrinkle_mask_weights[4];
Texture2D m_wrinkle_masks[16];
float GetWrinkleMaskWeight(uint index)
{
return m_wrinkle_mask_weights[index / 4][index % 4];
}
//! Reflection Probe (smallest probe volume that overlaps the object position)
struct ReflectionProbeData
{
@@ -0,0 +1,81 @@
/*
* 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
*
*/
#pragma once
#include <scenesrg.srgi>
ShaderResourceGroup ObjectSrg : SRG_PerObject
{
uint m_objectId;
//! Returns the matrix for transforming points from Object Space to World Space.
float4x4 GetWorldMatrix()
{
return SceneSrg::GetObjectToWorldMatrix(m_objectId);
}
//! Returns the inverse-transpose of the world matrix.
//! Commonly used to transform normals while supporting non-uniform scale.
float3x3 GetWorldMatrixInverseTranspose()
{
return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId);
}
uint m_wrinkle_mask_count;
float4 m_wrinkle_mask_weights[4];
Texture2D m_wrinkle_masks[16];
float GetWrinkleMaskWeight(uint index)
{
return m_wrinkle_mask_weights[index / 4][index % 4];
}
//! Reflection Probe (smallest probe volume that overlaps the object position)
struct ReflectionProbeData
{
row_major float3x4 m_modelToWorld;
row_major float3x4 m_modelToWorldInverse; // does not include extents
float3 m_outerObbHalfLengths;
float3 m_innerObbHalfLengths;
float m_padding;
bool m_useReflectionProbe;
bool m_useParallaxCorrection;
};
ReflectionProbeData m_reflectionProbeData;
TextureCube m_reflectionProbeCubeMap;
float4x4 GetReflectionProbeWorldMatrix()
{
float4x4 modelToWorld = float4x4(
float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(0, 0, 0, 1));
modelToWorld[0] = m_reflectionProbeData.m_modelToWorld[0];
modelToWorld[1] = m_reflectionProbeData.m_modelToWorld[1];
modelToWorld[2] = m_reflectionProbeData.m_modelToWorld[2];
return modelToWorld;
}
float4x4 GetReflectionProbeWorldMatrixInverse()
{
float4x4 modelToWorldInverse = float4x4(
float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(0, 0, 0, 1));
modelToWorldInverse[0] = m_reflectionProbeData.m_modelToWorldInverse[0];
modelToWorldInverse[1] = m_reflectionProbeData.m_modelToWorldInverse[1];
modelToWorldInverse[2] = m_reflectionProbeData.m_modelToWorldInverse[2];
return modelToWorldInverse;
}
}
@@ -6,30 +6,7 @@
*
*/
#include <viewsrg.srgi>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <DepthPassCommon.azsli>
struct VSInput
{
float3 m_position : POSITION;
};
struct VSDepthOutput
{
float4 m_position : SV_Position;
};
VSDepthOutput DepthPassVS(VSInput IN)
{
VSDepthOutput OUT;
float4x4 objectToWorld = ObjectSrg::GetWorldMatrix();
float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0));
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition);
return OUT;
}
// Use the depth pass shader with the default object srg
@@ -0,0 +1,36 @@
/*
* 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
*
*/
#pragma once
#include <viewsrg.srgi>
struct VSInput
{
float3 m_position : POSITION;
};
struct VSDepthOutput
{
float4 m_position : SV_Position;
};
VSDepthOutput DepthPassVS(VSInput IN)
{
VSDepthOutput OUT;
float4x4 objectToWorld = ObjectSrg::GetWorldMatrix();
float4 worldPosition = mul(objectToWorld, float4(IN.m_position, 1.0));
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, worldPosition);
return OUT;
}
@@ -0,0 +1,12 @@
/*
* 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 <Atom/Features/Skin/SkinObjectSrg.azsli>
#include <DepthPassCommon.azsli>
// Use the depth pass shader with the skin object srg
@@ -0,0 +1,24 @@
{
"Source" : "DepthPassSkin",
"DepthStencilState" : {
"Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" }
},
"CompilerHints" : {
"DisableOptimizations" : false
},
"ProgramSettings" :
{
"EntryPoints":
[
{
"name": "DepthPassVS",
"type" : "Vertex"
}
]
},
"DrawList" : "depth"
}
@@ -6,77 +6,7 @@
*
*/
#include <scenesrg.srgi>
#include <viewsrg.srgi>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
#include <MeshMotionVectorCommon.azsli>
struct VSInput
{
float3 m_position : POSITION;
// This gets set automatically by the system at runtime only if it's available.
// There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available.
// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention).
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
// Vertex position of last frame to capture small scale motion due to vertex animation
float3 m_optional_prevPosition : POSITIONT;
};
struct VSOutput
{
float4 m_position : SV_Position;
float3 m_worldPos : TEXCOORD0;
float3 m_worldPosPrev: TEXCOORD1;
};
struct PSOutput
{
float2 m_motion : SV_Target0;
};
// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition.
// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition.
// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention).
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
option bool o_prevPosition_isBound;
VSOutput MainVS(VSInput IN)
{
VSOutput OUT;
OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0));
if (o_prevPosition_isBound)
{
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz;
}
else
{
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
}
return OUT;
}
PSOutput MainPS(VSOutput IN)
{
PSOutput OUT;
// Current clip position
float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0));
// Reprojected last frame's clip position, for skinned mesh it also implies last key frame
float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0));
float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5;
OUT.m_motion = motion;
// Flip y to line up with uv coordinates
OUT.m_motion.y = -OUT.m_motion.y;
return OUT;
}
// Use the mesh motion vector with the default object srg
@@ -0,0 +1,83 @@
/*
* 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
*
*/
#pragma once
#include <scenesrg.srgi>
#include <viewsrg.srgi>
#include <Atom/RPI/ShaderResourceGroups/DefaultDrawSrg.azsli>
struct VSInput
{
float3 m_position : POSITION;
// This gets set automatically by the system at runtime only if it's available.
// There is a soft naming convention that associates this with o_prevPosition_isBound, which will be set to true whenever m_optional_prevPosition is available.
// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention).
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
// Vertex position of last frame to capture small scale motion due to vertex animation
float3 m_optional_prevPosition : POSITIONT;
};
struct VSOutput
{
float4 m_position : SV_Position;
float3 m_worldPos : TEXCOORD0;
float3 m_worldPosPrev: TEXCOORD1;
};
struct PSOutput
{
float2 m_motion : SV_Target0;
};
// Indicates whether the vertex input struct's "m_optional_prevPosition" is bound. If false, it is not safe to read from m_optional_prevPosition.
// This option gets set automatically by the system at runtime; there is a soft naming convention that associates it with m_optional_prevPosition.
// (search "m_optional_" in ShaderVariantAssetBuilder for details on the naming convention).
// [GFX TODO][ATOM-14475]: Come up with a more elegant way to associate the isBound flag with the input stream.
option bool o_prevPosition_isBound;
VSOutput MainVS(VSInput IN)
{
VSOutput OUT;
OUT.m_worldPos = mul(SceneSrg::GetObjectToWorldMatrix(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPos, 1.0));
if (o_prevPosition_isBound)
{
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_optional_prevPosition, 1.0)).xyz;
}
else
{
OUT.m_worldPosPrev = mul(SceneSrg::GetObjectToWorldMatrixPrev(ObjectSrg::m_objectId), float4(IN.m_position, 1.0)).xyz;
}
return OUT;
}
PSOutput MainPS(VSOutput IN)
{
PSOutput OUT;
// Current clip position
float4 clipPos = mul(ViewSrg::m_viewProjectionMatrix, float4(IN.m_worldPos, 1.0));
// Reprojected last frame's clip position, for skinned mesh it also implies last key frame
float4 clipPosPrev = mul(ViewSrg::m_viewProjectionPrevMatrix, float4(IN.m_worldPosPrev, 1.0));
float2 motion = (clipPos.xy / clipPos.w - clipPosPrev.xy / clipPosPrev.w) * 0.5;
OUT.m_motion = motion;
// Flip y to line up with uv coordinates
OUT.m_motion.y = -OUT.m_motion.y;
return OUT;
}
@@ -0,0 +1,12 @@
/*
* 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 <Atom/Features/Skin/SkinObjectSrg.azsli>
#include <MeshMotionVectorCommon.azsli>
// Use the mesh motion vector with the skin object srg
@@ -0,0 +1,24 @@
{
"Source" : "MeshMotionVectorSkin",
"DepthStencilState" : {
"Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" }
},
"DrawList" : "motion",
"ProgramSettings":
{
"EntryPoints":
[
{
"name": "MainVS",
"type": "Vertex"
},
{
"name": "MainPS",
"type": "Fragment"
}
]
}
}
@@ -6,27 +6,7 @@
*
*/
#include <scenesrg.srgi>
#include <viewsrg.srgi>
#include <Atom/Features/PBR/DefaultObjectSrg.azsli>
#include <ShadowmapCommon.azsli>
struct VertexInput
{
float3 m_position : POSITION;
};
struct VertexOutput
{
float4 m_position : SV_Position;
};
VertexOutput MainVS(VertexInput input)
{
const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix();
VertexOutput output;
const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz;
output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
return output;
}
// Use the shadowmap shader with the default object srg
@@ -0,0 +1,33 @@
/*
* 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
*
*/
#pragma once
#include <scenesrg.srgi>
#include <viewsrg.srgi>
struct VertexInput
{
float3 m_position : POSITION;
};
struct VertexOutput
{
float4 m_position : SV_Position;
};
VertexOutput MainVS(VertexInput input)
{
const float4x4 worldMatrix = ObjectSrg::GetWorldMatrix();
VertexOutput output;
const float3 worldPosition = mul(worldMatrix, float4(input.m_position, 1.0)).xyz;
output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
return output;
}
@@ -0,0 +1,12 @@
/*
* 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 <Atom/Features/Skin/SkinObjectSrg.azsli>
#include <ShadowmapCommon.azsli>
// Use the shadowmap shader with the skin object srg
@@ -0,0 +1,26 @@
{
"Source" : "ShadowmapSkin",
"DepthStencilState" : {
"Depth" : { "Enable" : true, "CompareFunc" : "LessEqual" }
},
"DrawList" : "shadow",
"RasterState" :
{
"depthBias" : "10",
"depthBiasSlopeScale" : "4"
},
"ProgramSettings":
{
"EntryPoints":
[
{
"name": "MainVS",
"type": "Vertex"
}
]
}
}
-1
View File
@@ -10,6 +10,5 @@ ly_add_external_target(
NAME renderdoc
3RDPARTY_ROOT_DIRECTORY "${LY_RENDERDOC_PATH}"
VERSION
INCLUDE_DIRECTORIES .
COMPILE_DEFINITIONS USE_RENDERDOC
)
@@ -6,4 +6,5 @@
#
#
set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/librenderdoc.so")
set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/lib/librenderdoc.so")
set(RENDERDOC_INCLUDE_DIRECTORIES "include")
@@ -7,3 +7,4 @@
#
set(RENDERDOC_RUNTIME_DEPENDENCIES "${BASE_PATH}/renderdoc.dll")
set(RENDERDOC_INCLUDE_DIRECTORIES ".")
+3 -3
View File
@@ -344,9 +344,9 @@ namespace Blast
void UpdateMassProperties(
[[maybe_unused]] AzPhysics::MassComputeFlags flags,
[[maybe_unused]] const AZ::Vector3* centerOfMassOffsetOverride,
[[maybe_unused]] const AZ::Matrix3x3* inertiaTensorOverride,
[[maybe_unused]] const float* massOverride) override
[[maybe_unused]] const AZ::Vector3& centerOfMassOffsetOverride,
[[maybe_unused]] const AZ::Matrix3x3& inertiaTensorOverride,
[[maybe_unused]] const float massOverride) override
{
}
@@ -166,7 +166,7 @@ namespace LmbrCentral
return intersection;
}
const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance) > 0;
const bool intersection = AZ::Intersect::IntersectRayObb(src, dir, m_intersectionDataCache.m_obb, distance);
return intersection;
}
@@ -153,7 +153,7 @@ namespace LmbrCentral
m_intersectionDataCache.UpdateIntersectionParams(m_currentTransform, m_diskShapeConfig);
return AZ::Intersect::IntersectRayDisk(
src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance) > 0;
src, dir, m_intersectionDataCache.m_position, m_intersectionDataCache.m_radius, m_intersectionDataCache.m_normal, distance);
}
void DiskShape::DiskIntersectionDataCache::UpdateIntersectionParamsImpl(
+152 -135
View File
@@ -8,6 +8,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/Math/ToString.h>
#include <AzFramework/Physics/Utils.h>
#include <AzFramework/Physics/Configuration/RigidBodyConfiguration.h>
#include <PhysX/NativeTypeIdentifiers.h>
@@ -23,6 +24,28 @@
namespace PhysX
{
namespace
{
const AZ::Vector3 DefaultCenterOfMass = AZ::Vector3::CreateZero();
const float DefaultMass = 1.0f;
const AZ::Matrix3x3 DefaultInertiaTensor = AZ::Matrix3x3::CreateIdentity();
bool IsSimulationShape(const physx::PxShape& pxShape)
{
return (pxShape.getFlags() & physx::PxShapeFlag::eSIMULATION_SHAPE);
}
bool CanShapeComputeMassProperties(const physx::PxShape& pxShape)
{
// Note: List based on computeMassAndInertia function in ExtRigidBodyExt.cpp file in PhysX.
const physx::PxGeometryType::Enum geometryType = pxShape.getGeometryType();
return geometryType == physx::PxGeometryType::eSPHERE
|| geometryType == physx::PxGeometryType::eBOX
|| geometryType == physx::PxGeometryType::eCAPSULE
|| geometryType == physx::PxGeometryType::eCONVEXMESH;
}
}
void RigidBody::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
@@ -152,104 +175,120 @@ namespace PhysX
m_shapes.erase(found);
}
void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3* centerOfMassOffsetOverride, const AZ::Matrix3x3* inertiaTensorOverride, const float* massOverride)
void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3& centerOfMassOffsetOverride, const AZ::Matrix3x3& inertiaTensorOverride, const float massOverride)
{
// Input validation
bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
AZ_Assert(computeCenterOfMass || centerOfMassOffsetOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_COM is not set but COM offset is not specified");
computeCenterOfMass = computeCenterOfMass || !centerOfMassOffsetOverride;
const bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
const bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
const bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
const bool needsCompute = computeCenterOfMass || computeInertiaTensor || computeMass;
const bool includeAllShapesInMassCalculation = AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES);
bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
AZ_Assert(computeInertiaTensor || inertiaTensorOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_INERTIA is not set but inertia tensor is not specified");
computeInertiaTensor = computeInertiaTensor || !inertiaTensorOverride;
bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
AZ_Assert(computeMass || massOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_MASS is not set but mass is not specified");
computeMass = computeMass || !massOverride;
AZ::u32 shapesCount = GetShapeCount();
// Basic cases when we don't need to compute anything
if (shapesCount == 0 || flags == AzPhysics::MassComputeFlags::NONE)
// Basic case where all properties are set directly.
if (!needsCompute)
{
if (massOverride)
{
SetMass(*massOverride);
}
if (inertiaTensorOverride)
{
SetInertia(*inertiaTensorOverride);
}
if (centerOfMassOffsetOverride)
{
SetCenterOfMassOffset(*centerOfMassOffsetOverride);
}
SetCenterOfMassOffset(centerOfMassOffsetOverride);
SetMass(massOverride);
SetInertia(inertiaTensorOverride);
return;
}
// Setup center of mass offset pointer for PxRigidBodyExt::updateMassAndInertia function
AZStd::optional<physx::PxVec3> optionalComOverride;
if (!computeCenterOfMass && centerOfMassOffsetOverride)
// If there are no shapes then set the properties directly without computing anything.
if (m_shapes.empty())
{
optionalComOverride = PxMathConvert(*centerOfMassOffsetOverride);
}
const physx::PxVec3* massLocalPose = optionalComOverride.has_value() ? &optionalComOverride.value() : nullptr;
bool includeAllShapesInMassCalculation =
AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES);
// Handle the case when we don't compute mass
if (!computeMass)
{
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, *massOverride, massLocalPose,
includeAllShapesInMassCalculation);
}
if (!computeInertiaTensor)
{
SetInertia(*inertiaTensorOverride);
}
SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride);
SetMass(computeMass ? DefaultMass : massOverride);
SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride);
return;
}
// Handle the cases when mass should be computed from density
if (shapesCount == 1)
auto cannotComputeMassProperties = [this, includeAllShapesInMassCalculation]
{
AZStd::shared_ptr<Physics::Shape> shape = GetShape(0);
float density = shape->GetMaterial()->GetDensity();
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
return AZStd::any_of(m_shapes.cbegin(), m_shapes.cend(),
[includeAllShapesInMassCalculation](const AZStd::shared_ptr<PhysX::Shape>& shape)
{
const physx::PxShape& pxShape = *shape->GetPxShape();
const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape);
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, density, massLocalPose,
includeAllShapesInMassCalculation);
return includeShape && !CanShapeComputeMassProperties(pxShape);
});
};
// If contains shapes that cannot compute mass properties (triangle mesh,
// plane or heightfield) then default values will be used.
if (cannotComputeMassProperties())
{
AZ_Warning("RigidBody", !computeCenterOfMass,
"Rigid body '%s' cannot compute COM because it contains triangle mesh, plane or heightfield shapes, it will default to %s.",
GetName().c_str(), AZ::ToString(DefaultCenterOfMass).c_str());
AZ_Warning("RigidBody", !computeMass,
"Rigid body '%s' cannot compute Mass because it contains triangle mesh, plane or heightfield shapes, it will default to %0.1f.",
GetName().c_str(), DefaultMass);
AZ_Warning("RigidBody", !computeInertiaTensor,
"Rigid body '%s' cannot compute Inertia because it contains triangle mesh, plane or heightfield shapes, it will default to %s.",
GetName().c_str(), AZ::ToString(DefaultInertiaTensor.RetrieveScale()).c_str());
SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride);
SetMass(computeMass ? DefaultMass : massOverride);
SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride);
return;
}
// Center of mass needs to be considered first since
// it's needed when computing mass and inertia.
if (computeCenterOfMass)
{
// Compute Center of Mass
UpdateCenterOfMass(includeAllShapesInMassCalculation);
}
else
{
AZStd::vector<float> densities(shapesCount);
for (AZ::u32 i = 0; i < shapesCount; ++i)
SetCenterOfMassOffset(centerOfMassOffsetOverride);
}
const physx::PxVec3 pxCenterOfMass = PxMathConvert(GetCenterOfMassLocal());
if (computeMass)
{
// Gather material densities from all shapes,
// mass computation is based on them.
AZStd::vector<float> densities;
densities.reserve(m_shapes.size());
for (const auto& shape : m_shapes)
{
densities[i] = GetShape(i)->GetMaterial()->GetDensity();
densities.emplace_back(shape->GetMaterial()->GetDensity());
}
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, densities.data(),
shapesCount, massLocalPose, includeAllShapesInMassCalculation);
}
// Compute Mass + Inertia
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor,
densities.data(), static_cast<AZ::u32>(densities.size()),
&pxCenterOfMass, includeAllShapesInMassCalculation);
}
// Set the overrides if provided.
// Note: We don't set the center of mass here because it was already provided
// to PxRigidBodyExt::updateMassAndInertia above
if (!computeInertiaTensor)
// There is no physx function to only compute the mass without
// computing the inertia. So now that both have been computed
// we can override the inertia if it's suppose to use a
// specific value set by the user.
if (!computeInertiaTensor)
{
SetInertia(inertiaTensorOverride);
}
}
else
{
SetInertia(*inertiaTensorOverride);
if (computeInertiaTensor)
{
// Set Mass + Compute Inertia
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, massOverride,
&pxCenterOfMass, includeAllShapesInMassCalculation);
}
else
{
SetMass(massOverride);
SetInertia(inertiaTensorOverride);
}
}
}
@@ -344,52 +383,49 @@ namespace PhysX
}
}
void RigidBody::UpdateComputedCenterOfMass()
void RigidBody::UpdateCenterOfMass(bool includeAllShapesInMassCalculation)
{
if (m_pxRigidActor)
if (m_shapes.empty())
{
physx::PxU32 shapeCount = 0;
SetCenterOfMassOffset(DefaultCenterOfMass);
return;
}
AZStd::vector<const physx::PxShape*> pxShapes;
pxShapes.reserve(m_shapes.size());
{
// Filter shapes in the same way that updateMassAndInertia function does.
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
for (const auto& shape : m_shapes)
{
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
shapeCount = m_pxRigidActor->getNbShapes();
}
if (shapeCount > 0)
{
AZStd::vector<physx::PxShape*> shapes;
shapes.resize(shapeCount);
const physx::PxShape& pxShape = *shape->GetPxShape();
const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape);
if (includeShape && CanShapeComputeMassProperties(pxShape))
{
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->getShapes(&shapes[0], shapeCount);
pxShapes.emplace_back(&pxShape);
}
shapes.erase(AZStd::remove_if(shapes.begin()
, shapes.end()
, [](const physx::PxShape* shape)
{
return shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE;
})
, shapes.end());
shapeCount = static_cast<physx::PxU32>(shapes.size());
if (shapeCount == 0)
{
SetZeroCenterOfMass();
return;
}
const auto properties = physx::PxRigidBodyExt::computeMassPropertiesFromShapes(&shapes[0], shapeCount);
const physx::PxTransform computedCenterOfMass(properties.centerOfMass);
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->setCMassLocalPose(computedCenterOfMass);
}
}
else
{
SetZeroCenterOfMass();
}
}
if (pxShapes.empty())
{
SetCenterOfMassOffset(DefaultCenterOfMass);
return;
}
const physx::PxMassProperties pxMassProperties = [this, &pxShapes]
{
// Note: PhysX computeMassPropertiesFromShapes function does not use densities
// to compute the shape's masses, which are needed to calculate the center of mass.
// This differs from updateMassAndInertia function, which uses material density values.
// So the masses used during center of mass calculation do not match the masses
// used during mass/inertia calculation. This is an inconsistency in PhysX.
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
return physx::PxRigidBodyExt::computeMassPropertiesFromShapes(pxShapes.data(), static_cast<physx::PxU32>(pxShapes.size()));
}();
SetCenterOfMassOffset(PxMathConvert(pxMassProperties.centerOfMass));
}
void RigidBody::SetInertia(const AZ::Matrix3x3& inertia)
@@ -401,16 +437,6 @@ namespace PhysX
}
}
void RigidBody::ComputeInertia()
{
if (m_pxRigidActor)
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
auto localPose = m_pxRigidActor->getCMassLocalPose().p;
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, m_pxRigidActor->getMass(), &localPose);
}
}
AZ::Vector3 RigidBody::GetLinearVelocity() const
{
if (m_pxRigidActor)
@@ -783,13 +809,4 @@ namespace PhysX
{
return m_name;
}
void RigidBody::SetZeroCenterOfMass()
{
if (m_pxRigidActor)
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->setCMassLocalPose(physx::PxTransform(PxMathConvert(AZ::Vector3::CreateZero())));
}
}
}
+4 -6
View File
@@ -109,17 +109,15 @@ namespace PhysX
void RemoveShape(AZStd::shared_ptr<Physics::Shape> shape) override;
void UpdateMassProperties(AzPhysics::MassComputeFlags flags = AzPhysics::MassComputeFlags::DEFAULT,
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
const float* massOverride = nullptr) override;
const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(),
const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(),
const float massOverride = 1.0f) override;
private:
void CreatePhysXActor(const AzPhysics::RigidBodyConfiguration& configuration);
void UpdateComputedCenterOfMass();
void ComputeInertia();
void UpdateCenterOfMass(bool includeAllShapesInMassCalculation);
void SetInertia(const AZ::Matrix3x3& inertia);
void SetZeroCenterOfMass();
AZStd::shared_ptr<physx::PxRigidDynamic> m_pxRigidActor;
AZStd::vector<AZStd::shared_ptr<PhysX::Shape>> m_shapes;
+2 -2
View File
@@ -198,8 +198,8 @@ namespace PhysX
AZ_Warning("PhysXScene", shapeAdded, "No Collider or Shape information found when creating Rigid body [%s]", configuration->m_debugName.c_str());
}
const AzPhysics::MassComputeFlags& flags = configuration->GetMassComputeFlags();
newBody->UpdateMassProperties(flags, &configuration->m_centerOfMassOffset,
&configuration->m_inertiaTensor, &configuration->m_mass);
newBody->UpdateMassProperties(flags, configuration->m_centerOfMassOffset,
configuration->m_inertiaTensor, configuration->m_mass);
crc = AZ::Crc32(newBody, sizeof(*newBody));
return newBody;
+172 -51
View File
@@ -12,6 +12,8 @@
#include <AzTest/AzTest.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/UnitTest/UnitTest.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AZTestShared/Utils/Utils.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
@@ -1283,11 +1285,13 @@ namespace PhysX
EXPECT_TRUE(AZ::IsClose(expectedMass, mass, 0.001f));
}
// Valid material density values: [0.01f, 1e5f]
INSTANTIATE_TEST_CASE_P(PhysX, MultiShapesDensityTestFixture,
::testing::Values(
AZStd::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::max()),
AZStd::make_pair(-std::numeric_limits<float>::max(), 0.0f),
AZStd::make_pair(1.0f, 1e9f)
AZStd::make_pair(0.01f, 0.01f),
AZStd::make_pair(1e5f, 1e5f),
AZStd::make_pair(0.01f, 1e5f),
AZStd::make_pair(2364.0f, 10.0f)
));
// Fixture for testing extreme density values
@@ -1311,6 +1315,7 @@ namespace PhysX
&& resultingDensity <= Physics::MaterialConfiguration::MaxDensityLimit);
}
// Valid material density values: [0.01f, 1e5f]
INSTANTIATE_TEST_CASE_P(PhysX, DensityBoundariesTestFixture,
::testing::Values(
std::numeric_limits<float>::min(),
@@ -1318,7 +1323,9 @@ namespace PhysX
-std::numeric_limits<float>::max(),
0.0f,
1.0f,
1e9f
1e9f,
0.01f,
1e5f
));
enum class SimulatedShapesMode
@@ -1329,7 +1336,7 @@ namespace PhysX
};
class MassComputeFixture
: public ::testing::TestWithParam<::testing::tuple<SimulatedShapesMode, AzPhysics::MassComputeFlags, bool>>
: public ::testing::TestWithParam<::testing::tuple<Physics::ShapeType, SimulatedShapesMode, AzPhysics::MassComputeFlags, bool, bool>>
{
public:
void SetUp() override final
@@ -1349,6 +1356,8 @@ namespace PhysX
AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &m_rigidBodyConfig);
m_rigidBody = azdynamic_cast<AzPhysics::RigidBody*>(sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, simBodyHandle));
}
ASSERT_TRUE(m_rigidBody != nullptr);
}
void TearDown() override final
@@ -1363,130 +1372,242 @@ namespace PhysX
m_rigidBody = nullptr;
}
SimulatedShapesMode GetShapesMode() const
Physics::ShapeType GetShapeType() const
{
return ::testing::get<0>(GetParam());
}
AzPhysics::MassComputeFlags GetMassComputeFlags() const
SimulatedShapesMode GetShapesMode() const
{
return ::testing::get<1>(GetParam());
}
AzPhysics::MassComputeFlags GetMassComputeFlags() const
{
const AzPhysics::MassComputeFlags massComputeFlags = ::testing::get<2>(GetParam());
if (IncludeAllShapes())
{
return massComputeFlags | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES;
}
else
{
return massComputeFlags;
}
}
bool IncludeAllShapes() const
{
return ::testing::get<3>(GetParam());
}
bool IsMultiShapeTest() const
{
return ::testing::get<2>(GetParam());
return ::testing::get<4>(GetParam());
}
bool IsMassExpectedToChange() const
{
return m_rigidBodyConfig.m_computeMass &&
(!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
(GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
}
bool IsComExpectedToChange() const
{
return m_rigidBodyConfig.m_computeCenterOfMass &&
(!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
(GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
}
bool IsInertiaExpectedToChange() const
{
return m_rigidBodyConfig.m_computeInertiaTensor &&
(!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
(GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
}
AZStd::shared_ptr<Physics::Shape> CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, Physics::ShapeType shapeType)
{
AZStd::shared_ptr<Physics::Shape> shape;
Physics::System* physics = AZ::Interface<Physics::System>::Get();
switch (shapeType)
{
case Physics::ShapeType::Sphere:
shape = physics->CreateShape(colliderConfiguration, Physics::SphereShapeConfiguration());
break;
case Physics::ShapeType::Box:
shape = physics->CreateShape(colliderConfiguration, Physics::BoxShapeConfiguration());
break;
case Physics::ShapeType::Capsule:
shape = physics->CreateShape(colliderConfiguration, Physics::CapsuleShapeConfiguration());
break;
}
return shape;
};
AzPhysics::RigidBodyConfiguration m_rigidBodyConfig;
AzPhysics::RigidBody* m_rigidBody;
AzPhysics::RigidBody* m_rigidBody = nullptr;
AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle;
};
TEST_P(MassComputeFixture, RigidBody_ComputeMassFlagsCombinationsTwoShapes_MassPropertiesCalculatedAccordingly)
{
SimulatedShapesMode shapeMode = GetShapesMode();
AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags();
bool multiShapeTest = IsMultiShapeTest();
Physics::System* physics = AZ::Interface<Physics::System>::Get();
const Physics::ShapeType shapeType = GetShapeType();
const SimulatedShapesMode shapeMode = GetShapesMode();
const AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags();
const bool multiShapeTest = IsMultiShapeTest();
// Save initial values
AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld();
AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld();
float massBefore = m_rigidBody->GetMass();
const AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld();
const AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld();
const float massBefore = m_rigidBody->GetMass();
// Box shape will be simulated for ALL and MIXED shape modes
Physics::ColliderConfiguration boxColliderConfig;
boxColliderConfig.m_isSimulated =
// Shape will be simulated for ALL and MIXED shape modes
Physics::ColliderConfiguration colliderConfig;
colliderConfig.m_isSimulated =
(shapeMode == SimulatedShapesMode::ALL || shapeMode == SimulatedShapesMode::MIXED);
boxColliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f);
colliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f);
AZStd::shared_ptr<Physics::Shape> boxShape =
physics->CreateShape(boxColliderConfig, Physics::BoxShapeConfiguration());
m_rigidBody->AddShape(boxShape);
AZStd::shared_ptr<Physics::Shape> shape = CreateShape(colliderConfig, shapeType);
m_rigidBody->AddShape(shape);
if (multiShapeTest)
{
// Sphere shape will be simulated only for the ALL shape mode
Physics::ColliderConfiguration sphereColliderConfig;
sphereColliderConfig.m_isSimulated = (shapeMode == SimulatedShapesMode::ALL);
sphereColliderConfig.m_position = AZ::Vector3(-1.0f, 0.0f, 0.0f);
AZStd::shared_ptr<Physics::Shape> sphereShape =
physics->CreateShape(sphereColliderConfig, Physics::SphereShapeConfiguration());
sphereColliderConfig.m_position = AZ::Vector3(-2.0f, 0.0f, 0.0f);
AZStd::shared_ptr<Physics::Shape> sphereShape = CreateShape(sphereColliderConfig, Physics::ShapeType::Sphere);
m_rigidBody->AddShape(sphereShape);
}
// Verify swapping materials results in changes in the mass.
m_rigidBody->UpdateMassProperties(massComputeFlags, &m_rigidBodyConfig.m_centerOfMassOffset,
&m_rigidBodyConfig.m_inertiaTensor, &m_rigidBodyConfig.m_mass);
m_rigidBody->UpdateMassProperties(massComputeFlags, m_rigidBodyConfig.m_centerOfMassOffset,
m_rigidBodyConfig.m_inertiaTensor, m_rigidBodyConfig.m_mass);
float massAfter = m_rigidBody->GetMass();
AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld();
AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld();
const float massAfter = m_rigidBody->GetMass();
const AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld();
const AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld();
using ::testing::Not;
using ::testing::FloatNear;
using ::UnitTest::IsClose;
if (IsMassExpectedToChange())
{
EXPECT_FALSE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON));
EXPECT_THAT(massBefore, Not(FloatNear(massAfter, FLT_EPSILON)));
}
else
{
EXPECT_TRUE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON));
EXPECT_THAT(massBefore, FloatNear(massAfter, FLT_EPSILON));
}
if (IsComExpectedToChange())
{
EXPECT_FALSE(comBefore.IsClose(comAfter));
EXPECT_THAT(comBefore, Not(IsClose(comAfter)));
}
else
{
EXPECT_TRUE(comBefore.IsClose(comAfter));
EXPECT_THAT(comBefore, IsClose(comAfter));
}
if (IsInertiaExpectedToChange())
{
EXPECT_FALSE(inertiaBefore.IsClose(inertiaAfter));
EXPECT_THAT(inertiaBefore, Not(IsClose(inertiaAfter)));
}
else
{
EXPECT_TRUE(inertiaBefore.IsClose(inertiaAfter));
EXPECT_THAT(inertiaBefore, IsClose(inertiaAfter));
}
}
AzPhysics::MassComputeFlags possibleMassComputeFlags[] = {
AzPhysics::MassComputeFlags::NONE, AzPhysics::MassComputeFlags::DEFAULT, AzPhysics::MassComputeFlags::COMPUTE_MASS,
AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
AzPhysics::MassComputeFlags::DEFAULT | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA, AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
static const AzPhysics::MassComputeFlags PossibleMassComputeFlags[] =
{
// No compute
AzPhysics::MassComputeFlags::NONE,
// Compute Mass only
AzPhysics::MassComputeFlags::COMPUTE_MASS,
// Compute Inertia only
AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
// Compute COM only
AzPhysics::MassComputeFlags::COMPUTE_COM,
// Compute combinations of 2
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM,
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES
// Compute all
AzPhysics::MassComputeFlags::DEFAULT, // COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS
};
INSTANTIATE_TEST_CASE_P(PhysX, MassComputeFixture, ::testing::Combine(
::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }),
::testing::ValuesIn(possibleMassComputeFlags),
::testing::Bool()));
::testing::ValuesIn({ Physics::ShapeType::Sphere, Physics::ShapeType::Box, Physics::ShapeType::Capsule }), // Values for GetShapeType()
::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }), // Values for GetShapesMode()
::testing::ValuesIn(PossibleMassComputeFlags), // Values for GetMassComputeFlags()
::testing::Bool(), // Values for IncludeAllShapes()
::testing::Bool())); // Values for IsMultiShapeTest()
class MassPropertiesWithTriangleMesh
: public ::testing::TestWithParam<AzPhysics::MassComputeFlags>
{
public:
void SetUp() override
{
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
AzPhysics::SceneConfiguration sceneConfiguration = physicsSystem->GetDefaultSceneConfiguration();
sceneConfiguration.m_sceneName = AzPhysics::DefaultPhysicsSceneName;
m_testSceneHandle = physicsSystem->AddScene(sceneConfiguration);
}
}
void TearDown() override
{
// Clean up the Test scene
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
physicsSystem->RemoveScene(m_testSceneHandle);
}
m_testSceneHandle = AzPhysics::InvalidSceneHandle;
}
AzPhysics::MassComputeFlags GetMassComputeFlags() const
{
return GetParam();
}
AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle;
};
TEST_P(MassPropertiesWithTriangleMesh, KinematicRigidBody_ComputeMassProperties_TriggersWarnings)
{
const AzPhysics::MassComputeFlags flags = GetMassComputeFlags();
const bool doesComputeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
const bool doesComputeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
const bool doesComputeInertia = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
UnitTest::ErrorHandler computeCenterOfMassWarningHandler(
"cannot compute COM");
UnitTest::ErrorHandler computeMassWarningHandler(
"cannot compute Mass");
UnitTest::ErrorHandler computeIneriaWarningHandler(
"cannot compute Inertia");
AzPhysics::SimulatedBodyHandle rigidBodyhandle = TestUtils::AddKinematicTriangleMeshCubeToScene(m_testSceneHandle, 3.0f, flags);
EXPECT_TRUE(rigidBodyhandle != AzPhysics::InvalidSimulatedBodyHandle);
EXPECT_EQ(computeCenterOfMassWarningHandler.GetExpectedWarningCount(), doesComputeCenterOfMass ? 1 : 0);
EXPECT_EQ(computeMassWarningHandler.GetExpectedWarningCount(), doesComputeMass ? 1 : 0);
EXPECT_EQ(computeIneriaWarningHandler.GetExpectedWarningCount(), doesComputeInertia ? 1 : 0);
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
sceneInterface->RemoveSimulatedBody(m_testSceneHandle, rigidBodyhandle);
}
}
INSTANTIATE_TEST_CASE_P(PhysX, MassPropertiesWithTriangleMesh,
::testing::ValuesIn(PossibleMassComputeFlags)); // Values for GetMassComputeFlags()
} // namespace PhysX
+30
View File
@@ -253,6 +253,36 @@ namespace PhysX
return AzPhysics::InvalidSimulatedBodyHandle;
}
AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags)
{
// Generate input data
VertexIndexData cubeMeshData = GenerateCubeMeshData(halfExtent);
AZStd::vector<AZ::u8> cookedData;
bool cookingResult = false;
Physics::SystemRequestBus::BroadcastResult(cookingResult, &Physics::SystemRequests::CookTriangleMeshToMemory,
cubeMeshData.first.data(), static_cast<AZ::u32>(cubeMeshData.first.size()),
cubeMeshData.second.data(), static_cast<AZ::u32>(cubeMeshData.second.size()),
cookedData);
AZ_Assert(cookingResult, "Failed to cook the cube mesh.");
// Setup shape & collider configurations
auto shapeConfig = AZStd::make_shared<Physics::CookedMeshShapeConfiguration>();
shapeConfig->SetCookedMeshData(cookedData.data(), cookedData.size(),
Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh);
AzPhysics::RigidBodyConfiguration rigidBodyConfiguration;
rigidBodyConfiguration.m_kinematic = true;
rigidBodyConfiguration.SetMassComputeFlags(massComputeFlags);
rigidBodyConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(
AZStd::make_shared<Physics::ColliderConfiguration>(), shapeConfig);
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
return sceneInterface->AddSimulatedBody(scene, &rigidBodyConfiguration);
}
return AzPhysics::InvalidSimulatedBodyHandle;
}
void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag)
{
Physics::CollisionFilteringRequestBus::Event(entity->GetId(), &Physics::CollisionFilteringRequests::SetCollisionLayer, layerName, AZ::Crc32(colliderTag.c_str()));
+1
View File
@@ -89,6 +89,7 @@ namespace PhysX
const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
AzPhysics::SimulatedBodyHandle AddStaticTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent);
AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags);
// Collision Filtering
void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag = "");
@@ -567,13 +567,15 @@ namespace Terrain
ShaderMacroMaterialData& shaderData = macroMaterialData.at(i);
const AZ::Aabb& materialBounds = materialData.m_bounds;
// Use reverse coordinates (1 - y) for the y direction so that the lower left corner of the macro material images
// map to the lower left corner in world space. This will match up with the height uv coordinate mapping.
shaderData.m_uvMin = {
(xPatch - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(),
(yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()
1.0f - ((yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent())
};
shaderData.m_uvMax = {
((xPatch + GridMeters) - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(),
((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()
1.0f - (((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent())
};
shaderData.m_normalFactor = materialData.m_normalFactor;
shaderData.m_flipNormalX = materialData.m_normalFlipX;
@@ -91,6 +91,11 @@ namespace WhiteBox
bodyConfiguration.m_position = worldTransform.GetTranslation();
bodyConfiguration.m_kinematic = true; // note: this field is ignored in the WhiteBoxBodyType::Static case
bodyConfiguration.m_colliderAndShapeData = shape;
// Since the shape used is a triangle mesh the COM, Mass and Inertia
// cannot be computed. Disable them to use default values.
bodyConfiguration.m_computeCenterOfMass = false;
bodyConfiguration.m_computeMass = false;
bodyConfiguration.m_computeInertiaTensor = false;
m_simulatedBodyHandle = sceneInterface->AddSimulatedBody(defaultScene, &bodyConfiguration);
}
break;