Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/AnimationConfiguration.h>
#include <AzCore/Serialization/EditContext.h>
namespace Physics
{
AZ_CLASS_ALLOCATOR_IMPL(AnimationConfiguration, AZ::SystemAllocator, 0)
void AnimationConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<AnimationConfiguration>()
->Version(3)
->Field("hitDetectionConfig", &AnimationConfiguration::m_hitDetectionConfig)
->Field("ragdollConfig", &AnimationConfiguration::m_ragdollConfig)
->Field("clothConfig", &AnimationConfiguration::m_clothConfig)
->Field("simulatedObjectColliderConfig", &AnimationConfiguration::m_simulatedObjectColliderConfig)
;
}
}
} // Physics
@@ -0,0 +1,37 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Physics/Character.h>
#include <AzFramework/Physics/Ragdoll.h>
namespace Physics
{
/// Configuration for animated physics structures which are more detailed than the character controller.
/// For example, ragdoll or hit detection configurations.
class AnimationConfiguration
{
public:
AZ_RTTI(AnimationConfiguration, "{6D53168F-470E-4B41-986A-612506F09B40}");
AZ_CLASS_ALLOCATOR_DECL
virtual ~AnimationConfiguration() = default;
static void Reflect(AZ::ReflectContext* context);
CharacterColliderConfiguration m_hitDetectionConfig;
RagdollConfiguration m_ragdollConfig;
CharacterColliderConfiguration m_clothConfig;
CharacterColliderConfiguration m_simulatedObjectColliderConfig;
};
} // namespace Physics
@@ -0,0 +1,127 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/World.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzFramework/Physics/Casts.h>
#include <AzFramework/Physics/CollisionBus.h>
#include <AzFramework/Physics/Configuration/CollisionConfiguration.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/sort.h>
#include <AzCore/Interface/Interface.h>
namespace Physics
{
/// This structure is used only for reflecting type vector<RayCastHit> to
/// serialize and behavior context. It's not used in the API anywhere
struct RaycastHitArray
{
AZ_TYPE_INFO(RaycastHitArray, "{BAFCC4E7-A06B-4909-B2AE-C89D9E84FE4E}");
AZStd::vector<Physics::RayCastHit> m_hitArray;
};
AZStd::vector<AZStd::pair<AzPhysics::CollisionGroup, AZStd::string>> PopulateCollisionGroups()
{
AZStd::vector<AZStd::pair<AzPhysics::CollisionGroup, AZStd::string>> elems;
const AzPhysics::CollisionConfiguration& configuration = AZ::Interface<Physics::CollisionRequests>::Get()->GetCollisionConfiguration();
for (const AzPhysics::CollisionGroups::Preset& preset : configuration.m_collisionGroups.GetPresets())
{
elems.push_back({ AzPhysics::CollisionGroup(preset.m_name), preset.m_name });
}
return elems;
}
void RayCastHit::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<RayCastRequest>()
->Field("Distance", &RayCastRequest::m_distance)
->Field("Start", &RayCastRequest::m_start)
->Field("Direction", &RayCastRequest::m_direction)
->Field("Collision", &RayCastRequest::m_collisionGroup)
->Field("QueryType", &RayCastRequest::m_queryType)
->Field("MaxResults", &RayCastRequest::m_maxResults)
;
serializeContext->Class<RayCastHit>()
->Field("Distance", &RayCastHit::m_distance)
->Field("Position", &RayCastHit::m_position)
->Field("Normal", &RayCastHit::m_normal)
;
serializeContext->Class<RaycastHitArray>()
->Field("HitArray", &RaycastHitArray::m_hitArray)
;
if (auto editContext = azrtti_cast<AZ::EditContext*>(serializeContext->GetEditContext()))
{
editContext->Enum<QueryType>("Query Type", "Object types to include in the query")
->Value("Static", QueryType::Static)
->Value("Dynamic", QueryType::Dynamic)
->Value("Static and Dynamic", QueryType::StaticAndDynamic)
;
editContext->Class<RayCastRequest>("RayCast Request", "Parameters for raycast")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &RayCastRequest::m_start, "Start", "Start position of the raycast")
->DataElement(AZ::Edit::UIHandlers::Default, &RayCastRequest::m_distance, "Distance", "Length of the raycast")
->DataElement(AZ::Edit::UIHandlers::Default, &RayCastRequest::m_direction, "Direction", "Direction of the raycast")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &RayCastRequest::m_collisionGroup, "Collision Group", "The layers to include in the query")
->Attribute(AZ::Edit::Attributes::EnumValues, &PopulateCollisionGroups)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &RayCastRequest::m_queryType, "Query Type", "Object types to include in the query")
->DataElement(AZ::Edit::UIHandlers::Default, &RayCastRequest::m_maxResults, "Max results", "The Maximum results for this request to return, this is limited by the value set in WorldConfiguration")
;
}
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<RayCastRequest>("RayCastRequest")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Property("Distance", BehaviorValueProperty(&RayCastRequest::m_distance))
->Property("Start", BehaviorValueProperty(&RayCastRequest::m_start))
->Property("Direction", BehaviorValueProperty(&RayCastRequest::m_direction))
->Property("Collision", BehaviorValueProperty(&RayCastRequest::m_collisionGroup))
// Until enum class support for behavior context is done, expose this as an int
->Property("QueryType", [](const RayCastRequest& self) { return static_cast<int>(self.m_queryType); },
[](RayCastRequest& self, int newQueryType) { self.m_queryType = QueryType(newQueryType); })
->Property("MaxResults", BehaviorValueProperty(&RayCastRequest::m_maxResults))
;
behaviorContext->Class<RayCastHit>("RayCastHit")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Property("Distance", BehaviorValueProperty(&RayCastHit::m_distance))
->Property("Position", BehaviorValueProperty(&RayCastHit::m_position))
->Property("Normal", BehaviorValueProperty(&RayCastHit::m_normal))
->Property("EntityId", [](RayCastHit& result) { return result.m_body != nullptr ? result.m_body->GetEntityId() : AZ::EntityId(); }, nullptr)
;
behaviorContext->Class<RaycastHitArray>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Property("HitArray", BehaviorValueProperty(&RaycastHitArray::m_hitArray))
;
}
}
} // namespace Physics
@@ -0,0 +1,175 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <functional>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzFramework/Physics/WorldBody.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Material.h>
namespace Physics
{
class WorldBody;
class Shape;
class ShapeConfiguration;
/// Enum to specify the hit type returned by the filter callback.
enum QueryHitType
{
None, ///< The hit should not be reported.
Touch, ///< The hit should be reported but it should not block the query
Block ///< The hit should be reported and it should block the query
};
/// Callback used for directed scene queries: RayCasts and ShapeCasts
using FilterCallback = AZStd::function<QueryHitType(const Physics::WorldBody* body, const Physics::Shape* shape)>;
/// Enum to specify which shapes are included in the query.
enum class QueryType : int
{
Static, ///< Only test against static shapes
Dynamic, ///< Only test against dynamic shapes
StaticAndDynamic ///< Test against both static and dynamic shapes
};
//! Scene query and geometry query behavior flags.
//!
//! HitFlags are used for 3 different purposes:
//!
//! 1) To request hit fields to be filled in by scene queries (such as hit position, normal, face index or UVs).
//! 2) Once query is completed, to indicate which fields are valid (note that a query may produce more valid fields than requested).
//! 3) To specify additional options for the narrow phase and mid-phase intersection routines.
enum class HitFlags : AZ::u16
{
Position = (1 << 0), //!< "position" member of the hit is valid
Normal = (1 << 1), //!< "normal" member of the hit is valid
UV = (1 << 3), //!< "u" and "v" barycentric coordinates of the hit are valid. Not applicable to ShapeCast queries.
//! Performance hint flag for ShapeCasts when it is known upfront there's no initial overlap.
//! NOTE: using this flag may cause undefined results if shapes are initially overlapping.
AssumeNoInitialOverlap = (1 << 4),
MeshMultiple = (1 << 5), //!< Report all hits for meshes rather than just the first. Not applicable to ShapeCast queries.
//! Report any first hit for meshes. If neither MeshMultiple nor MeshAny is specified,
//! a single closest hit will be reported for meshes.
MeshAny = (1 << 6),
//! Report hits with back faces of mesh triangles. Also report hits for raycast
//! originating on mesh surface and facing away from the surface normal. Not applicable to ShapeCast queries.
MeshBothSides = (1 << 7),
PreciseSweep = (1 << 8), //!< Use more accurate but slower narrow phase sweep tests.
MTD = (1 << 9), //!< Report the minimum translation depth, normal and contact point.
FaceIndex = (1 << 10), //!< "face index" member of the hit is valid. Required to get the per-face material data.
Default = Position | Normal | FaceIndex
};
/// Casts a ray from a starting pose along a direction returning objects that intersected with the ray.
struct RayCastRequest
{
AZ_CLASS_ALLOCATOR(RayCastRequest, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(RayCastRequest, "{53EAD088-A391-48F1-8370-2A1DBA31512F}");
float m_distance = 500.0f; ///< The distance along m_dir direction.
AZ::Vector3 m_start = AZ::Vector3::CreateZero(); ///< World space point where ray starts from.
AZ::Vector3 m_direction = AZ::Vector3::CreateZero(); ///< World space direction (normalized).
AzPhysics::CollisionGroup m_collisionGroup = AzPhysics::CollisionGroup::All; ///< The layers to include in the query
FilterCallback m_filterCallback = nullptr; ///< Hit filtering function
QueryType m_queryType = QueryType::StaticAndDynamic; ///< Object types to include in the query
HitFlags m_hitFlags = HitFlags::Default; ///< Query behavior flags
AZ::u64 m_maxResults = 32; ///< The Maximum results for this request to return, this is limited by the value set in WorldConfiguration
};
/// Sweeps a shape from a starting pose along a direction returning objects that intersected with the shape.
struct ShapeCastRequest
{
AZ_CLASS_ALLOCATOR(ShapeCastRequest, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(ShapeCastRequest, "{52F6C536-92F6-4C05-983D-0A74800AE56D}");
float m_distance = 500.0f; /// The distance to cast along m_dir direction.
AZ::Transform m_start = AZ::Transform::CreateIdentity(); ///< World space start position. Assumes only rotation + translation (no scaling).
AZ::Vector3 m_direction = AZ::Vector3::CreateZero(); ///< World space direction (Should be normalized)
ShapeConfiguration* m_shapeConfiguration = nullptr; ///< Shape information.
AzPhysics::CollisionGroup m_collisionGroup = AzPhysics::CollisionGroup::All; ///< Collision filter for the query.
FilterCallback m_filterCallback = nullptr; ///< Hit filtering function
QueryType m_queryType = QueryType::StaticAndDynamic; ///< Object types to include in the query
HitFlags m_hitFlags = HitFlags::Default; ///< Query behavior flags
AZ::u64 m_maxResults = 32; ///< The Maximum results for this request to return, this is limited by the value set in WorldConfiguration
};
/// Callback used for undirected scene queries: Overlaps
using OverlapFilterCallback = AZStd::function<bool(const Physics::WorldBody* body, const Physics::Shape* shape)>;
/// Searches a region enclosed by a specified shape for any overlapping objects in the scene.
struct OverlapRequest
{
AZ_CLASS_ALLOCATOR(OverlapRequest, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(OverlapRequest, "{3DC986C2-316B-4C54-A0A6-8ABBB8ABCC4A}");
AZ::Transform m_pose = AZ::Transform::CreateIdentity(); ///< Initial shape pose
ShapeConfiguration* m_shapeConfiguration = nullptr; ///< Shape information.
AzPhysics::CollisionGroup m_collisionGroup = AzPhysics::CollisionGroup::All; ///< Collision filter for the query.
OverlapFilterCallback m_filterCallback = nullptr; ///< Hit filtering function
QueryType m_queryType = QueryType::StaticAndDynamic; ///< Object types to include in the query
AZ::u64 m_maxResults = 32; ///< The Maximum results for this request to return, this is limited by the value set in WorldConfiguration
};
/// Structure used to store the result from either a raycast or a shape cast.
struct RayCastHit
{
AZ_CLASS_ALLOCATOR(RayCastHit, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(RayCastHit, "{A46CBEA6-6B92-4809-9363-9DDF0F74F296}");
static void Reflect(AZ::ReflectContext* context);
inline operator bool() const { return m_body != nullptr; }
float m_distance = 0.0f; ///< The distance along the cast at which the hit occurred as given by Dot(m_normal, startPoint) - Dot(m_normal, m_point).
AZ::Vector3 m_position = AZ::Vector3::CreateZero(); ///< The position of the hit in world space
AZ::Vector3 m_normal = AZ::Vector3::CreateZero(); ///< The normal of the surface hit
WorldBody* m_body = nullptr; ///< World body that was hit.
Shape* m_shape = nullptr; ///< The shape on the body that was hit
Material* m_material = nullptr; ///< The material on the shape (or face) that was hit
};
/// Overlap hit.
struct OverlapHit
{
AZ_CLASS_ALLOCATOR(OverlapHit, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(OverlapHit, "{7A7201B9-67B5-438B-B4EB-F3EEBB78C617}");
inline operator bool() const { return m_body != nullptr; }
WorldBody* m_body = nullptr; ///< World body that was hit.
Shape* m_shape = nullptr; ///< The shape on the body that was hit
Material* m_material = nullptr; ///< The material on the shape (or face) that was hit
};
/// Bitwise operators for HitFlags
inline HitFlags operator|(HitFlags lhs, HitFlags rhs)
{
return static_cast<HitFlags>(static_cast<AZ::u16>(lhs) | static_cast<AZ::u16>(rhs));
}
inline HitFlags operator&(HitFlags lhs, HitFlags rhs)
{
return static_cast<HitFlags>(static_cast<AZ::u16>(lhs) & static_cast<AZ::u16>(rhs));
}
} // namespace Physics
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(Physics::QueryType, "{0E0E56A8-73A8-40B4-B438-B19FC852E3C0}");
}
@@ -0,0 +1,139 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/Character.h>
#include <AzCore/Serialization/EditContext.h>
namespace Physics
{
AZ_CLASS_ALLOCATOR_IMPL(CharacterColliderNodeConfiguration, AZ::SystemAllocator, 0)
AZ_CLASS_ALLOCATOR_IMPL(CharacterColliderConfiguration, AZ::SystemAllocator, 0)
void CharacterColliderNodeConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<CharacterColliderNodeConfiguration>()
->Version(1)
->Field("name", &CharacterColliderNodeConfiguration::m_name)
->Field("shapes", &CharacterColliderNodeConfiguration::m_shapes)
;
}
}
Physics::CharacterColliderNodeConfiguration* CharacterColliderConfiguration::FindNodeConfigByName(const AZStd::string& nodeName) const
{
auto nodeIterator = AZStd::find_if(m_nodes.begin(), m_nodes.end(), [&nodeName](const Physics::CharacterColliderNodeConfiguration& node)
{
return node.m_name == nodeName;
});
if (nodeIterator != m_nodes.end())
{
return const_cast<Physics::CharacterColliderNodeConfiguration*>(nodeIterator);
}
return nullptr;
}
AZ::Outcome<size_t> CharacterColliderConfiguration::FindNodeConfigIndexByName(const AZStd::string& nodeName) const
{
auto nodeIterator = AZStd::find_if(m_nodes.begin(), m_nodes.end(), [&nodeName](const Physics::CharacterColliderNodeConfiguration& node)
{
return node.m_name == nodeName;
});
if (nodeIterator != m_nodes.end())
{
return AZ::Success(static_cast<size_t>(nodeIterator - m_nodes.begin()));
}
return AZ::Failure();
}
void CharacterColliderConfiguration::RemoveNodeConfigByName(const AZStd::string& nodeName)
{
const AZ::Outcome<size_t> configIndex = FindNodeConfigIndexByName(nodeName);
if (configIndex.IsSuccess())
{
m_nodes.erase(m_nodes.begin() + configIndex.GetValue());
}
}
void CharacterColliderConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<CharacterColliderConfiguration>()
->Version(1)
->Field("nodes", &CharacterColliderConfiguration::m_nodes)
;
}
}
void CharacterConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<CharacterConfiguration>()
->Version(2)
->Field("CollisionLayer", &CharacterConfiguration::m_collisionLayer)
->Field("CollisionGroupId", &CharacterConfiguration::m_collisionGroupId)
->Field("Material", &CharacterConfiguration::m_materialSelection)
->Field("UpDirection", &CharacterConfiguration::m_upDirection)
->Field("MaximumSlopeAngle", &CharacterConfiguration::m_maximumSlopeAngle)
->Field("StepHeight", &CharacterConfiguration::m_stepHeight)
->Field("MinDistance", &CharacterConfiguration::m_minimumMovementDistance)
->Field("MaxSpeed", &CharacterConfiguration::m_maximumSpeed)
->Field("ColliderTag", &CharacterConfiguration::m_colliderTag)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<CharacterConfiguration>(
"Character Configuration", "Character Configuration")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_collisionLayer,
"Collision Layer", "The collision layer assigned to the controller")
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_collisionGroupId,
"Collides With", "The collision layers this character controller collides with")
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_materialSelection,
"Physics Material", "Assign physics material library and select materials to use for the character")
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_maximumSlopeAngle,
"Maximum Slope Angle", "Maximum angle of slopes on which the controller can walk")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 1.0f)
->Attribute(AZ::Edit::Attributes::Max, 89.0f)
->Attribute(AZ::Edit::Attributes::Suffix, " degrees")
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_stepHeight,
"Step Height", "Affects the height of steps the character controller will be able to traverse")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_minimumMovementDistance,
"Minimum Movement Distance", "To avoid jittering, the controller will not attempt to move distances below this")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.001f)
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_maximumSpeed,
"Maximum Speed", "If the accumulated requested velocity for a tick exceeds this magnitude, it will be clamped")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 1.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterConfiguration::m_colliderTag,
"Collider Tag", "Used to identify the collider associated with the character controller")
;
}
}
}
} // Physics
@@ -0,0 +1,134 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/WorldBody.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
namespace Physics
{
class Character;
class CharacterColliderNodeConfiguration
{
public:
AZ_RTTI(CharacterColliderNodeConfiguration, "{C16F3301-0979-400C-B734-692D83755C39}");
AZ_CLASS_ALLOCATOR_DECL
virtual ~CharacterColliderNodeConfiguration() = default;
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_name;
ShapeConfigurationList m_shapes;
};
class CharacterColliderConfiguration
{
public:
AZ_RTTI(CharacterColliderConfiguration, "{4DFF1434-DF5B-4ED5-BE0F-D3E66F9B331A}");
AZ_CLASS_ALLOCATOR_DECL
virtual ~CharacterColliderConfiguration() = default;
CharacterColliderNodeConfiguration* FindNodeConfigByName(const AZStd::string& nodeName) const;
AZ::Outcome<size_t> FindNodeConfigIndexByName(const AZStd::string& nodeName) const;
void RemoveNodeConfigByName(const AZStd::string& nodeName);
static void Reflect(AZ::ReflectContext* context);
AZStd::vector<CharacterColliderNodeConfiguration> m_nodes;
};
/// Information required to create the basic physics representation of a character.
class CharacterConfiguration
: public WorldBodyConfiguration
{
public:
AZ_CLASS_ALLOCATOR(CharacterConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(CharacterConfiguration, "{58D5A6CA-113B-4AC3-8D53-239DB0C4E240}");
virtual ~CharacterConfiguration() = default;
static void Reflect(AZ::ReflectContext* context);
AzPhysics::CollisionGroups::Id m_collisionGroupId; ///< Which layers does this character collide with.
AzPhysics::CollisionLayer m_collisionLayer; ///< Which collision layer is this character on.
MaterialSelection m_materialSelection; ///< Material selected from library for the body associated with the character.
AZ::Vector3 m_upDirection = AZ::Vector3::CreateAxisZ(); ///< Up direction for character orientation and step behavior.
float m_maximumSlopeAngle = 30.0f; ///< The maximum slope on which the character can move, in degrees.
float m_stepHeight = 0.5f; ///< Affects what size steps the character can climb.
float m_minimumMovementDistance = 0.001f; ///< To avoid jittering, the controller will not attempt to move distances below this.
float m_maximumSpeed = 100.0f; ///< If the accumulated requested velocity for a tick exceeds this magnitude, it will be clamped.
AZStd::string m_colliderTag; ///< Used to identify the collider associated with the character controller.
};
/// Basic implementation of common character-style needs as a WorldBody. Is not a full-functional ship-ready
/// all-purpose character controller implementation. This class just abstracts some common functionality amongst
/// typical characters, and is take-it-or-leave it style; useful as a starting point or reference.
class Character
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(Character, AZ::SystemAllocator, 0);
AZ_RTTI(Character, "{962E37A1-3401-4672-B896-0A6157CFAC97}", WorldBody);
~Character() override = default;
virtual AZ::Vector3 GetBasePosition() const = 0;
virtual void SetBasePosition(const AZ::Vector3& position) = 0;
virtual void SetRotation(const AZ::Quaternion& rotation) = 0;
virtual AZ::Vector3 GetCenterPosition() const = 0;
virtual float GetStepHeight() const = 0;
virtual void SetStepHeight(float stepHeight) = 0;
virtual AZ::Vector3 GetUpDirection() const = 0;
virtual void SetUpDirection(const AZ::Vector3& upDirection) = 0;
virtual float GetSlopeLimitDegrees() const = 0;
virtual void SetSlopeLimitDegrees(float slopeLimitDegrees) = 0;
virtual float GetMaximumSpeed() const = 0;
virtual void SetMaximumSpeed(float maximumSpeed) = 0;
virtual AZ::Vector3 GetVelocity() const = 0;
virtual void SetCollisionLayer(const AzPhysics::CollisionLayer& layer) = 0;
virtual void SetCollisionGroup(const AzPhysics::CollisionGroup& group) = 0;
virtual AzPhysics::CollisionLayer GetCollisionLayer() const = 0;
virtual AzPhysics::CollisionGroup GetCollisionGroup() const = 0;
virtual AZ::Crc32 GetColliderTag() const = 0;
/// Queues up a request to apply a velocity to the character.
/// All requests received during a tick are accumulated (so for example, the effects of animation and gravity
/// can be applied in two separate requests), and a movement with the accumulated velocity is performed once
/// per tick, prior to the physics update.
/// Obstacles may prevent the actual movement from exactly matching the requested movement.
/// @param velocity The velocity to be added to the accumulated requests.
virtual void AddVelocity(const AZ::Vector3& velocity) = 0;
/// Applies the queued velocity requests and zeros the accumulated requested velocity.
/// The expected usage is for this function to be called internally by the physics system once per tick,
/// so that the cumulative result of multiple movement effects (e.g. animation, gravity, pseudo-impulses etc)
/// can be combined from separate calls to AddVelocity. Accumulating the requests avoids performing
/// multiple expensive character updates, and avoids any effects from the order of requests within a tick.
/// Users who wish to add a new movement effect should generally just be able to use AddVelocity, and
/// rely on the existing physics system call to ApplyRequestedVelocity.
/// @param deltaTime The duration over which to apply the accumulated requested velocity.
virtual void ApplyRequestedVelocity(float deltaTime) = 0;
virtual void AttachShape(AZStd::shared_ptr<Physics::Shape> shape) = 0;
};
} // namespace Physics
@@ -0,0 +1,93 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
namespace AZ
{
class Vector3;
}
namespace Physics
{
class Character;
/// Messages serviced by character controllers.
class CharacterRequests
: public AZ::ComponentBus
{
public:
virtual ~CharacterRequests() = default;
/// Gets the base position of the character.
virtual AZ::Vector3 GetBasePosition() const = 0;
/// Directly moves (teleports) the character to a new base position.
/// @param position The new base position for the character.
virtual void SetBasePosition(const AZ::Vector3& position) = 0;
/// Gets the position of the center of the character.
virtual AZ::Vector3 GetCenterPosition() const = 0;
/// Gets the step height (the parameter which affects how high the character can step).
virtual float GetStepHeight() const = 0;
/// Sets the step height (the parameter which affects how high the character can step).
/// @param stepHeight The new value for the step height parameter.
virtual void SetStepHeight(float stepHeight) = 0;
/// Gets the character's up direction (the direction used by various controller logic, for example stepping).
virtual AZ::Vector3 GetUpDirection() const = 0;
/// Sets the character's up direction (the direction used by various controller logic, for example stepping).
/// @param upDirection The new value for the up direction.
virtual void SetUpDirection(const AZ::Vector3& upDirection) = 0;
/// Gets the maximum slope which the character can climb, in degrees.
virtual float GetSlopeLimitDegrees() const = 0;
/// Sets the maximum slope which the character can climb, in degrees.
/// @param slopeLimitDegrees the new slope limit value (in degrees, should be between 0 and 90).
virtual void SetSlopeLimitDegrees(float slopeLimitDegrees) = 0;
/// Gets the maximum speed.
/// If the accumulated requested velocity for the character exceeds this magnitude, it will be clamped.
virtual float GetMaximumSpeed() const = 0;
/// Sets the maximum speed.
/// If the accumulated requested velocity for the character exceeds this magnitude, it will be clamped.
/// Values below 0 will be treated as 0.
/// @param maximumSpeed The new value for the maximum speed.
virtual void SetMaximumSpeed(float maximumSpeed) = 0;
/// Gets the observed velocity of the character, which may differ from the desired velocity if the character is obstructed.
virtual AZ::Vector3 GetVelocity() const = 0;
/// Queues up a request to apply a velocity to the character.
/// All requests received during a tick are accumulated (so for example, the effects of animation and gravity
/// can be applied in two separate requests), and a movement with the accumulated velocity is performed once
/// per tick, prior to the physics update.
/// Obstacles may prevent the actual movement from exactly matching the requested movement.
virtual void AddVelocity(const AZ::Vector3& velocity) = 0;
/// Check if there is a character physics component present.
/// Return true in the request handler implementation in order for things like the animation system to work properly.
virtual bool IsPresent() const { return false; }
/// Gets a pointer to the Character object owned by the controller.
virtual Character* GetCharacter() = 0;
};
using CharacterRequestBus = AZ::EBus<CharacterRequests>;
} // namespace Physics
@@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <AzFramework/Physics/Ragdoll.h>
namespace AzFramework
{
class CharacterPhysicsDataRequests
: public AZ::ComponentBus
{
public:
virtual ~CharacterPhysicsDataRequests() = default;
virtual bool GetRagdollConfiguration(Physics::RagdollConfiguration& config) const = 0;
virtual Physics::RagdollState GetBindPose(const Physics::RagdollConfiguration& config) const = 0;
virtual AZStd::string GetParentNodeName(const AZStd::string& childName) const = 0;
};
using CharacterPhysicsDataRequestBus = AZ::EBus<CharacterPhysicsDataRequests>;
class CharacterPhysicsDataNotifications
: public AZ::ComponentBus
{
public:
virtual ~CharacterPhysicsDataNotifications() = default;
virtual void OnRagdollConfigurationReady() = 0;
};
using CharacterPhysicsDataNotificationBus = AZ::EBus<CharacterPhysicsDataNotifications>;
} // namespace AzFramework
@@ -0,0 +1,326 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/ClassConverters.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
#include <AzFramework/Physics/Material.h>
#include <AzFramework/Physics/Ragdoll.h>
#include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/utils.h>
namespace Physics
{
namespace ClassConverters
{
bool RagdollNodeConfigConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() < 2)
{
// this conversion is to deal with m_shapes in the RagdollNodeConfiguration changing from
// AZStd::vector<AZStd::shared_ptr<ShapeConfiguration>> to
// AZStd::vector<AZStd::pair<AZStd::shared_ptr<ColliderConfiguration>, AZStd::shared_ptr<ShapeConfiguration>>>,
// and the collider related information from ShapeConfiguration moving to ColliderConfiguration
const int shapesIndex = classElement.FindElement(AZ_CRC("shapes", 0x93dba512));
if (shapesIndex != -1)
{
AZ::SerializeContext::DataElementNode& shapesElement = classElement.GetSubElement(shapesIndex);
// copy the old shape config data before removing the original vector
AZStd::vector<AZ::SerializeContext::DataElementNode> shapesCopy;
const int numSubElements = shapesElement.GetNumSubElements();
shapesCopy.reserve(numSubElements);
for (int i = 0; i < numSubElements; i++)
{
AZ::SerializeContext::DataElementNode& sharedPtrElement = shapesElement.GetSubElement(i);
if (sharedPtrElement.GetNumSubElements() > 0)
{
AZ::SerializeContext::DataElementNode& shape = sharedPtrElement.GetSubElement(0);
shapesCopy.push_back(shape);
}
}
// remove the old vector
classElement.RemoveElement(shapesIndex);
// add a new vector in the new format
const int newShapesIndex = classElement.AddElement<ShapeConfigurationList>(context, "shapes");
if (newShapesIndex != -1)
{
AZ::SerializeContext::DataElementNode& newShapesElement = classElement.GetSubElement(newShapesIndex);
// convert the old shapes into the new format and add to the vector
for (AZ::SerializeContext::DataElementNode shape : shapesCopy)
{
const int pairIndex = newShapesElement.AddElementWithData<ShapeConfigurationPair>(context, "element", ShapeConfigurationPair());
AZ::SerializeContext::DataElementNode& pairElement = newShapesElement.GetSubElement(pairIndex);
ColliderConfiguration colliderConfig;
if (AZ::SerializeContext::DataElementNode* baseClassNode = shape.FindSubElement(AZ_CRC("BaseClass1", 0xd4925735)))
{
baseClassNode->FindSubElementAndGetData(AZ_CRC("Trigger", 0x1a6b0f5d), colliderConfig.m_isTrigger);
baseClassNode->FindSubElementAndGetData(AZ_CRC("Position", 0x462ce4f5), colliderConfig.m_position);
baseClassNode->FindSubElementAndGetData(AZ_CRC("Rotation", 0x297c98f1), colliderConfig.m_rotation);
baseClassNode->FindSubElementAndGetData(AZ_CRC("CollisionLayer", 0x39931633), colliderConfig.m_collisionLayer);
}
shape.RemoveElementByName(AZ_CRC("BaseClass1", 0xd4925735));
pairElement.GetSubElement(0).AddElementWithData<ColliderConfiguration>(context, "element", colliderConfig);
pairElement.GetSubElement(1).AddElement(shape);
}
}
}
}
// Don't remove the 'shapes' element here, even though it got removed with v3. The version converter converts elements bottom-up
// which means the ragdoll node configs get converted before the ragdoll config. If we remove the shapes element here, we would
// not be able to pass the shapes over to the character collider config. The ragdoll config version converter takes care of this.
//if (classElement.GetVersion() < 3)
//{
// classElement.RemoveElementByName(AZ_CRC("shapes", 0x93dba512));
//}
// Version 4 adds visibility settings to hide rigid body settings that aren't relevant for the animation editor.
if (classElement.GetVersion() < 4)
{
const int rigidBodyConfigIndex = classElement.FindElement(AZ_CRC("RigidBodyConfiguration", 0x152d8d79));
if (rigidBodyConfigIndex != -1)
{
AZ::SerializeContext::DataElementNode& rigidBodyConfigElement = classElement.GetSubElement(rigidBodyConfigIndex);
// in the animation editor we want to show inertia, damping, sleep, interpolation, gravity and CCD properties
// so the value should be (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 7) = 190
rigidBodyConfigElement.AddElementWithData<AZ::u16>(context, "Property Visibility Flags", 190);
}
}
return true;
}
bool RagdollConfigConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() < 3)
{
CharacterColliderConfiguration newColliderConfig;
AZ::SerializeContext::DataElementNode* ragdollNodeConfig = classElement.FindSubElement(AZ_CRC("nodes", 0x1d3d05fc));
if (ragdollNodeConfig)
{
int numNodes = ragdollNodeConfig->GetNumSubElements();
for (int i = 0; i < numNodes; ++i)
{
AZ::SerializeContext::DataElementNode& nodeElement = ragdollNodeConfig->GetSubElement(i);
AZStd::string name;
AZ::SerializeContext::DataElementNode* baseClass1 = nodeElement.FindSubElement(AZ_CRC("BaseClass1", 0xd4925735));
if (baseClass1)
{
AZ::SerializeContext::DataElementNode* baseBaseClass1 = baseClass1->FindSubElement(AZ_CRC("BaseClass1", 0xd4925735));
if (baseBaseClass1 && baseBaseClass1->FindSubElementAndGetData<AZStd::string>(AZ_CRC("name", 0x5e237e06), name))
{
ShapeConfigurationList shapes;
if (nodeElement.FindSubElementAndGetData<ShapeConfigurationList>(AZ_CRC("shapes", 0x93dba512), shapes))
{
CharacterColliderNodeConfiguration newColliderNodeConfig;
newColliderNodeConfig.m_name = name;
newColliderNodeConfig.m_shapes = shapes;
newColliderConfig.m_nodes.push_back(newColliderNodeConfig);
}
}
}
nodeElement.RemoveElementByName(AZ_CRC("shapes", 0x93dba512));
}
}
classElement.AddElementWithData(context, "colliders", newColliderConfig);
}
return true;
}
bool MaterialLibraryAssetConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() <= 1)
{
AZStd::vector<MaterialFromAssetConfiguration> newConfiguration;
auto oldConfigurationsListDataNode = AZ::Utils::FindDescendantElements(context, classElement, { AZ_CRC("Properties", 0x87c331c7) });
for (auto dataElement : oldConfigurationsListDataNode)
{
int elementsCount = dataElement->GetNumSubElements();
for (int i = 0; i < elementsCount; ++i)
{
MaterialConfiguration oldConfiguration;
auto oldConfigurationDataNode = dataElement->GetSubElement(i);
if (!oldConfigurationDataNode.GetDataHierarchy(context, oldConfiguration))
{
return false;
}
MaterialId oldId;
if (auto oldIdNode = oldConfigurationDataNode.FindSubElement(AZ_CRC("UID", 0x539b0606)))
{
oldIdNode->GetData<MaterialId>(oldId);
}
MaterialFromAssetConfiguration configuration;
configuration.m_configuration = oldConfiguration;
configuration.m_id = oldId;
newConfiguration.push_back(configuration);
}
}
classElement.RemoveElementByName(AZ_CRC("Properties", 0x87c331c7));
if (classElement.AddElementWithData(context, "Properties", newConfiguration) == -1)
{
return false;
}
}
return true;
}
bool ColliderConfigurationConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& dataElement)
{
// version 1->2
if (dataElement.GetVersion() <= 1)
{
// Convert collision group to group id
dataElement.RemoveElementByName(AZ_CRC("CollisionGroup", 0xb08873ec));
dataElement.AddElement<AzPhysics::CollisionGroups::Id>(context, "CollisionGroupId");
}
// version 2->3
if (dataElement.GetVersion() <= 2)
{
// Force all new colliders to have exclusive shapes
dataElement.RemoveElementByName(AZ_CRC("Exclusive", 0x012318fc));
dataElement.AddElementWithData<bool>(context, "Exclusive", true);
}
// version 3->4
if (dataElement.GetVersion() <= 3)
{
const int elementIndex = dataElement.FindElement(AZ_CRC("Trigger", 0x1a6b0f5d));
if (elementIndex >= 0)
{
bool isTrigger = false;
AZ::SerializeContext::DataElementNode& triggerElement = dataElement.GetSubElement(elementIndex);
const bool found = triggerElement.GetData<bool>(isTrigger);
if (found && isTrigger)
{
// Version 4 added "InSceneQueries" field set to true by default.
// The field is applicable to both trigger and simulated shapes.
// However before all trigger shapes were always invisible to scene queries.
// Setting "In Scene Queries" to false for all existing triggers to avoid breaking the existing content.
const int idx = dataElement.AddElement<bool>(context, "InSceneQueries");
if (idx != -1)
{
if (!dataElement.GetSubElement(idx).SetData<bool>(context, false))
{
return false;
}
}
}
}
}
return true;
}
bool MaterialSelectionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& dataElement)
{
bool success = true;
if (dataElement.GetVersion() <= 1)
{
Physics::MaterialId materialId;
success = dataElement.FindSubElementAndGetData(AZ_CRC("MaterialId", 0x9360e002), materialId);
if (success)
{
success = success && dataElement.RemoveElementByName(AZ_CRC("MaterialId", 0x9360e002));
success = success && dataElement.AddElementWithData(context, "MaterialIds", AZStd::vector<Physics::MaterialId> { materialId });
}
}
return success;
}
bool RigidBodyVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
if (classElement.GetVersion() <= 1)
{
const int elementIndex = classElement.FindElement(AZ_CRC("Centre of mass offset", 0x1e569a45));
if (elementIndex >= 0)
{
AZ::Vector3 existingCenterOfMassOffset;
AZ::SerializeContext::DataElementNode& centerOfMassElement = classElement.GetSubElement(elementIndex);
const bool found = centerOfMassElement.GetData<AZ::Vector3>(existingCenterOfMassOffset);
if (found && !existingCenterOfMassOffset.IsZero())
{
// An existing center of mass (COM) offset value was specified for this rigid body.
// Version 2 includes a new m_computeCenterOfMass boolean flag to specify the automatic calculation of COM.
// In this case set m_computeCenterOfMass to false so that the existing center of mass offset value is utilized correctly.
const int idx = classElement.AddElement<bool>(context, "Compute COM");
if (idx != -1)
{
if (!classElement.GetSubElement(idx).SetData<bool>(context, false))
{
return false;
}
}
}
}
}
if (classElement.GetVersion() <= 2)
{
const int elementIndex = classElement.FindElement(AZ_CRC("Mass", 0x6c035b66));
if (elementIndex >= 0)
{
float existingMass = 0;
AZ::SerializeContext::DataElementNode& massElement = classElement.GetSubElement(elementIndex);
const bool found = massElement.GetData<float>(existingMass);
if (found && existingMass > 0)
{
// Keeping the existing mass and disabling auto-compute of the mass for this rigid body.
// Version 3 includes a new m_computeMass boolean flag to specify the automatic calculation of mass.
const int idx = classElement.AddElement<bool>(context, "Compute Mass");
if (idx != -1)
{
if (!classElement.GetSubElement(idx).SetData<bool>(context, false))
{
return false;
}
}
}
}
}
return true;
}
} // namespace ClassConverters
} // namespace Physics
@@ -0,0 +1,27 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Physics
{
namespace ClassConverters
{
bool RagdollNodeConfigConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
bool RagdollConfigConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
bool MaterialLibraryAssetConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
bool ColliderConfigurationConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
bool MaterialSelectionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
bool RigidBodyVersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
} // namespace ClassConverters
} // namespace Physics
@@ -0,0 +1,27 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
namespace Physics
{
/// Events dispatched by a ColliderComponent.
/// A ColliderComponent describes the shape of an entity to the physics system.
class ColliderComponentEvents
: public AZ::ComponentBus
{
public:
virtual void OnColliderChanged() {}
};
using ColliderComponentEventBus = AZ::EBus<ColliderComponentEvents>;
} // namespace Physics
@@ -0,0 +1,305 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/CollisionBus.h>
//This bit is defined in the TouchBending Gem wscript.
//Make sure the bit has a valid value.
#ifdef TOUCHBENDING_LAYER_BIT
#if (TOUCHBENDING_LAYER_BIT < 1) || (TOUCHBENDING_LAYER_BIT > 63)
#error Invalid Bit Definition For the TouchBending Layer Bit
#endif
#endif //#ifdef TOUCHBENDING_LAYER_BIT
namespace AzPhysics
{
AZ_CLASS_ALLOCATOR_IMPL(CollisionGroup, AZ::SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(CollisionGroups, AZ::SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(CollisionGroups::Id, AZ::SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(CollisionGroups::Preset, AZ::SystemAllocator, 0);
const CollisionGroup CollisionGroup::None = 0x0000000000000000ULL;
const CollisionGroup CollisionGroup::All = 0xFFFFFFFFFFFFFFFFULL;
#ifdef TOUCHBENDING_LAYER_BIT
const CollisionGroup CollisionGroup::All_NoTouchBend = CollisionGroup::All.GetMask() & ~CollisionLayer::TouchBend.GetMask();
#endif
void CollisionGroup::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CollisionGroup>()
->Version(1)
->Field("Mask", &CollisionGroup::m_mask)
;
}
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<AzPhysics::CollisionGroup>("CollisionGroup")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "AzPhysics")
->Constructor<const AZStd::string>()
;
}
}
void CollisionGroups::Reflect(AZ::ReflectContext* context)
{
CollisionGroups::Id::Reflect(context);
CollisionGroups::Preset::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CollisionGroups>()
->Version(1)
->Field("GroupPresets", &CollisionGroups::m_groups)
;
}
}
void CollisionGroups::Id::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CollisionGroups::Id>()
->Version(1)
->Field("GroupId", &CollisionGroups::Id::m_id)
;
}
}
void CollisionGroups::Preset::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CollisionGroups::Preset>()
->Version(1)
->Field("Id", &CollisionGroups::Preset::m_id)
->Field("Name", &CollisionGroups::Preset::m_name)
->Field("Group", &CollisionGroups::Preset::m_group)
->Field("ReadOnly", &CollisionGroups::Preset::m_readOnly)
;
}
}
bool CollisionGroups::Preset::operator==(const Preset& other) const
{
return m_readOnly == other.m_readOnly &&
m_group == other.m_group &&
m_name == other.m_name &&
m_id == other.m_id
;
}
bool CollisionGroups::Preset::operator!=(const Preset& other) const
{
return !(*this == other);
}
CollisionGroup::CollisionGroup(AZ::u64 mask)
: m_mask(mask)
{
}
CollisionGroup::CollisionGroup(const AZStd::string& groupName)
{
CollisionGroup group;
Physics::CollisionRequestBus::BroadcastResult(group, &Physics::CollisionRequests::GetCollisionGroupByName, groupName);
m_mask = group.GetMask();
}
void CollisionGroup::SetLayer(CollisionLayer layer, bool set)
{
if (set)
{
m_mask |= 1ULL << layer.GetIndex();
}
else
{
m_mask &= ~(1ULL << layer.GetIndex());
}
}
bool CollisionGroup::IsSet(CollisionLayer layer) const
{
return (m_mask & layer.GetMask()) != 0;
}
AZ::u64 CollisionGroup::GetMask() const
{
return m_mask;
}
bool CollisionGroup::operator!=(const CollisionGroup& collisionGroup) const
{
return collisionGroup.m_mask != m_mask;
}
bool CollisionGroup::operator==(const CollisionGroup& collisionGroup) const
{
return collisionGroup.m_mask == m_mask;
}
CollisionGroups::Id CollisionGroups::CreateGroup(const AZStd::string& name, CollisionGroup group, Id id, bool readOnly)
{
Preset preset;
preset.m_id = id;
preset.m_name = name;
preset.m_group = group;
preset.m_readOnly = readOnly;
m_groups.push_back(preset);
return preset.m_id;
}
void CollisionGroups::DeleteGroup(Id id)
{
if (!id.m_id.IsNull())
{
auto last = AZStd::remove_if(m_groups.begin(), m_groups.end(), [id](const Preset& preset)
{
return preset.m_id == id;
});
m_groups.erase(last);
}
}
CollisionGroup CollisionGroups::FindGroupById(CollisionGroups::Id id) const
{
auto found = AZStd::find_if(m_groups.begin(), m_groups.end(), [id](const Preset& preset)
{
return preset.m_id == id;
});
if (found != m_groups.end())
{
return found->m_group;
}
return CollisionGroup::All;
}
CollisionGroup CollisionGroups::FindGroupByName(const AZStd::string& groupName) const
{
CollisionGroup group = CollisionGroup::All;
TryFindGroupByName(groupName, group);
return group;
}
bool CollisionGroups::TryFindGroupByName(const AZStd::string& groupName, CollisionGroup& group) const
{
auto found = AZStd::find_if(m_groups.begin(), m_groups.end(), [groupName](const Preset& preset)
{
return preset.m_name == groupName;
});
if (found != m_groups.end())
{
group = found->m_group;
return true;
}
AZ_Warning("CollisionGroups", false, "Could not find collision group:%s. Does it exist in the physx configuration window?", groupName.c_str());
return false;
}
CollisionGroups::Id CollisionGroups::FindGroupIdByName(const AZStd::string& groupName) const
{
auto found = AZStd::find_if(m_groups.begin(), m_groups.end(), [groupName](const Preset& preset)
{
return preset.m_name == groupName;
});
if (found != m_groups.end())
{
return found->m_id;
}
return CollisionGroups::Id();
}
AZStd::string CollisionGroups::FindGroupNameById(Id id) const
{
auto found = AZStd::find_if(m_groups.begin(), m_groups.end(), [id](const Preset& preset)
{
return preset.m_id.m_id == id.m_id;
});
if (found != m_groups.end())
{
return found->m_name;
}
return "";
}
void CollisionGroups::SetGroupName(CollisionGroups::Id id, const AZStd::string& groupName)
{
auto found = AZStd::find_if(m_groups.begin(), m_groups.end(), [id](const Preset& preset)
{
return preset.m_id.m_id == id.m_id;
});
if (found != m_groups.end())
{
found->m_name = groupName;
}
}
void CollisionGroups::SetLayer(Id id, CollisionLayer layer, bool enabled)
{
auto found = AZStd::find_if(m_groups.begin(), m_groups.end(), [id](const Preset& preset)
{
return preset.m_id.m_id == id.m_id;
});
if (found != m_groups.end())
{
found->m_group.SetLayer(layer, enabled);
}
}
const AZStd::vector<CollisionGroups::Preset>& CollisionGroups::GetPresets() const
{
return m_groups;
}
bool CollisionGroups::operator==(const CollisionGroups& other) const
{
return m_groups == other.m_groups;
}
bool CollisionGroups::operator!=(const CollisionGroups& other) const
{
return !(*this == other);
}
CollisionGroup operator|(CollisionLayer layer1, CollisionLayer layer2)
{
CollisionGroup group = CollisionGroup::None;
group.SetLayer(layer1, true);
group.SetLayer(layer2, true);
return group;
}
CollisionGroup operator|(CollisionGroup otherGroup, CollisionLayer layer)
{
CollisionGroup group = otherGroup;
group.SetLayer(layer, true);
return group;
}
}
@@ -0,0 +1,181 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
namespace AZ
{
class ReflectContext;
}
namespace AzPhysics
{
//! This class represents the layers a collider should collide with.
//! For two colliders to collide, each layer must be present
//! in the other colliders group.
class CollisionGroup
{
public:
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(CollisionGroup, "{E6DA080B-7ED1-4135-A78C-A6A5E495A43E}");
static void Reflect(AZ::ReflectContext* context);
static const CollisionGroup None; //!< Collide with nothing
static const CollisionGroup All; //!< Collide with everything
static const CollisionGroup All_NoTouchBend; //!< Collide with everything, except Touch Bendable Vegetation.
//! Construct a Group with the given bitmask.
//! The each bit in the bitmask corresponds to a CollisionLayer.
//! @param mask The bitmask to assign to the group.
CollisionGroup(AZ::u64 mask = All.GetMask());
//! Construct a Group with the given name.
//! This will lookup the group name to retrieve the group mask. If not found, CollisionGroup::All is set.
//! @param groupName The name of the group to look up the group mask.
CollisionGroup(const AZStd::string& groupName);
//! Enable/Disable a CollisionLayer on this group.
//! @param layer The layer to modify.
//! @param set If true, toggle the layer ON for this group, otherwise toggle OFF.
void SetLayer(CollisionLayer layer, bool set);
//! Check is the given CollisionLayer is ON in this group.
//! @param layer The layer to check.
//! @return Returns true if the given layer is ON in this group, otherwise returns false.
bool IsSet(CollisionLayer layer) const;
//! Get the groups bitmask.
AZ::u64 GetMask() const;
bool operator==(const CollisionGroup& collisionGroup) const;
bool operator!=(const CollisionGroup& collisionGroup) const;
private:
AZ::u64 m_mask;
};
//! Overloads for collision layers and groups.
//! Example usage:
//! @code{.cpp}
//! CollisionGroup group1 = CollisionLayer(0) | CollisionLayer(1) | CollisionLayer(2).
//! @endcode
CollisionGroup operator|(CollisionLayer layer1, CollisionLayer layer2);
CollisionGroup operator|(CollisionGroup group, CollisionLayer layer);
//! Collision groups can be defined and edited in the PhysXConfiguration window.
//! The idea is that collision groups are authored there, and then assigned to components via the
//! edit context by reflecting Physics::CollisionGroups::Id, or alternatively can be retrieved by
//! name from the CollisionConfiguration.
class CollisionGroups
{
public:
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(CollisionGroups, "{309B0B28-F51F-48E2-972E-DA7618ED7249}");
static void Reflect(AZ::ReflectContext* context);
//! Id of a collision group. Mainly used by the editor to assign a collision
//! group to an editor component.
class Id
{
public:
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(Id, "{DFED4FE5-2292-4F07-A318-41C68DAEFE9C}");
static void Reflect(AZ::ReflectContext* context);
bool operator==(const Id& other) const { return m_id == other.m_id; }
bool operator!=(const Id& other) const { return m_id != other.m_id; }
bool operator<(const Id& other) const { return m_id < other.m_id; }
static Id Create() { Id id; id.m_id = AZ::Uuid::Create(); return id; }
AZ::Uuid m_id = AZ::Uuid::CreateNull();
};
//! A collision group defined with a name and an id which
//! can be edited in the editor.
struct Preset
{
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(Preset, "{032D1485-60A4-45B6-8D74-5B38C929B066}");
static void Reflect(AZ::ReflectContext* context);
Id m_id;
AZStd::string m_name;
CollisionGroup m_group;
bool m_readOnly;
bool operator==(const Preset& other) const;
bool operator!=(const Preset& other) const;
};
CollisionGroups() = default;
//! Create a CollisionGroup.
//! @param name The name to give the group.
//! @param group The CollisionGroup data to set to this group.
//! @param id A CollisionGroup::Id to assign to the group. By default will create a new Id.
//! @param readOnly Mark the group as read only. Default false.
//! @return Returns the CollisionGroup::Id associated with the new Group.
Id CreateGroup(const AZStd::string& name, CollisionGroup group, Id id = Id::Create(), bool readOnly = false);
//! Delete a group with the given CollisionGroup::Id.
void DeleteGroup(Id id);
//! Set the name of a group with the given CollisionGroup::Id.
void SetGroupName(Id id, const AZStd::string& groupName);
//! Set a CollisionLayer ON or OFF on in the given CollisionGroup::Id.
//! This will verify id is valid.
//! @param id The group id to affect.
//! @param layer The CollisionLayer to turn ON or OFF.
//! @param enabled If true toggle the given CollisionLayer to ON, otherwise OFF
void SetLayer(Id id, CollisionLayer layer, bool enabled);
// Get a CollisionGroup by its id.
//! @param id The CollisionGroup::Id to find.
//! @return Returns the requested CollisionGroup, otherwise return CollisionGroup::All.
CollisionGroup FindGroupById(Id id) const;
// Get a CollisionGroup by its name.
//! @param groupName The CollisionGroup name to find.
//! @return Returns the requested CollisionGroup, otherwise return CollisionGroup::All.
CollisionGroup FindGroupByName(const AZStd::string& groupName) const;
// Get a CollisionGroup by its name.
//! @param groupName The CollisionGroup name to find.
//! @param group [Out] The requested CollisionGroup if successful. Otherwise group is unchanged.
//! @return Returns true if located the requested CollisionGroup, otherwise false.
bool TryFindGroupByName(const AZStd::string& groupName, CollisionGroup& group) const;
//! Retrieve the CollisionGroup::Id of the request name.
//! @param groupName The name of the CollisionGroup to lookup.
//! @return Returns the request CollisionGroup::Id otherwise returns a 'null id'.
Id FindGroupIdByName(const AZStd::string& groupName) const;
//! Retrieve the name of the requested CollisionGroup::Id.
//! @param id The CollisionGroup::Id to preform a name lookup for.
//! @return The name of the CollisionGroup, otherwise returns an empty string.
AZStd::string FindGroupNameById(Id id) const;
//! Retrieve a list of all current Presets (see CollisionGroup::Preset).
const AZStd::vector<Preset>& GetPresets()const;
bool operator==(const CollisionGroups& other) const;
bool operator!=(const CollisionGroups& other) const;
private:
AZStd::vector<Preset> m_groups;
};
}
@@ -0,0 +1,174 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/Collision/CollisionLayers.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/CollisionBus.h>
//This bit is defined in the TouchBending Gem wscript.
//Make sure the bit has a valid value.
#ifdef TOUCHBENDING_LAYER_BIT
#if (TOUCHBENDING_LAYER_BIT < 1) || (TOUCHBENDING_LAYER_BIT > 63)
#error Invalid Bit Definition For the TouchBending Layer Bit
#endif
#endif //#ifdef TOUCHBENDING_LAYER_BIT
namespace AzPhysics
{
AZ_CLASS_ALLOCATOR_IMPL(CollisionLayer, AZ::SystemAllocator, 0);
AZ_CLASS_ALLOCATOR_IMPL(CollisionLayers, AZ::SystemAllocator, 0);
const CollisionLayer CollisionLayer::Default = 0;
#ifdef TOUCHBENDING_LAYER_BIT
const CollisionLayer CollisionLayer::TouchBend = TOUCHBENDING_LAYER_BIT;
#endif
void CollisionLayer::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CollisionLayer>()
->Version(1)
->Field("Index", &CollisionLayer::m_index)
;
}
}
CollisionLayer::CollisionLayer(AZ::u8 index)
: m_index(index)
{
AZ_Assert(m_index < CollisionLayers::MaxCollisionLayers, "Index is too large. Valid values are 0-%d"
, CollisionLayers::MaxCollisionLayers - 1);
}
CollisionLayer::CollisionLayer(const AZStd::string& layerName)
{
CollisionLayer layer;
Physics::CollisionRequestBus::BroadcastResult(layer, &Physics::CollisionRequests::GetCollisionLayerByName, layerName);
m_index = layer.m_index;
}
AZ::u8 CollisionLayer::GetIndex() const
{
return m_index;
}
void CollisionLayer::SetIndex(AZ::u8 index)
{
AZ_Assert(m_index < CollisionLayers::MaxCollisionLayers, "Index is too large. Valid values are 0-%d"
, CollisionLayers::MaxCollisionLayers - 1);
m_index = index;
}
AZ::u64 CollisionLayer::GetMask() const
{
return 1ULL << m_index;
}
bool CollisionLayer::operator==(const CollisionLayer& other) const
{
return m_index == other.m_index;
}
bool CollisionLayer::operator!=(const CollisionLayer& other) const
{
return !(*this == other);
}
void CollisionLayers::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CollisionLayers>()
->Version(1)
->Field("LayerNames", &CollisionLayers::m_names)
;
if (auto* editContext = serializeContext->GetEditContext())
{
editContext->Class<CollisionLayers>("Collision Layers", "List of defined collision layers")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &CollisionLayers::m_names, "Layers", "Names of each collision layer")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}
}
CollisionLayer CollisionLayers::GetLayer(const AZStd::string& layerName) const
{
CollisionLayer layer = CollisionLayer::Default;
TryGetLayer(layerName, layer);
return layer;
}
bool CollisionLayers::TryGetLayer(const AZStd::string& layerName, CollisionLayer& layer) const
{
if (layerName.empty())
{
return false;
}
for (AZ::u8 i = 0; i < m_names.size(); ++i)
{
if (m_names[i] == layerName)
{
layer = CollisionLayer(i);
return true;
}
}
AZ_Warning("CollisionLayers", false, "Could not find collision layer:%s. Does it exist in the physx configuration window?", layerName.c_str());
return false;
}
const AZStd::string& CollisionLayers::GetName(CollisionLayer layer) const
{
return m_names[layer.GetIndex()];
}
const AZStd::array<AZStd::string, CollisionLayers::MaxCollisionLayers>& CollisionLayers::GetNames() const
{
return m_names;
}
void CollisionLayers::SetName(CollisionLayer layer, const AZStd::string& layerName)
{
m_names[layer.GetIndex()] = layerName;
}
void CollisionLayers::SetName(AZ::u64 layerIndex, const AZStd::string& layerName)
{
if (layerIndex >= m_names.size())
{
AZ_Warning("PhysX Collision Layers", false, "Trying to set layer name of layer with invalid index: %d", layerIndex);
return;
}
m_names[layerIndex] = layerName;
}
bool CollisionLayers::operator==(const CollisionLayers& other) const
{
return m_names == other.m_names;
}
bool CollisionLayers::operator!=(const CollisionLayers& other) const
{
return !(*this == other);
}
}
@@ -0,0 +1,114 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
class ReflectContext;
}
namespace AzPhysics
{
//! This class represents which layer a collider exists on.
//! A collider can only exist on a single layer, defined by the index.
//! There is a maximum of 64 layers.
class CollisionLayer
{
public:
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(CollisionLayer, "{5AA459C8-2D92-46D2-9154-ED49EE4FE70E}");
static void Reflect(AZ::ReflectContext* context);
static const CollisionLayer Default; //!< Default collision layer, 0.
static const CollisionLayer TouchBend; //!< Touch Bendable Vegetation collision layer.
//! Construct a layer with the given index.
//! @param index The index of the layer. Must be between 0 - CollisionLayers::MaxCollisionLayers. Default CollisionLayer::Default.
CollisionLayer(AZ::u8 index = Default.GetIndex());
//! Construct a layer with the given name.
//! This will lookup the layer name to retrieve the index. If not found will set the index to CollisionLayer::Default.
//! @param layername The name of the layer.
CollisionLayer(const AZStd::string& layerName);
//! Get the index of this layer.
//! Index will be between 0 - CollisionLayers::MaxCollisionLayers
//! @return The layers index.
AZ::u8 GetIndex() const;
//! Set the index of this layer.
//! @param index The index to set. Must be between 0 - CollisionLayers::MaxCollisionLayers
void SetIndex(AZ::u8 index);
//! Get the Layer index represented as a bitmask.
//! @return A bitmask with the layer index bit toggled on.
AZ::u64 GetMask() const;
bool operator==(const CollisionLayer& other) const;
bool operator!=(const CollisionLayer& other) const;
private:
AZ::u8 m_index;
};
//! Collision layers defined for the project.
class CollisionLayers
{
public:
static const AZ::u8 MaxCollisionLayers = 64;
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(CollisionLayers, "{68E7CB59-29BC-4825-AE99-182D6421EE65}");
static void Reflect(AZ::ReflectContext* context);
CollisionLayers() = default;
//! Get the requested layer.
//! @param name The name of the layer to retrieve.
//! @return The request layer if found, otherwise CollisionLayer::Defualt.
CollisionLayer GetLayer(const AZStd::string& name) const;
//! Get the requested layer.
//! @param name The name of the layer to retrieve.
//! @param layer [OUT] The request layer if found, otherwise layer is left untouched.
//! @return Returns true if the layer was found, otherwise false.
bool TryGetLayer(const AZStd::string& name, CollisionLayer& layer) const;
//! Get the name of the requested layer.
//! @return Returns the name of the requested layer
const AZStd::string& GetName(CollisionLayer layer) const;
//! Get the names of all the layers.
//! @return Returns an array of all the layers names.
const AZStd::array<AZStd::string, MaxCollisionLayers>& GetNames() const;
//! Set the name of the requested layer.
//! @param layer The requested layer to modify.
//! @param layerName The name of the layer.
void SetName(CollisionLayer layer, const AZStd::string& layerName);
//! Set the name of the requested layer by index.
//! Will verify layerIndex is within bounds.
//! @param layerIndex The requested layer index.
//! @param layerName The name of the layer.
void SetName(AZ::u64 layerIndex, const AZStd::string& layerName);
bool operator==(const CollisionLayers& other) const;
bool operator!=(const CollisionLayers& other) const;
private:
AZStd::array<AZStd::string, MaxCollisionLayers> m_names;
};
}
@@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "CollisionBus.h"
#include <AzCore/RTTI/BehaviorContext.h>
namespace Physics
{
void CollisionFilteringRequests::Reflect(AZ::ReflectContext* context)
{
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Physics::CollisionFilteringRequestBus>("CollisionFilteringBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::Preview)
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Event("SetCollisionLayer", &Physics::CollisionFilteringRequestBus::Events::SetCollisionLayer)
->Event("GetCollisionLayerName", &Physics::CollisionFilteringRequestBus::Events::GetCollisionLayerName)
->Event("SetCollisionGroup", &Physics::CollisionFilteringRequestBus::Events::SetCollisionGroup)
->Event("GetCollisionGroupName", &Physics::CollisionFilteringRequestBus::Events::GetCollisionGroupName)
->Event("ToggleCollisionLayer", &Physics::CollisionFilteringRequestBus::Events::ToggleCollisionLayer)
;
}
}
}
@@ -0,0 +1,112 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <AzFramework/Physics/Configuration/CollisionConfiguration.h>
namespace Physics
{
//! CollisionRequests configures global project-level collision filtering settings.
//! This is equivalent to setting values via the UI.
class CollisionRequests
{
public:
AZ_TYPE_INFO(CollisionRequests, "{5A937391-DC65-4E1D-84A6-AE151A1200D1}");
CollisionRequests() = default;
virtual ~CollisionRequests() = default;
// AZ::Interface requires these to be deleted.
CollisionRequests(CollisionRequests&&) = delete;
CollisionRequests& operator=(CollisionRequests&&) = delete;
/// Gets a collision layer by name. The Default layer is returned if the layer name was not found.
virtual AzPhysics::CollisionLayer GetCollisionLayerByName(const AZStd::string& layerName) = 0;
/// Looks up the name of a collision layer
virtual AZStd::string GetCollisionLayerName(const AzPhysics::CollisionLayer& layer) = 0;
/// Tries to find a collision layer which matches layerName.
/// Returns true if it was found and the result is stored in collisionLayer, otherwise false.
virtual bool TryGetCollisionLayerByName(const AZStd::string& layerName, AzPhysics::CollisionLayer& collisionLayer) = 0;
/// Gets a collision group by name. The All group is returned if the group name was not found.
virtual AzPhysics::CollisionGroup GetCollisionGroupByName(const AZStd::string& groupName) = 0;
/// Tries to find a collision group which matches groupName.
/// Returns true if it was found, and the group is stored in collisionGroup, otherwise false.
virtual bool TryGetCollisionGroupByName(const AZStd::string& groupName, AzPhysics::CollisionGroup& collisionGroup) = 0;
/// Looks up a name from a collision group
virtual AZStd::string GetCollisionGroupName(const AzPhysics::CollisionGroup& collisionGroup) = 0;
/// Gets a collision group by id.
virtual AzPhysics::CollisionGroup GetCollisionGroupById(const AzPhysics::CollisionGroups::Id& groupId) = 0;
/// Sets the layer name by index.
virtual void SetCollisionLayerName(int index, const AZStd::string& layerName) = 0;
/// Creates a new collision group preset with corresponding groupName.
virtual void CreateCollisionGroup(const AZStd::string& groupName, const AzPhysics::CollisionGroup& group) = 0;
virtual AzPhysics::CollisionConfiguration GetCollisionConfiguration() = 0;
};
/// Collision requests bus traits. Singleton pattern.
class CollisionRequestsTraits
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
};
using CollisionRequestBus = AZ::EBus<CollisionRequests, CollisionRequestsTraits>;
//! CollisionFilteringRequests configures filtering settings per entity.
class CollisionFilteringRequests
: public AZ::ComponentBus
{
public:
static void Reflect(AZ::ReflectContext* context);
//! Sets the collision layer on an entity.
//! layerName should match a layer defined in the PhysX cConfiguration window.
//! Colliders with a matching colliderTag will be updated. Specify the empty tag to update all colliders.
virtual void SetCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag) = 0;
//! Gets the collision layer name for a collider on an entity
//! If the collision layer can't be found, an empty string is returned.
//! Note: Multiple colliders on an entity are currently not supported.
virtual AZStd::string GetCollisionLayerName() = 0;
//! Sets the collision group on an entity.
//! groupName should match a group defined in the PhysX configuration window.
//! Colliders with a matching colliderTag will be updated. Specify the empty tag to update all colliders.
virtual void SetCollisionGroup(const AZStd::string& groupName, AZ::Crc32 colliderTag) = 0;
//! Gets the collision group name for a collider on an entity.
//! If the collision group can't be found, an empty string is returned.
//! Note: Multiple colliders on an entity are currently not supported.
virtual AZStd::string GetCollisionGroupName() = 0;
//! Toggles a single collision layer on or off on an entity.
//! layerName should match a layer defined in the PhysX configuration window.
//! Colliders with a matching colliderTag will be updated. Specify the empty tag to update all colliders.
virtual void ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) = 0;
};
using CollisionFilteringRequestBus = AZ::EBus<CollisionFilteringRequests>;
} // namespace Physics
@@ -0,0 +1,47 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <AzFramework/Physics/WorldEventhandler.h>
namespace Physics
{
/// CollisionNotifications
/// Bus interface for receiving collision events from a Physics::World
///
/// The bus is addressed by EntityId. Body1 inside collisionEvent will correspond
/// to the eEntity id subscribed to. Body2 will always be the other body colliding with the entity.
class CollisionNotifications
: public AZ::ComponentBus
{
public:
// Ebus Traits. ID'd on body1 entity Id
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const bool EnableEventQueue = true;
using BusIdType = AZ::EntityId;
virtual ~CollisionNotifications() {}
/// Dispatched when two shapes start colliding.
virtual void OnCollisionBegin(const CollisionEvent& /*collisionEvent*/) {}
/// Dispatched when two shapes continue colliding.
virtual void OnCollisionPersist(const CollisionEvent& /*collisionEvent*/) {}
/// Dispatched when two shapes stop colliding.
virtual void OnCollisionEnd(const CollisionEvent& /*collisionEvent*/) {}
};
/// Bus to service the PhysX Trigger Area Component event group.
using CollisionNotificationBus = AZ::EBus<CollisionNotifications>;
} // namespace PhysX
@@ -0,0 +1,53 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/EBus/Event.h>
namespace AzPhysics
{
struct SystemConfiguration;
struct SceneConfiguration;
namespace SystemEvents
{
//! Event that triggers when the physics system configuration has been changed.
//! When triggered the event will send the newly applied SystemConfiguration object.
using OnConfigurationChangedEvent = AZ::Event<const SystemConfiguration*>;
//! Event triggers when the physics system has completed initialization.
//! When triggered the event will send the SystemConfiguration used to initialize the system.
using OnInitializedEvent = AZ::Event<const SystemConfiguration*>;
//! Event triggers when the physics system has completed reinitialization.
using OnReinitializedEvent = AZ::Event<>;
//! Event triggers when the physics system has completed its shutdown.
using OnShutdownEvent = AZ::Event<>;
//! Event triggers at the beginning of the SystemInterface::Simulate call.
//! Parameter is the total time that the physics system will run for during the Simulate call.
using OnPresimulateEvent = AZ::Event<float>;
//! Event triggers at the end of the SystemInterface::Simulate call.
using OnPostsimulateEvent = AZ::Event<>;
//! Event that triggers when the default material library changes.
//! When triggered the event will send the Asset Id of the new material library.
using OnDefaultMaterialLibraryChangedEvent = AZ::Event<const AZ::Data::AssetId&>;
//! Event that triggers when the default scene configuration changes.
//! When triggered the event will send the new default scene configuration.
using OnDefaultSceneConfigurationChangedEvent = AZ::Event<const SceneConfiguration*>;
}
}
@@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/tuple.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
namespace AzPhysics
{
using SceneIndex = AZ::s8;
//! A handle to a Scene within the physics simulation.
//! A SceneHandle is a tuple of a Crc of the scenes name and the index in the Scene list.
using SceneHandle = AZStd::tuple<AZ::Crc32, SceneIndex>;
//! Helper for retrieving the values from the SceneHandle tuple.
//! Example usage
//! @code{ .cpp }
//! SceneHandle someHandle;
//! AZ::Crc32 handleCrc = AZStd::get<SceneHandleValues::Crc>(someHandle);
//! SceneIndex index = AZStd::get<SceneHandleValues::Index>(someHandle);
//! @endcode
enum SceneHandleValues
{
Crc = 0,
Index
};
static constexpr SceneHandle InvalidSceneHandle = { AZ::Crc32(), -1 };
//! Ease of use type for referencing a List of SceneHandle objects.
using SceneHandleList = AZStd::vector<SceneHandle>;
}
@@ -0,0 +1,46 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/Configuration/CollisionConfiguration.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzPhysics
{
AZ_CLASS_ALLOCATOR_IMPL(CollisionConfiguration, AZ::SystemAllocator, 0);
void CollisionConfiguration::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CollisionConfiguration>()
->Version(1)
->Field("Layers", &CollisionConfiguration::m_collisionLayers)
->Field("Groups", &CollisionConfiguration::m_collisionGroups)
;
}
}
bool CollisionConfiguration::operator==(const CollisionConfiguration& other) const
{
return m_collisionLayers == other.m_collisionLayers &&
m_collisionGroups == other.m_collisionGroups
;
}
bool CollisionConfiguration::operator!=(const CollisionConfiguration& other) const
{
return !(*this == other);
}
}
@@ -0,0 +1,49 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
namespace AZ
{
class ReflectContext;
}
namespace AzPhysics
{
//! Collision configuration is a convenience storage class for /ref CollisionLayers and /ref CollisionGroups,
//! as they are frequently used together. It can be retrieved/mutated through
//! the SystemConfiguration (for global settings) and the SceneConfiguration (for scene modifications).
class CollisionConfiguration
{
public:
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(CollisionConfiguration, "{84059477-BF6E-4421-9AC7-A0A3B27DEA40}");
static void Reflect(AZ::ReflectContext* context);
CollisionConfiguration() = default;
CollisionLayers m_collisionLayers;
CollisionGroups m_collisionGroups;
bool operator==(const CollisionConfiguration& other) const;
bool operator!=(const CollisionConfiguration& other) const;
};
}
@@ -0,0 +1,54 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/Configuration/SceneConfiguration.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzPhysics
{
AZ_CLASS_ALLOCATOR_IMPL(SceneConfiguration, AZ::SystemAllocator, 0);
/*static*/ void SceneConfiguration::Reflect(AZ::ReflectContext* context)
{
Physics::WorldConfiguration::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SceneConfiguration>()
->Version(1)
->Field("LegacyConfig", &SceneConfiguration::m_legacyConfiguration)
->Field("LegacyId", &SceneConfiguration::m_legacyId)
->Field("Name", &SceneConfiguration::m_sceneName)
;
}
}
/*static*/ SceneConfiguration SceneConfiguration::CreateDefault()
{
return SceneConfiguration();
}
bool SceneConfiguration::operator==(const SceneConfiguration& other) const
{
return m_legacyId == other.m_legacyId
&& m_sceneName == other.m_sceneName
&& m_legacyConfiguration == other.m_legacyConfiguration
;
}
bool SceneConfiguration::operator!=(const SceneConfiguration& other) const
{
return !(*this == other);
}
}
@@ -0,0 +1,48 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Physics/World.h> //this will be removed with LYN-438.
namespace AZ
{
class ReflectContext;
}
namespace AzPhysics
{
//! Configuration object that contains data to setup a Scene.
struct SceneConfiguration
{
AZ_CLASS_ALLOCATOR_DECL;
AZ_TYPE_INFO(SceneConfiguration, "{4ABF9993-8E52-4E41-B38D-28FD569B4EAF}");
static void Reflect(AZ::ReflectContext* context);
static SceneConfiguration CreateDefault();
// Legacy members Will be removed and replaced with LYN-438 work.
Physics::WorldConfiguration m_legacyConfiguration;
AZ::Crc32 m_legacyId; //use SceneConfiguration::m_SceneName instead
AZStd::string m_sceneName = "DefaultScene"; //!< Name given to the scene.
bool operator==(const SceneConfiguration& other) const;
bool operator!=(const SceneConfiguration& other) const;
};
//! Alias for a list of SceneConfiguration objects, used for the creation of multiple Scenes at once.
using SceneConfigurationList = AZStd::vector<SceneConfiguration>;
}
@@ -0,0 +1,55 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/Configuration/SystemConfiguration.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzPhysics
{
AZ_CLASS_ALLOCATOR_IMPL(SystemConfiguration, AZ::SystemAllocator, 0);
/*static*/ void SystemConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SystemConfiguration>()
->Version(2)
->Field("AutoManageSimulationUpdate", &SystemConfiguration::m_autoManageSimulationUpdate)
->Field("MaxTimestep", &SystemConfiguration::m_maxTimestep)
->Field("FixedTimeStep", &SystemConfiguration::m_fixedTimestep)
->Field("RaycastBufferSize", &SystemConfiguration::m_raycastBufferSize)
->Field("ShapecastBufferSize", &SystemConfiguration::m_shapecastBufferSize)
->Field("OverlapBufferSize", &SystemConfiguration::m_overlapBufferSize)
->Field("CollisionConfig", &SystemConfiguration::m_collisionConfig)
;
}
}
bool SystemConfiguration::operator==(const SystemConfiguration& other) const
{
return m_autoManageSimulationUpdate == other.m_autoManageSimulationUpdate &&
m_raycastBufferSize == other.m_raycastBufferSize &&
m_shapecastBufferSize == other.m_shapecastBufferSize &&
m_overlapBufferSize == other.m_overlapBufferSize &&
AZ::IsClose(m_maxTimestep, other.m_maxTimestep) &&
AZ::IsClose(m_fixedTimestep, other.m_fixedTimestep) &&
m_collisionConfig == other.m_collisionConfig
;
}
bool SystemConfiguration::operator!=(const SystemConfiguration& other) const
{
return !(*this == other);
}
}
@@ -0,0 +1,55 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzFramework/Physics/Configuration/CollisionConfiguration.h>
namespace AZ
{
class ReflectContext;
}
namespace AzPhysics
{
//! Contains global physics settings.
//! Used to initialize the Physics System.
struct SystemConfiguration
{
AZ_CLASS_ALLOCATOR_DECL;
AZ_RTTI(SystemConfiguration, "{24697CAF-AC00-443D-9C27-28D58734A84C}");
static void Reflect(AZ::ReflectContext* context);
SystemConfiguration() = default;
virtual ~SystemConfiguration() = default;
static constexpr float DefaultFixedTimestep = 0.0166667f; //! Value represents 1/60th or 60 FPS.
float m_maxTimestep = 1.f / 20.f; //!< Maximum fixed timestep in seconds to run the physics update.
float m_fixedTimestep = DefaultFixedTimestep; //!< Timestep in seconds to run the physics update. See DefaultFixedTimestep.
AZ::u64 m_raycastBufferSize = 32; //!< Maximum number of hits that will be returned from a raycast.
AZ::u64 m_shapecastBufferSize = 32; //!< Maximum number of hits that can be returned from a shapecast.
AZ::u64 m_overlapBufferSize = 32; //!< Maximum number of overlaps that can be returned from an overlap query.
//! Contains the default global collision layers and groups.
//! Each Physics Scene uses this as a base and will override as needed.
CollisionConfiguration m_collisionConfig;
//! Controls whether the Physics System will self register to the TickBus and call StartSimulation / FinishSimulation on each Scene.
//! Disable this to manually control Physics Scene simulation logic.
bool m_autoManageSimulationUpdate = true;
bool operator==(const SystemConfiguration& other) const;
bool operator!=(const SystemConfiguration& other) const;
};
}
@@ -0,0 +1,55 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/Joint.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Physics
{
const char* JointLimitConfiguration::GetTypeName()
{
return "Base Joint";
}
void JointLimitConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<JointLimitConfiguration>()
->Version(1)
->Field("ParentLocalRotation", &JointLimitConfiguration::m_parentLocalRotation)
->Field("ParentLocalPosition", &JointLimitConfiguration::m_parentLocalPosition)
->Field("ChildLocalRotation", &JointLimitConfiguration::m_childLocalRotation)
->Field("ChildLocalPosition", &JointLimitConfiguration::m_childLocalPosition)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<JointLimitConfiguration>(
"Joint Configuration", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_parentLocalRotation,
"Parent local rotation", "The rotation of the parent joint frame relative to the parent body")
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_parentLocalPosition,
"Parent local position", "The position of the joint in the frame of the parent body")
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_childLocalRotation,
"Child local rotation", "The rotation of the child joint frame relative to the child body")
->DataElement(AZ::Edit::UIHandlers::Default, &JointLimitConfiguration::m_childLocalPosition,
"Child local position", "The position of the joint in the frame of the child body")
;
}
}
}
} // namespace Physics
@@ -0,0 +1,71 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/WorldBody.h>
namespace Physics
{
class JointLimitConfiguration
{
public:
AZ_CLASS_ALLOCATOR(JointLimitConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(JointLimitConfiguration, "{C9B70C4D-22D7-45AB-9B0A-30A4ED5E42DB}");
static void Reflect(AZ::ReflectContext* context);
JointLimitConfiguration() = default;
JointLimitConfiguration(const JointLimitConfiguration&) = default;
virtual ~JointLimitConfiguration() = default;
virtual const char* GetTypeName();
AZ::Quaternion m_parentLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Parent joint frame relative to parent body.
AZ::Vector3 m_parentLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to parent body.
AZ::Quaternion m_childLocalRotation = AZ::Quaternion::CreateIdentity(); ///< Child joint frame relative to child body.
AZ::Vector3 m_childLocalPosition = AZ::Vector3::CreateZero(); ///< Joint position relative to child body.
};
class Joint
{
public:
AZ_CLASS_ALLOCATOR(Joint, AZ::SystemAllocator, 0);
AZ_RTTI(Joint, "{405F517C-E986-4ACB-9606-D5D080DDE987}");
virtual Physics::WorldBody* GetParentBody() const = 0;
virtual Physics::WorldBody* GetChildBody() const = 0;
virtual void SetParentBody(Physics::WorldBody* parentBody) = 0;
virtual void SetChildBody(Physics::WorldBody* childBody) = 0;
virtual const AZStd::string& GetName() const = 0;
virtual void SetName(const AZStd::string& name) = 0;
virtual const AZ::Crc32 GetNativeType() const = 0;
virtual void* GetNativePointer() = 0;
/// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions.
/// @param scale Scale factor for the output display data.
/// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation).
/// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation).
/// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed.
/// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed.
/// @param[out] lineBufferOut Used to define lines to be displayed.
/// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit.
virtual void GenerateJointLimitVisualizationData(
float scale,
AZ::u32 angularSubdivisions,
AZ::u32 radialSubdivisions,
AZStd::vector<AZ::Vector3>& vertexBufferOut,
AZStd::vector<AZ::u32>& indexBufferOut,
AZStd::vector<AZ::Vector3>& lineBufferOut,
AZStd::vector<bool>& lineValidityBufferOut) = 0;
};
} // namespace Physics
@@ -0,0 +1,611 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Physics/Material.h>
#include <AzFramework/Physics/NameConstants.h>
#include <AzFramework/Physics/ClassConverters.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzFramework/Physics/PhysicsSystem.h>
namespace
{
const char* const s_entireObjectSlotName = "Entire object";
const AZ::Data::Asset<Physics::MaterialLibraryAsset> s_invalidMaterialLibrary = { AZ::Data::AssetLoadBehavior::NoLoad };
}
namespace Physics
{
class MaterialLibraryAssetEventHandler
: public AZ::SerializeContext::IEventHandler
{
void OnReadBegin(void* classPtr)
{
auto matAsset = static_cast<MaterialLibraryAsset*>(classPtr);
matAsset->GenerateMissingIds();
}
};
class MaterialSelectionEventHandler
: public AZ::SerializeContext::IEventHandler
{
void OnReadEnd(void* classPtr)
{
auto materialSelection = static_cast<MaterialSelection*>(classPtr);
if (materialSelection->GetMaterialIdsAssignedToSlots().empty())
{
materialSelection->SetMaterialSlots(Physics::MaterialSelection::SlotsArray());
}
if (materialSelection->IsDefaultMaterialLibraryAsset())
{
materialSelection->SyncSelectionToMaterialLibrary();
}
}
};
//////////////////////////////////////////////////////////////////////////
const AZ::Crc32 MaterialConfiguration::s_stringGroup = AZ_CRC("StringGroup", 0x878e4bbd);
const AZ::Crc32 MaterialConfiguration::s_forbiddenStringSet = AZ_CRC("ForbiddenStringSet", 0x8c132196);
const AZ::Crc32 MaterialConfiguration::s_configLineEdit = AZ_CRC("ConfigLineEdit", 0x3e41d737);
void MaterialConfiguration::Reflect(AZ::ReflectContext* context)
{
MaterialId::Reflect(context);
MaterialFromAssetConfiguration::Reflect(context);
MaterialSelection::Reflect(context);
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MaterialConfiguration>()
->Version(3, &VersionConverter)
->Field("SurfaceType", &MaterialConfiguration::m_surfaceType)
->Field("DynamicFriction", &MaterialConfiguration::m_dynamicFriction)
->Field("StaticFriction", &MaterialConfiguration::m_staticFriction)
->Field("Restitution", &MaterialConfiguration::m_restitution)
->Field("FrictionCombine", &MaterialConfiguration::m_frictionCombine)
->Field("RestitutionCombine", &MaterialConfiguration::m_restitutionCombine)
->Field("Density", &MaterialConfiguration::m_density)
->Field("DebugColor", &MaterialConfiguration::m_debugColor)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
AZStd::unordered_set<AZStd::string> forbiddenSurfaceTypeNames;
forbiddenSurfaceTypeNames.insert("Default");
editContext->Class<MaterialConfiguration>("", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "Physics Material")
->DataElement(MaterialConfiguration::s_configLineEdit, &MaterialConfiguration::m_surfaceType, "Surface type", "Game surface type") // Uses ConfigStringLineEditCtrl in PhysX gem.
->Attribute(AZ::Edit::Attributes::MaxLength, 64)
->Attribute(MaterialConfiguration::s_stringGroup, AZ_CRC("LineEditGroupSurfaceType", 0x6670659e))
->Attribute(MaterialConfiguration::s_forbiddenStringSet, forbiddenSurfaceTypeNames)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_staticFriction, "Static friction", "Friction coefficient when object is still")
->Attribute(AZ::Edit::Attributes::Min, 0.f)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_dynamicFriction, "Dynamic friction", "Friction coefficient when object is moving")
->Attribute(AZ::Edit::Attributes::Min, 0.f)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_restitution, "Restitution", "Restitution coefficient")
->Attribute(AZ::Edit::Attributes::Min, 0.f)
->Attribute(AZ::Edit::Attributes::Max, 1.f)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &MaterialConfiguration::m_frictionCombine, "Friction combine", "How the friction is combined between colliding objects")
->EnumAttribute(Material::CombineMode::Average, "Average")
->EnumAttribute(Material::CombineMode::Minimum, "Minimum")
->EnumAttribute(Material::CombineMode::Maximum, "Maximum")
->EnumAttribute(Material::CombineMode::Multiply, "Multiply")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &MaterialConfiguration::m_restitutionCombine, "Restitution combine", "How the restitution is combined between colliding objects")
->EnumAttribute(Material::CombineMode::Average, "Average")
->EnumAttribute(Material::CombineMode::Minimum, "Minimum")
->EnumAttribute(Material::CombineMode::Maximum, "Maximum")
->EnumAttribute(Material::CombineMode::Multiply, "Multiply")
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialConfiguration::m_density, "Density", "Material density")
->Attribute(AZ::Edit::Attributes::Min, MaterialConfiguration::MinDensityLimit)
->Attribute(AZ::Edit::Attributes::Max, MaterialConfiguration::MaxDensityLimit)
->Attribute(AZ::Edit::Attributes::Suffix, " " + Physics::NameConstants::GetDensityUnit())
->DataElement(AZ::Edit::UIHandlers::Color, &MaterialConfiguration::m_debugColor, "Debug Color", "Debug color to use for this material")
;
}
}
}
AZ::Color MaterialConfiguration::GenerateDebugColor(const char* materialName)
{
static const AZ::Color colors[] =
{
AZ::Colors::Aqua, AZ::Colors::Silver,
AZ::Colors::Gray, AZ::Colors::Maroon,
AZ::Colors::Green, AZ::Colors::Blue,
AZ::Colors::Navy, AZ::Colors::Yellow,
AZ::Colors::Orange, AZ::Colors::Olive,
AZ::Colors::Purple, AZ::Colors::Fuchsia,
AZ::Colors::Teal, AZ::Colors::Lime,
AZ::Colors::White
};
unsigned int selection = static_cast<unsigned int>(AZ_CRC(materialName)) % AZ_ARRAY_SIZE(colors);
return colors[selection];
}
bool MaterialConfiguration::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
{
bool success = true;
if (classElement.GetVersion() <= 1)
{
const int surfaceTypeElemIndex = classElement.FindElement(AZ_CRC("SurfaceType", 0x8b1fc300));
AZ::Color debugColor = AZ::Colors::White;
if (surfaceTypeElemIndex >= 0)
{
AZStd::string surfaceType;
AZ::SerializeContext::DataElementNode& surfaceTypeElem = classElement.GetSubElement(surfaceTypeElemIndex);
surfaceTypeElem.GetData(surfaceType);
debugColor = GenerateDebugColor(surfaceType.c_str());
}
classElement.AddElementWithData(context, "DebugColor", debugColor);
}
return success;
}
//////////////////////////////////////////////////////////////////////////
void MaterialLibraryAsset::Reflect(AZ::ReflectContext * context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MaterialLibraryAsset, AZ::Data::AssetData>()
->Version(2, &ClassConverters::MaterialLibraryAssetConverter)
->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true)
->EventHandler<MaterialLibraryAssetEventHandler>()
->Field("Properties", &MaterialLibraryAsset::m_materialLibrary)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<MaterialLibraryAsset>("", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialLibraryAsset::m_materialLibrary, "Physics Materials", "List of physics materials")
->Attribute("EditButton", "")
->Attribute(AZ::Edit::Attributes::ForceAutoExpand, true)
;
}
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialLibraryAssetReflectionWrapper::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MaterialLibraryAssetReflectionWrapper>()
->Version(1)
->Field("Asset", &MaterialLibraryAssetReflectionWrapper::m_asset)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<MaterialLibraryAssetReflectionWrapper>("", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialLibraryAssetReflectionWrapper::m_asset, "Physics Material Library", "Physics Material Library")
->Attribute("EditButton", "")
;
}
}
}
//////////////////////////////////////////////////////////////////////////
void DefaultMaterialLibraryAssetReflectionWrapper::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<DefaultMaterialLibraryAssetReflectionWrapper>()
->Version(1)
->Field("Asset", &DefaultMaterialLibraryAssetReflectionWrapper::m_asset)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<DefaultMaterialLibraryAssetReflectionWrapper>("", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &DefaultMaterialLibraryAssetReflectionWrapper::m_asset, "Default Physics Material Library", "Library to use by default")
->Attribute(AZ::Edit::Attributes::AllowClearAsset, false)
->Attribute("EditButton", "")
;
}
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialFromAssetConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<MaterialFromAssetConfiguration>()
->Version(1)
->Field("Configuration", &MaterialFromAssetConfiguration::m_configuration)
->Field("UID", &MaterialFromAssetConfiguration::m_id)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<MaterialFromAssetConfiguration>("", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialFromAssetConfiguration::m_configuration, "Physics Material", "Physics Material properties")
->Attribute(AZ::Edit::Attributes::ForceAutoExpand, true)
;
}
}
}
//////////////////////////////////////////////////////////////////////////
bool MaterialLibraryAsset::GetDataForMaterialId(const MaterialId& materialId, MaterialFromAssetConfiguration& configuration) const
{
auto foundMaterialConfiguration = AZStd::find_if(m_materialLibrary.begin(), m_materialLibrary.end(), [materialId](const auto& data)
{
return data.m_id == materialId;
});
if (foundMaterialConfiguration != m_materialLibrary.end())
{
configuration = *foundMaterialConfiguration;
return true;
}
return false;
}
bool MaterialLibraryAsset::HasDataForMaterialId(const MaterialId& materialId) const
{
auto foundMaterialConfiguration = AZStd::find_if(m_materialLibrary.begin(), m_materialLibrary.end(), [materialId](const auto& data)
{
return data.m_id == materialId;
});
return foundMaterialConfiguration != m_materialLibrary.end();
}
bool MaterialLibraryAsset::GetDataForMaterialName(const AZStd::string& materialName, MaterialFromAssetConfiguration& configuration) const
{
auto foundMaterialConfiguration = AZStd::find_if(m_materialLibrary.begin(), m_materialLibrary.end(), [&materialName](const auto& data)
{
return data.m_configuration.m_surfaceType == materialName;
});
if (foundMaterialConfiguration != m_materialLibrary.end())
{
configuration = *foundMaterialConfiguration;
return true;
}
return false;
}
void MaterialLibraryAsset::AddMaterialData(const MaterialFromAssetConfiguration& data)
{
MaterialFromAssetConfiguration existingConfiguration;
if (!data.m_id.IsNull() && GetDataForMaterialId(data.m_id, existingConfiguration))
{
AZ_Warning("MaterialLibraryAsset", false, "Trying to add material that already exists");
return;
}
m_materialLibrary.push_back(data);
GenerateMissingIds();
}
void MaterialLibraryAsset::GenerateMissingIds()
{
for (auto& materialData : m_materialLibrary)
{
if (materialData.m_id.IsNull())
{
materialData.m_id = MaterialId::Create();
}
}
}
//////////////////////////////////////////////////////////////////////////
void MaterialId::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Physics::MaterialId>()
->Version(1)
->Field("MaterialId", &Physics::MaterialId::m_id)
;
}
}
MaterialId MaterialId::Create()
{
MaterialId id;
id.m_id = AZ::Uuid::Create();
return id;
}
MaterialId MaterialId::FromUUID(const AZ::Uuid& uuid)
{
MaterialId id;
id.m_id = uuid;
return id;
}
//////////////////////////////////////////////////////////////////////////
void MaterialSelection::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<MaterialSelection>()
->Version(2, &ClassConverters::MaterialSelectionConverter)
->EventHandler<MaterialSelectionEventHandler>()
->Field("Material", &MaterialSelection::m_materialLibrary)
->Field("MaterialIds", &MaterialSelection::m_materialIdsAssignedToSlots)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<MaterialSelection>("Physics Material", "Select physics material library and which materials to use for the object")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialLibrary, "Library", "Physics material library to use for this object")
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, true)
->Attribute("EditButton", "")
->Attribute("EditDescription", "Open in Asset Editor")
->Attribute(AZ::Edit::Attributes::DefaultAsset, &MaterialSelection::GetDefaultMaterialLibraryId)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &MaterialSelection::OnMaterialLibraryChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &MaterialSelection::m_materialIdsAssignedToSlots, "Mesh Surfaces", "Specify which Physics Material to use for each element of this object")
->ElementAttribute(Attributes::MaterialLibraryAssetId, &MaterialSelection::GetMaterialLibraryAssetId)
->Attribute(AZ::Edit::Attributes::IndexedChildNameLabelOverride, &MaterialSelection::GetMaterialSlotLabel)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->ElementAttribute(AZ::Edit::Attributes::ReadOnly, &MaterialSelection::AreMaterialSlotsReadOnly)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
;
}
}
}
AZ::u32 MaterialSelection::OnMaterialLibraryChanged()
{
SyncSelectionToMaterialLibrary();
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
AZStd::string MaterialSelection::GetMaterialSlotLabel(int index)
{
if (index < m_materialSlots.size())
{
return m_materialSlots[index];
}
else if (m_materialIdsAssignedToSlots.size() == 1)
{
// this is valid scenario to allow MaterialSelection to
// be used by just reflecting it to editorContext, in simple cases
// when we only need to assign one default material, such as terrain or ragdoll
return s_entireObjectSlotName;
}
else
{
// If there is more than one material slot
// the caller must use SetMaterialSlots function
return "<error>";
}
}
AZ::Data::AssetId MaterialSelection::GetMaterialLibraryAssetId() const
{
return GetMaterialLibraryAsset().GetId();
}
const Physics::MaterialLibraryAsset* MaterialSelection::GetMaterialLibraryAssetData() const
{
return GetMaterialLibraryAsset().Get();
}
const AZStd::string& MaterialSelection::GetMaterialLibraryAssetHint() const
{
return m_materialLibrary.GetHint();
}
void MaterialSelection::OnDefaultMaterialLibraryChanged(const AZ::Data::AssetId& defaultMaterialLibraryId)
{
AZ_UNUSED(defaultMaterialLibraryId);
if (IsDefaultMaterialLibraryAsset())
{
OnMaterialLibraryChanged();
}
}
void MaterialSelection::SetSlotsReadOnly(bool readOnly)
{
m_slotsReadOnly = readOnly;
}
bool MaterialSelection::IsMaterialLibraryValid() const
{
if (GetMaterialLibraryAssetId().IsValid())
{
auto materialAsset = LoadAsset();
const auto& materialsData = materialAsset.Get()->GetMaterialsData();
if (materialsData.size() != 0)
{
return true;
}
}
return false;
}
bool MaterialSelection::GetMaterialConfiguration(Physics::MaterialFromAssetConfiguration& configuration, const Physics::MaterialId& materialId) const
{
if (IsMaterialLibraryValid())
{
auto materialAsset = LoadAsset();
if (materialAsset.Get())
{
return materialAsset.Get()->GetDataForMaterialId(materialId, configuration);
}
}
return false;
}
void MaterialSelection::SetMaterialLibrary(const AZ::Data::AssetId& assetId)
{
m_materialLibrary = AZ::Data::AssetManager::Instance().GetAsset<Physics::MaterialLibraryAsset>(assetId, m_materialLibrary.GetAutoLoadBehavior());
m_materialLibrary.BlockUntilLoadComplete();
}
void MaterialSelection::ResetToDefaultMaterialLibrary()
{
m_materialLibrary = {};
}
void MaterialSelection::SetMaterialSlots(const SlotsArray& slots)
{
if (slots.empty())
{
m_materialSlots = { s_entireObjectSlotName };
}
else
{
m_materialSlots = slots;
}
m_materialIdsAssignedToSlots.resize(m_materialSlots.size());
}
const AZStd::vector<Physics::MaterialId>& MaterialSelection::GetMaterialIdsAssignedToSlots() const
{
return m_materialIdsAssignedToSlots;
}
Physics::MaterialId MaterialSelection::GetMaterialId(int slotIndex) const
{
if (slotIndex >= m_materialIdsAssignedToSlots.size() || slotIndex < 0)
{
return Physics::MaterialId();
}
return m_materialIdsAssignedToSlots[slotIndex];
}
void MaterialSelection::SetMaterialId(const Physics::MaterialId& materialId, int slotIndex)
{
if (m_materialIdsAssignedToSlots.empty())
{
m_materialIdsAssignedToSlots.resize(1);
}
slotIndex = AZ::GetClamp(slotIndex, 0, static_cast<int>(m_materialIdsAssignedToSlots.size()) - 1);
m_materialIdsAssignedToSlots[slotIndex] = materialId;
}
AZ::Data::Asset<Physics::MaterialLibraryAsset> MaterialSelection::LoadAsset() const
{
AZ::Data::Asset<MaterialLibraryAsset> asset = AZ::Data::AssetManager::Instance()
.GetAsset<Physics::MaterialLibraryAsset>(GetMaterialLibraryAssetId(), AZ::Data::AssetLoadBehavior::Default);
asset.BlockUntilLoadComplete();
return asset;
}
void MaterialSelection::SyncSelectionToMaterialLibrary()
{
if (GetMaterialLibraryAssetId().IsValid())
{
auto materialLibraryAsset = AZ::Data::AssetManager::Instance().GetAsset<Physics::MaterialLibraryAsset>(GetMaterialLibraryAssetId(), AZ::Data::AssetLoadBehavior::Default);
materialLibraryAsset.BlockUntilLoadComplete();
// We try to check whether existing selection matches any materials in the newly assigned library and do one of the following:
// 1. If previous MaterialId is invalid for this material library, and it is not the Default material, we set it to the Default material from the library.
// 2. If it's valid, or it is the Default material, we don't change it (useful when user accidentally re-assigns the same library: previous selection won't go away).
if (materialLibraryAsset.Get())
{
for (Physics::MaterialId& materialId : m_materialIdsAssignedToSlots)
{
if (!materialLibraryAsset.Get()->HasDataForMaterialId(materialId)
&& !materialId.IsNull()) // Null materialId is the Default material.
{
materialId = MaterialId();
}
}
}
else
{
AZ_Warning("PhysX", false, "MaterialSelection: invalid material library");
}
}
}
const AZ::Data::Asset<Physics::MaterialLibraryAsset>& MaterialSelection::GetMaterialLibraryAsset() const
{
if (IsDefaultMaterialLibraryAsset())
{
const AZ::Data::Asset<Physics::MaterialLibraryAsset>& defaultMaterialLibrary = GetDefaultMaterialLibrary();
return defaultMaterialLibrary;
}
return m_materialLibrary;
}
bool MaterialSelection::IsDefaultMaterialLibraryAsset() const
{
return !m_materialLibrary.GetId().IsValid();
}
const AZ::Data::Asset<Physics::MaterialLibraryAsset>& MaterialSelection::GetDefaultMaterialLibrary()
{
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
return physicsSystem->GetDefaultMaterialLibrary();
}
return s_invalidMaterialLibrary;
}
const AZ::Data::AssetId& MaterialSelection::GetDefaultMaterialLibraryId()
{
return GetDefaultMaterialLibrary().GetId();
}
bool MaterialSelection::AreMaterialSlotsReadOnly() const
{
return m_slotsReadOnly;
}
} // namespace Physics
@@ -0,0 +1,386 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetTypeInfoBus.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Math/Color.h>
#include <AzFramework/Asset/GenericAssetHandler.h>
namespace AZ
{
class ReflectContext;
}
namespace Physics
{
/// Physics material
/// =========================
/// This is the interface to the wrapper around native material type (such as PxMaterial in PhysX gem)
/// that stores extra metadata, like Surface Type name.
/// To see more details about PhysX implementation please refer to PhysX::Material class
///
/// Usage example
/// -------------------------
/// Create new material using Physics::SystemRequestBus and Physics::MaterialConfiguration:
///
/// Physics::MaterialConfiguration materialProperties;
/// AZStd::shared_ptr<Physics::Material> newMaterial = AZ::Interface<Physics::System>::Get()->CreateMaterial(materialProperties);
///
/// To get PxMaterial use GetNativePointer function
///
/// physx::PxMaterial* material = static_cast<physx::PxMaterial*>(newMaterial->GetNativePointer());
///
/// You can use retrieved PxMaterial pointer on its own, provided you increment its reference count.
/// If this class goes out of scope, the PxMaterial pointer will be valid, but its userData
/// will be cleaned up to point to nullptr.
class Material
{
public:
AZ_CLASS_ALLOCATOR(Material, AZ::SystemAllocator, 0);
AZ_RTTI(Material, "{44636CEA-46DD-4D4A-B1EF-5ED6DEA7F714}");
/// Enumeration that determines how two materials properties are combined when
/// processing collisions.
enum class CombineMode : AZ::u8
{
Average,
Minimum,
Maximum,
Multiply
};
/// Returns AZ::Crc32 of the surface name.
virtual AZ::Crc32 GetSurfaceType() const = 0;
virtual void SetSurfaceType(AZ::Crc32 surfaceType) = 0;
virtual const AZStd::string& GetSurfaceTypeName() const = 0;
virtual float GetDynamicFriction() const = 0;
virtual void SetDynamicFriction(float dynamicFriction) = 0;
virtual float GetStaticFriction() const = 0;
virtual void SetStaticFriction(float staticFriction) = 0;
virtual float GetRestitution() const = 0;
virtual void SetRestitution(float restitution) = 0;
virtual CombineMode GetFrictionCombineMode() const = 0;
virtual void SetFrictionCombineMode(CombineMode mode) = 0;
virtual CombineMode GetRestitutionCombineMode() const = 0;
virtual void SetRestitutionCombineMode(CombineMode mode) = 0;
virtual float GetDensity() const = 0;
virtual void SetDensity(float density) = 0;
/// If the name of this material matches the name of one of the CrySurface types, it will return its CrySurface Id.\n
/// If there's no match it will return default CrySurface Id.\n
/// CrySurface types are defined in libs/materialeffects/surfacetypes.xml
virtual AZ::u32 GetCryEngineSurfaceId() const = 0;
/// Returns underlying pointer of the native physics type (for example PxMaterial in PhysX).
virtual void* GetNativePointer() = 0;
};
/// Default values used for initializing materials
/// ===================
///
/// Use MaterialConfiguration to define properties for materials at the time of creation. \n
/// Use Physics::SystemRequestBus to create new materials.
class MaterialConfiguration
{
public:
AZ_TYPE_INFO(MaterialConfiguration, "{8807CAA1-AD08-4238-8FDB-2154ADD084A1}");
static void Reflect(AZ::ReflectContext* context);
const static AZ::Crc32 s_stringGroup; ///< Edit context data attribute. Identifies a string group instance. String values in the same group are unique.
const static AZ::Crc32 s_forbiddenStringSet; ///< Edit context data attribute. A set of strings that are not acceptable as values to the data element. Can be AZStd::unordered_set<AZStd::string>, AZStd::set<AZStd::string>, AZStd::vector<AZStd::string>
const static AZ::Crc32 s_configLineEdit; ///< Edit context data element handler. Creates custom line edit widget that allows string values to be unique in a group.
static constexpr float MinDensityLimit = 0.01f; //!< Minimum possible value of density.
static constexpr float MaxDensityLimit = 100000.0f; //!< Maximum possible value of density.
AZStd::string m_surfaceType{ "Default" };
float m_dynamicFriction = 0.5f;
float m_staticFriction = 0.5f;
float m_restitution = 0.5f;
float m_density = 1000.0f;
Material::CombineMode m_restitutionCombine = Material::CombineMode::Average;
Material::CombineMode m_frictionCombine = Material::CombineMode::Average;
AZ::Color m_debugColor = AZ::Colors::White;
private:
static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement);
static AZ::Color GenerateDebugColor(const char* materialName);
};
namespace Attributes
{
const static AZ::Crc32 MaterialLibraryAssetId = AZ_CRC("MaterialAssetId", 0x4a88a3f5);
}
/// Class that is used to identify the material in the collection of materials
/// ============================================================
///
/// Collection of the materials is stored in MaterialLibraryAsset.
class MaterialId
{
public:
AZ_CLASS_ALLOCATOR(MaterialId, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(MaterialId, "{744CCE6C-9F69-4E2F-B950-DAB8514F870B}");
static void Reflect(AZ::ReflectContext* context);
static MaterialId Create();
static MaterialId FromUUID(const AZ::Uuid& uuid);
bool IsNull() const { return m_id.IsNull(); }
bool operator==(const MaterialId& other) const { return m_id == other.m_id; }
const AZ::Uuid& GetUuid() const { return m_id; }
private:
AZ::Uuid m_id = AZ::Uuid::CreateNull();
};
/// A single Material entry in the material library
/// ===============================================
///
/// MaterialLibraryAsset holds a collection of MaterialFromAssetConfiguration instances.
class MaterialFromAssetConfiguration
{
public:
AZ_TYPE_INFO(MaterialFromAssetConfiguration, "{FBD76628-DE57-435E-BE00-6FFAE64DDF1D}");
static void Reflect(AZ::ReflectContext* context);
MaterialConfiguration m_configuration;
MaterialId m_id;
};
/// An asset that holds a list of materials to be edited and assigned in Lumberyard Editor
/// ======================================================================================
///
/// Use Asset Editor to create a MaterialLibraryAsset and add materials to it.\n
/// You can assign this library on primitive colliders, terrain layers, mesh colliders.
/// You can later select a specific material out of the library.\n
/// Please note, MaterialLibraryAsset is used only to provide a way to edit materials in the
/// Editor, if you need to create materials at runtime (for example, from custom configuration files)
/// please use Physics::Material class directly.
class MaterialLibraryAsset
: public AZ::Data::AssetData
{
public:
AZ_CLASS_ALLOCATOR(MaterialLibraryAsset, AZ::SystemAllocator, 0);
AZ_RTTI(MaterialLibraryAsset, "{9E366D8C-33BB-4825-9A1F-FA3ADBE11D0F}", AZ::Data::AssetData);
MaterialLibraryAsset() = default;
virtual ~MaterialLibraryAsset() = default;
static void Reflect(AZ::ReflectContext* context);
/// Finds MaterialFromAssetConfiguration in the library given MaterialId
/// @param materialId material id to find the configuration for
/// @param data stores the material data if material with such ID exists
/// @return true if MaterialFromAssetConfiguration was found, False otherwise
bool GetDataForMaterialId(const MaterialId& materialId, MaterialFromAssetConfiguration& configuration) const;
/// Retrieves if there is any data with the given Material Id
/// @param materialId material id to find
/// @return true if material with that id was found, False otherwise
bool HasDataForMaterialId(const MaterialId& materialId) const;
/// Finds MaterialFromAssetConfiguration in the library given material name
/// @param materialName material name to find the configuration for
/// @param data stores the material data if material with such name exists
/// @return true if MaterialFromAssetConfiguration was found, False otherwise
bool GetDataForMaterialName(const AZStd::string& materialName, MaterialFromAssetConfiguration& configuration) const;
/// Adds material data to the asset library.\n
/// If MaterialId is not set, it'll be generated automatically.\n
/// If MaterialId is set and is unique for this collection it'll be added to the library unchanged.\n
/// @param data Material data to add
void AddMaterialData(const MaterialFromAssetConfiguration& data);
/// Returns all MaterialFromAssetConfiguration instances from this library
/// @return a Vector of all MaterialFromAssetConfiguration stored in this library
const AZStd::vector<MaterialFromAssetConfiguration>& GetMaterialsData() const { return m_materialLibrary; }
protected:
friend class MaterialLibraryAssetEventHandler;
void GenerateMissingIds();
AZStd::vector<MaterialFromAssetConfiguration> m_materialLibrary;
};
/// The class is used to expose a MaterialLibraryAsset to Edit Context
/// =======================================================================
///
/// Since AZ::Data::Asset doesn't reflect the data to EditContext
/// we have to have a wrapper doing it.
class MaterialLibraryAssetReflectionWrapper
{
public:
AZ_CLASS_ALLOCATOR(MaterialLibraryAssetReflectionWrapper, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(MaterialLibraryAssetReflectionWrapper, "{3D2EF5DF-EFD0-47EB-B88F-3E6FE1FEE5B0}");
static void Reflect(AZ::ReflectContext* context);
AZ::Data::Asset<Physics::MaterialLibraryAsset> m_asset =
AZ::Data::AssetLoadBehavior::NoLoad;
};
/// Customized material library for use as default material library
class DefaultMaterialLibraryAssetReflectionWrapper : public Physics::MaterialLibraryAssetReflectionWrapper
{
public:
AZ_CLASS_ALLOCATOR(MaterialLibraryAssetReflectionWrapper, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(DefaultMaterialLibraryAssetReflectionWrapper, "{02AB8CBC-D35B-4E0F-89BA-A96D94DAD4F9}");
static void Reflect(AZ::ReflectContext* context);
AZ::Data::Asset<Physics::MaterialLibraryAsset> m_asset =
AZ::Data::AssetLoadBehavior::NoLoad;
};
/// The class is used to store a MaterialLibraryAsset and a vector of MaterialIds selected from the library
/// =======================================================================
///
/// This class is used to store a reference to the library asset and user's
/// selection of the materials from this library.\n
/// It also reflects UI controls for assigning MaterialLibraryAsset and selecting a material from it.
/// You can reflect this class in EditorContext to provide UI for selecting materials
/// on any custom component or QWidget.
class MaterialSelection
{
friend class MaterialSelectionEventHandler;
public:
AZ_CLASS_ALLOCATOR(MaterialSelection, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(MaterialSelection, "{F571AFF4-C4BB-4590-A204-D11D9EEABBC4}");
using SlotsArray = AZStd::vector<AZStd::string>;
static void Reflect(AZ::ReflectContext* context);
/// Returns whether MaterialLibraryAsset assigned to this selection exists and valid. Attempts to load
/// the library if it's not loaded yet.
/// @return true if MaterialLibraryAsset has a valid AssetId, loaded and isn't empty
bool IsMaterialLibraryValid() const;
/// Looks up MaterialLibraryAsset for MaterialFromAssetConfiguration with MaterialId that is stored intrenally.
/// @param configuration contains material data if there is a material selected by user
/// and if it exists in the MaterialLibraryAsset
/// @param materialId MaterialId to retrieve MaterialFromAssetConfiguration for
/// @return true if lookup was successful.
bool GetMaterialConfiguration(Physics::MaterialFromAssetConfiguration& configuration, const Physics::MaterialId& materialId) const;
/// Sets and loads MaterialLibraryAsset with specified AssetId.
/// It is used to construct MaterialSelection at runtime.
/// It is not a typical use case and mostly needed to convert legacy entities and auto-generate material libraries
/// @param assetId AssetId to create MaterialLibraryAsset with
void SetMaterialLibrary(const AZ::Data::AssetId& assetId);
/// Sets the material library to none, this will cause to use the project-wide default material library
void ResetToDefaultMaterialLibrary();
/// Sets an array of material slots to pick MaterialIds for. Having multiple slots is required for assigning multiple materials on a mesh
/// or heightfield object. SlotsArray can be empty and in this case Default slot will be created.
/// @param slots Array of names for slots. Can be empty, in this case Default slot will be created
void SetMaterialSlots(const SlotsArray& slots);
/// Returns a list of MaterialId that were assigned for each corresponding slot.
const AZStd::vector<Physics::MaterialId>& GetMaterialIdsAssignedToSlots() const;
/// Sets the MaterialId from MaterialLibraryAsset as the selected material at a specific slotIndex.
/// @param materialId MaterialId that user selected from the MaterialLibraryAsset
/// @param slotIndex index of the slot to set MaterialId for
void SetMaterialId(const Physics::MaterialId& materialId, int slotIndex = 0);
/// Returns the material library asset id.
AZ::Data::AssetId GetMaterialLibraryAssetId() const;
/// Returns the material id assigned to this selection at a specific slotIndex.
/// @param slotIndex index of the slot to retrieve MaterialId for
Physics::MaterialId GetMaterialId(int slotIndex = 0) const;
/// Returns the material library asset.
const Physics::MaterialLibraryAsset* GetMaterialLibraryAssetData() const;
/// Returns the material library asset hint(UI display string)
const AZStd::string& GetMaterialLibraryAssetHint() const;
/// Called when the material library has changed
void OnDefaultMaterialLibraryChanged(const AZ::Data::AssetId& defaultMaterialLibraryId);
/// Set if the material slots are editable in the edit context
void SetSlotsReadOnly(bool readOnly);
private:
AZ::Data::Asset<Physics::MaterialLibraryAsset> m_materialLibrary { AZ::Data::AssetLoadBehavior::NoLoad };
AZStd::vector<Physics::MaterialId> m_materialIdsAssignedToSlots;
SlotsArray m_materialSlots;
bool m_slotsReadOnly = false;
const AZ::Data::Asset<Physics::MaterialLibraryAsset>& GetMaterialLibraryAsset() const;
AZ::Data::Asset<Physics::MaterialLibraryAsset> LoadAsset() const;
bool IsDefaultMaterialLibraryAsset() const;
void SyncSelectionToMaterialLibrary();
static const AZ::Data::Asset<Physics::MaterialLibraryAsset>& GetDefaultMaterialLibrary();
static const AZ::Data::AssetId& GetDefaultMaterialLibraryId();
bool AreMaterialSlotsReadOnly() const;
// EditorContext callbacks
AZ::u32 OnMaterialLibraryChanged();
AZStd::string GetMaterialSlotLabel(int index);
};
/// Editor Bus used to assign material to terrain surface id.
/// ========================================================
///
/// Used by Terrain Layer Editor window to save material selection for a specific Terrain Layer. \n
/// Must be used before cooking the terrain, in-game usage won't have any effect.
class EditorTerrainMaterialRequests
: public AZ::ComponentBus
{
public:
virtual void SetMaterialSelectionForSurfaceId(int surfaceId, const MaterialSelection& selection) = 0;
virtual bool GetMaterialSelectionForSurfaceId(int surfaceId, MaterialSelection& selection) = 0;
protected:
~EditorTerrainMaterialRequests() = default;
};
using EditorTerrainMaterialRequestsBus = AZ::EBus<EditorTerrainMaterialRequests>;
/// Alias for surface id to terrain material unordered map.
using TerrainMaterialSurfaceIdMap = AZStd::unordered_map<int, Physics::MaterialSelection>;
/// Bus that is used to retrieve SurfaceType id from the legacy material system
/// ========================================================
///
/// Returns CrySurfaceType Id given Crc32 of its name
class LegacySurfaceTypeRequests
: public AZ::EBusTraits
{
public:
virtual ~LegacySurfaceTypeRequests() {}
/// Returns CrySurfaceType Id given Crc32 of its name
/// @param Crc32 hash of the CrySurfaceType name
/// @return ID of the CrySurfaceType. If such type does not exists it will default to 0.
virtual AZ::u32 GetLegacySurfaceType(AZ::Crc32 pxMaterialType) = 0;
virtual AZ::u32 GetLegacySurfaceTypeFronName(const AZStd::string& pxMaterialTypeName) = 0;
};
using LegacySurfaceTypeRequestsBus = AZ::EBus<LegacySurfaceTypeRequests>;
} // namespace Physics
@@ -0,0 +1,59 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include "Material.h"
#include <AzCore/EBus/EBus.h>
namespace Physics
{
/// Listens to requests for physics materials.
class PhysicsMaterialRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; // Implemented by sole owner of materials, e.g. class MaterialManager in PhysX gem.
/// Get default material
virtual AZStd::shared_ptr<Physics::Material> GetGenericDefaultMaterial() = 0;
/// Returns weak pointers to physics materials.
/// Connect to PhysicsMaterialNotifications::MaterialsReleased to be informed when material pointers are deleted by owner.
virtual void GetMaterials(const MaterialSelection& materialSelection
, AZStd::vector<AZStd::weak_ptr<Physics::Material>>& outMaterials) = 0;
/// Returns a weak pointer to physics material with the given name.
virtual AZStd::weak_ptr<Physics::Material> GetMaterialByName(const AZStd::string& name) = 0;
/// Returns index of the first selected material in MaterialSelection's material library.
/// A MaterialSelection can contain multiple material selections.
/// Returned index is 0-based where 0 is the Default material, and materials from the material library are 1 and onwards.
virtual AZ::u32 GetFirstSelectedMaterialIndex(const MaterialSelection& materialSelection) = 0;
};
using PhysicsMaterialRequestBus = AZ::EBus<PhysicsMaterialRequests>;
/// Dispatches changes in physics materials to others.
class PhysicsMaterialNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; // SystemComponent of multiple gems may listen to changes in materials.
typedef AZ::Uuid BusIdType;
/// Notifies that material pointers have been deleted by owner.
virtual void MaterialsReleased() = 0;
};
using PhysicsMaterialNotificationsBus = AZ::EBus<PhysicsMaterialNotifications>;
}
@@ -0,0 +1,107 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/NameConstants.h>
namespace Physics
{
namespace NameConstants
{
// Some of these constants include UTF-8 hex values which generate unicode characters needed for some units such
// as superscripts when QT strings are created from them. To add more unicode characters, find them on a site
// such as https://www.fileformat.info/info/unicode/char/search.htm and look for the UTF-8 (hex) values.
const AZStd::string& GetSuperscriptMinus()
{
static const AZStd::string superscriptMinus = "\xE2\x81\xBB"; // equivalent to U+207B
return superscriptMinus;
}
const AZStd::string& GetSuperscriptOne()
{
static const AZStd::string superscriptOne = "\xC2\xB9"; // equivalent to U+00B9
return superscriptOne;
}
const AZStd::string& GetSuperscriptTwo()
{
static const AZStd::string superscriptTwo = "\xC2\xB2"; // equivalent to U+00B2
return superscriptTwo;
}
const AZStd::string& GetSuperscriptThree()
{
static const AZStd::string superscriptThree = "\xC2\xB3"; // equivalent to U+00B3
return superscriptThree;
}
const AZStd::string& GetInterpunct()
{
static const AZStd::string interpunct = "\xC2\xB7"; // equivalent to U+00B7, also known as middle dot
return interpunct;
}
const AZStd::string& GetSpeedUnit()
{
static const AZStd::string speedUnit = AZStd::string::format("m%ss%s%s",
GetInterpunct().c_str(), GetSuperscriptMinus().c_str(), GetSuperscriptOne().c_str());
return speedUnit;
}
const AZStd::string& GetAngularVelocityUnit()
{
static const AZStd::string angularVelocityUnit = AZStd::string::format("rad%ss%s%s",
GetInterpunct().c_str(), GetSuperscriptMinus().c_str(), GetSuperscriptOne().c_str());
return angularVelocityUnit;
}
const AZStd::string& GetLengthUnit()
{
static const AZStd::string lengthUnit = "m";
return lengthUnit;
}
const AZStd::string& GetVolumeUnit()
{
static const AZStd::string volumeUnit = AZStd::string::format("%s%s",
GetLengthUnit().c_str(), GetSuperscriptThree().c_str());
return volumeUnit;
}
const AZStd::string& GetMassUnit()
{
static const AZStd::string massUnit = "kg";
return massUnit;
}
const AZStd::string& GetInertiaUnit()
{
static const AZStd::string inertiaUnit = AZStd::string::format("kg%sm%s",
GetInterpunct().c_str(), GetSuperscriptTwo().c_str());
return inertiaUnit;
}
const AZStd::string& GetSleepThresholdUnit()
{
static const AZStd::string sleepThresholdUnit = AZStd::string::format("m%ss%s%s",
GetSuperscriptTwo().c_str(), GetInterpunct().c_str(), GetSuperscriptTwo().c_str());
return sleepThresholdUnit;
}
const AZStd::string& GetDensityUnit()
{
static const AZStd::string densityUnit = AZStd::string::format("%s/%s",
GetMassUnit().c_str(), GetVolumeUnit().c_str());
return densityUnit;
}
} // namespace NameConstants
} // namespace PhysX
@@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/string/string.h>
namespace Physics
{
/// Constants for naming such as unit suffixes for physics properties.
namespace NameConstants
{
const AZStd::string& GetSuperscriptMinus();
const AZStd::string& GetSuperscriptOne();
const AZStd::string& GetSuperscriptTwo();
const AZStd::string& GetSuperscriptThree();
const AZStd::string& GetInterpunct();
const AZStd::string& GetSpeedUnit();
const AZStd::string& GetAngularVelocityUnit();
const AZStd::string& GetLengthUnit();
const AZStd::string& GetVolumeUnit();
const AZStd::string& GetMassUnit();
const AZStd::string& GetInertiaUnit();
const AZStd::string& GetSleepThresholdUnit();
const AZStd::string& GetDensityUnit();
} // namespace NameConstants
} // namespace Physics
@@ -0,0 +1,60 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzFramework/Physics/World.h> // Temporary until LYN-438 work is complete
namespace AzPhysics
{
struct SceneConfiguration;
class Scene
{
public:
AZ_RTTI(Scene, "{52BD8163-BDC4-4B09-ABB2-11DD1F601FFD}");
Scene() = default;
virtual ~Scene() = default;
//! Start the simulation process.
//! As an example, this is a good place to trigger and queue any long running work in separate threads.
//! @param deltatime The time in seconds to run the simulation for.
virtual void StartSimulation(float deltatime) = 0;
//! Complete the simulation process.
//! As an example, this is a good place to wait for any work to complete that was triggered in StartSimulation, or swap buffers if double buffering.
virtual void FinishSimulation() = 0;
//! Enable or Disable this Scene's Simulation tick.
//! Default is Enabled.
//! @param enable When true the Scene will execute its simulation tick when StartSimulation is called. When false, StartSimulation will not execute.
virtual void Enable(bool enable) = 0;
//! Check if this Scene is currently Enabled.
//! @return When true the Scene is enabled and will execute its simulation tick when StartSimulation is called. When false, StartSimulation will not execute.
virtual bool IsEnabled() const = 0;
//! Accessor to the Scenes Configuration.
//! @returns Return the currently used SceneConfiguration.
virtual const SceneConfiguration& GetConfiguration() const = 0;
//! Update the SceneConfiguration.
//! @param config The new configuration to apply.
virtual void UpdateConfiguration(const SceneConfiguration& config) = 0;
// Temporary until LYN-438 work is complete
virtual AZStd::shared_ptr<Physics::World> GetLegacyWorld() const = 0;
};
using SceneList = AZStd::vector<Scene*>;
} // namespace AzPhysics
@@ -0,0 +1,170 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/Event.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/functional.h>
#include <AzFramework/Physics/Material.h>
#include <AzFramework/Physics/PhysicsScene.h>
#include <AzFramework/Physics/Common/PhysicsEvents.h>
#include <AzFramework/Physics/Common/PhysicsTypes.h>
#include <AzFramework/Physics/Configuration/SystemConfiguration.h>
#include <AzFramework/Physics/Configuration/SceneConfiguration.h>
namespace AzPhysics
{
//!Interface to access the Physics System.
//!
class SystemInterface
{
public:
AZ_RTTI(SystemInterface, "{B6F4D92A-061B-4CB3-AAB5-984B599A53AE}");
SystemInterface() = default;
virtual ~SystemInterface() = default;
AZ_DISABLE_COPY_MOVE(SystemInterface);
//! Initialize the Physics system with the given configuration.
//! @param config Contains the configuration options
virtual void Initialize(const SystemConfiguration* config) = 0;
//! Will re-initialize the physics backend.
//! Will preserve Scene and Simulation Body data along with any existing Handles.
virtual void Reinitialize() = 0;
//! Teardown the whole Physics system.
//! This removes all Scene and Simulation Body data, then physics will stop running.
virtual void Shutdown() = 0;
//! Advance the Physics state.
//! This will iterate the Scene list, update All simulation bodies and advance the physics state by the given delta time.
//! It is recommended to call this function to run the physics tick. Advanced users may manually update each scene, if required to have finer control.
//! This function will also signal the OnPresimulateEvent and OnPostsimulateEvent. The OnPresimulateEvent will have a parameter that will be the total time executed by the simulation.
//! When SystemConfiguration::m_fixedTimestep is greater than zero, the simulation will run at the fixed time step and may run multiple steps per frame.
//! This time can range from 0.0f to SystemConfiguration::m_maxTimestep. Where 0.0 time indicates that the simulation did not execute any steps this frame.
//! When SystemConfiguration::m_fixedTimestep equal to or less than zero, the simulation will step once with a time between deltaTime and SystemConfiguration::m_maxTimestep.
//! Example Advanced users might do to self manage Advancing the physics state.
//! @code{ .cpp }
//! //The following would be somewhere in the client code game update loop.
//! if (auto* system = AZ::Interface<AzPhysics::SystemInterface>::Get())
//! {
//! SceneInterfaceList& sceneList = system->GetAllScenes();
//! //Here you may want to order the update of the scenes, or only update specific scenes.
//! for(auto scene : sceneList)
//! {
//! //StartSimulation + FinishSimulation must be called in order.
//! scene->StartSimulation(deltatime); //spawns physics jobs.
//!
//! //here you can perform other actions that do not rely on up to date physics.
//!
//! scene->FinishSimulation(); //blocks until jobs are complete and swap buffers.
//! }
//! }
//! @endcode
//! @param deltaTime This is the time in seconds to simulate physics for this tick (60fps = 0.01666667). Typically the frame deltaTime of the game loop.
virtual void Simulate(float deltaTime) = 0;
//! Add a scene to the physics simulation.
//! @param config This is the Configuration of the scene to add.
//! @return Returns a SceneHandle of the Scene created or InvalidSceneHandle if it fails.
virtual SceneHandle AddScene(const SceneConfiguration& config) = 0;
//! Add multiple scenes to the physics simulation.
//! @param configs This is the list of SceneConfiguration objects to add.
//! @return Returns a list of SceneHandle objects for each created Scene. Order will be the same as the SceneConfigurationList provided.
virtual SceneHandleList AddScenes(const SceneConfigurationList& configs) = 0;
//! Get the Scene of the requested SceneHandle.
//! @param handle The SceneHandle of the requested scene.
//! @return Returns a SceneInterface pointer if found, otherwise nullptr.
virtual Scene* GetScene(SceneHandle handle) = 0;
//! Get multiple Scenes.
//! @param handles A list of SceneHandle objects to retrieve.
//! @returns Returns a list of SceneInterface pointers. The order is the same as supplied. Pointer may be null if not a valid SceneHandle.
virtual SceneList GetScenes(const SceneHandleList& handles) = 0;
//! Retrieve all current Scenes.
//! @return Returns a list of SceneInterface pointers.
virtual SceneList& GetAllScenes() = 0;
//! Remove the requested Scene if it exists.
//! @param handle The handle to the scene to remove.
virtual void RemoveScene(SceneHandle handle) = 0;
//! Remove many Scenes if they exist.
//! @param handles A list of handles to each scene to remove.
virtual void RemoveScenes(const SceneHandleList& handles) = 0;
//! Removes All Scenes.
virtual void RemoveAllScenes() = 0;
//! Get the current SystemConfiguration used to initialize the Physics system.
virtual const SystemConfiguration* GetConfiguration() const = 0;
//! Update the SystemConfiguration.
//! This will apply the new configuration, some properties may require the reinitialization of the physics system and will tear down all Scenes and Simulation bodies.
//! @param newConfig The new configuration to apply.
//! @param forceReinitialization Flag to force a reinitialization of the physics system. Default false.
virtual void UpdateConfiguration(const SystemConfiguration* newConfig, bool forceReinitialization = false) = 0;
//! Update the default material library.
//! @param materialLibrary The new material library asset to use.
virtual void UpdateDefaultMaterialLibrary(const AZ::Data::Asset<Physics::MaterialLibraryAsset>& materialLibrary) = 0;
//! Accessor to get the current Material Library. This is also available in the PhysXSystemConfiguration.
virtual const AZ::Data::Asset<Physics::MaterialLibraryAsset>& GetDefaultMaterialLibrary() const = 0;
//! Update the current default scene configuration.
//! This is the configuration used to to create scenes without a custom configuration.
//! @param sceneConfiguration The new configuration to apply.
virtual void UpdateDefaultSceneConfiguration(const SceneConfiguration& sceneConfiguration) = 0;
//! Gets the current default scene configuration.
virtual const SceneConfiguration& GetDefaultSceneConfiguration() const = 0;
//! Register to receive notifications when the Physics System is Initialized.
//! @param handler The handler to receive the event.
void RegisterSystemInitializedEvent(SystemEvents::OnInitializedEvent::Handler& handler) { handler.Connect(m_initializeEvent); }
//! Register to receive notifications when the Physics System is reinitialized.
//! @param handler The handler to receive the event.
void RegisterSystemReInitializedEvent(SystemEvents::OnReinitializedEvent::Handler& handler) { handler.Connect(m_reinitializeEvent); }
//! Register to receive notifications when the Physics System shuts down.
//! @param handler The handler to receive the event.
void RegisterSystemShutdownEvent(SystemEvents::OnShutdownEvent::Handler& handler) { handler.Connect(m_shutdownEvent); }
//! Register to receive notifications when the Physics System simulation begins.
//! @param handler The handler to receive the event.
void RegisterPreSimulateEvent(SystemEvents::OnPresimulateEvent::Handler& handler) { handler.Connect(m_preSimulateEvent); }
//! Register to receive notifications when the Physics System simulation ends.
//! @param handler The handler to receive the event.
void RegisterPostSimulateEvent(SystemEvents::OnPostsimulateEvent::Handler& handler) { handler.Connect(m_postSimulateEvent); }
//! Register to receive notifications when the SystemConfiguration changes.
//! @param handler The handler to receive the event.
void RegisterSystemConfigurationChangedEvent(SystemEvents::OnConfigurationChangedEvent::Handler& handler) { handler.Connect(m_configChangeEvent); }
//! Register a handler to receive an event when the default material library changes.
//! @param handler The handler to receive the event.
void RegisterOnDefaultMaterialLibraryChangedEventHandler(SystemEvents::OnDefaultMaterialLibraryChangedEvent::Handler& handler) { handler.Connect(m_onDefaultMaterialLibraryChangedEvent); }
//! Register a handler to receive an event when the default SceneConfiguration changes.
//! @param handler The handler to receive the event.
void RegisterOnDefaultSceneConfigurationChangedEventHandler(SystemEvents::OnDefaultSceneConfigurationChangedEvent::Handler& handler) { handler.Connect(m_onDefaultSceneConfigurationChangedEvent); }
protected:
SystemEvents::OnInitializedEvent m_initializeEvent;
SystemEvents::OnReinitializedEvent m_reinitializeEvent;
SystemEvents::OnShutdownEvent m_shutdownEvent;
SystemEvents::OnPresimulateEvent m_preSimulateEvent;
SystemEvents::OnPostsimulateEvent m_postSimulateEvent;
SystemEvents::OnConfigurationChangedEvent m_configChangeEvent;
SystemEvents::OnDefaultMaterialLibraryChangedEvent m_onDefaultMaterialLibraryChangedEvent;
SystemEvents::OnDefaultSceneConfigurationChangedEvent m_onDefaultSceneConfigurationChangedEvent;
};
} // namespace AzPhysics
@@ -0,0 +1,25 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
namespace Physics
{
namespace Edit
{
const static AZ::Crc32 CollisionLayerSelector = AZ_CRC("CollisionLayerSelector", 0xae1da12d);
const static AZ::Crc32 CollisionGroupSelector = AZ_CRC("CollisionGroupSelector", 0x7d498664);
const static AZ::Crc32 MaterialIdSelector = AZ_CRC("MaterialIdSelector", 0x494511ad);
}
}
@@ -0,0 +1,106 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/Ragdoll.h>
#include <AzFramework/Physics/ClassConverters.h>
namespace Physics
{
RagdollNodeConfiguration::RagdollNodeConfiguration()
{
m_propertyVisibilityFlags =
PropertyVisibility::InertiaProperties |
PropertyVisibility::Damping |
PropertyVisibility::SleepOptions |
PropertyVisibility::Interpolation |
PropertyVisibility::Gravity |
PropertyVisibility::ContinuousCollisionDetection |
PropertyVisibility::MaxVelocities;
}
void RagdollNodeConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<RagdollNodeConfiguration, RigidBodyConfiguration>()
->Version(4, &ClassConverters::RagdollNodeConfigConverter)
->Field("JointLimit", &RagdollNodeConfiguration::m_jointLimit)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<RagdollNodeConfiguration>("Ragdoll node Configuration", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}
}
void RagdollConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<RagdollConfiguration, WorldBodyConfiguration>()
->Version(2, &ClassConverters::RagdollConfigConverter)
->Field("nodes", &RagdollConfiguration::m_nodes)
->Field("colliders", &RagdollConfiguration::m_colliders)
;
}
}
Physics::RagdollNodeConfiguration* RagdollConfiguration::FindNodeConfigByName(const AZStd::string& nodeName) const
{
auto nodeIterator = AZStd::find_if(m_nodes.begin(), m_nodes.end(), [&nodeName](const Physics::RagdollNodeConfiguration& node)
{
return node.m_debugName == nodeName;
});
if (nodeIterator != m_nodes.end())
{
return const_cast<Physics::RagdollNodeConfiguration*>(nodeIterator);
}
return nullptr;
}
AZ::Outcome<size_t> RagdollConfiguration::FindNodeConfigIndexByName(const AZStd::string& nodeName) const
{
auto nodeIterator = AZStd::find_if(m_nodes.begin(), m_nodes.end(), [&nodeName](const Physics::RagdollNodeConfiguration& node)
{
return node.m_debugName == nodeName;
});
if (nodeIterator != m_nodes.end())
{
return AZ::Success(static_cast<size_t>(nodeIterator - m_nodes.begin()));
}
return AZ::Failure();
}
void RagdollConfiguration::RemoveNodeConfigByName(const AZStd::string& nodeName)
{
const AZ::Outcome<size_t> configIndex = FindNodeConfigIndexByName(nodeName);
if (configIndex.IsSuccess())
{
m_nodes.erase(m_nodes.begin() + configIndex.GetValue());
}
}
} // namespace Physics
@@ -0,0 +1,138 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzFramework/Physics/Character.h>
#include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/WorldBody.h>
#include <AzFramework/Physics/RigidBody.h>
#include <AzFramework/Physics/RagdollPhysicsBus.h>
#include <AzFramework/Physics/Joint.h>
namespace Physics
{
class RagdollNodeConfiguration
: public RigidBodyConfiguration
{
public:
AZ_CLASS_ALLOCATOR(RagdollNodeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(RagdollNodeConfiguration, "{A1796586-85AB-496E-93C9-C5841F03B1AD}", RigidBodyConfiguration);
static void Reflect(AZ::ReflectContext* context);
RagdollNodeConfiguration();
RagdollNodeConfiguration(const RagdollNodeConfiguration& settings) = default;
AZStd::shared_ptr<JointLimitConfiguration> m_jointLimit;
};
class RagdollConfiguration
: public WorldBodyConfiguration
{
public:
AZ_CLASS_ALLOCATOR(RagdollConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(RagdollConfiguration, "{7C96D332-61D8-4C58-A2BF-707716D38D14}", WorldBodyConfiguration);
static void Reflect(AZ::ReflectContext* context);
RagdollConfiguration() = default;
explicit RagdollConfiguration(const RagdollConfiguration& settings) = default;
RagdollNodeConfiguration* FindNodeConfigByName(const AZStd::string& nodeName) const;
AZ::Outcome<size_t> FindNodeConfigIndexByName(const AZStd::string& nodeName) const;
void RemoveNodeConfigByName(const AZStd::string& nodeName);
AZStd::vector<RagdollNodeConfiguration> m_nodes;
CharacterColliderConfiguration m_colliders;
};
/// Represents a single rigid part of a ragdoll.
class RagdollNode
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(RagdollNode, AZ::SystemAllocator, 0);
AZ_RTTI(RagdollNode, "{226D02B7-6138-4F6B-9870-DE5A1C3C5077}", WorldBody);
virtual RigidBody& GetRigidBody() = 0;
virtual ~RagdollNode() = default;
virtual const AZStd::shared_ptr<Physics::Joint>& GetJoint() const = 0;
};
/// A hierarchical collection of rigid bodies connected by joints typically used to physically simulate a character.
class Ragdoll
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(Ragdoll, AZ::SystemAllocator, 0);
AZ_RTTI(Ragdoll, "{01F09602-80EC-4693-A0E7-C2719239044B}", WorldBody);
virtual ~Ragdoll() = default;
/// Inserts the ragdoll into the physics simulation.
/// @param initialState State for initializing the ragdoll positions, orientations and velocities.
virtual void EnableSimulation(const RagdollState& initialState) = 0;
/// Queues inserting the ragdoll into the physics simulation, to be executed before the next physics update.
/// @param initialState State for initializing the ragdoll positions, orientations and velocities.
virtual void EnableSimulationQueued(const RagdollState& initialState) = 0;
/// Removes the ragdoll from physics simulation.
virtual void DisableSimulation() = 0;
/// Queues removing the ragdoll from the physics simulation, to be executed before the next physics update.
virtual void DisableSimulationQueued() = 0;
/// Is the ragdoll currently simulated?
/// @result True in case the ragdoll is simulated, false if not.
virtual bool IsSimulated() = 0;
/// Writes the state for all of the bodies in the ragdoll to the provided output.
/// The caller owns the output state and can safely manipulate it without affecting the physics simulation.
/// @param[out] ragdollState Output parameter to write ragdoll state to.
virtual void GetState(RagdollState& ragdollState) const = 0;
/// Updates the state for all of the bodies in the ragdoll using the input ragdoll state.
/// @param ragdollState The state with which to update the ragdoll.
virtual void SetState(const RagdollState& ragdollState) = 0;
/// Queues updating the state for all of the bodies in the ragdoll using the input ragdoll state.
/// The new state is applied before the next physics update.
/// @param ragdollState The state with which to update the ragdoll.
virtual void SetStateQueued(const RagdollState& ragdollState) = 0;
/// Writes the state for an individual body in the ragdoll to the provided output.
/// The caller owns the output state and can safely manipulate it without affecting the physics simulation.
/// @param nodeIndex Index in the physics representation of the character. Note this does not necessarily
/// correspond to indices used in other systems.
/// @param[out] nodeState Output parameter to write the node state to.
virtual void GetNodeState(size_t nodeIndex, RagdollNodeState& nodeState) const = 0;
/// Updates the state for an individual body in the ragdoll using the input node state.
/// @param nodeIndex Index in the physics representation of the character. Note this does not necessarily
/// correspond to indices used in other systems.
/// @param nodeState Contains the state with which to update the individual node.
virtual void SetNodeState(size_t nodeIndex, const RagdollNodeState& nodeState) = 0;
/// Gets a pointer to an individual rigid body in the ragdoll.
/// @param nodeIndex Index in the physics representation of the character. Note this does not necessarily
/// correspond to indices used in other systems.
virtual RagdollNode* GetNode(size_t nodeIndex) const = 0;
/// Returns the number of ragdoll nodes in the ragdoll.
virtual size_t GetNumNodes() const = 0;
/// Returns the id of the world the ragdoll exists in.
virtual AZ::Crc32 GetWorldId() const = 0;
};
}
@@ -0,0 +1,132 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Vector3.h>
namespace Physics
{
enum class SimulationType
{
Kinematic, ///< For ragdoll nodes controlled directly by animation.
Dynamic ///< For ragdoll nodes driven by the physics simulation.
};
/// Contains pose and velocity information, simulation type and joint strength properties for a node in the ragdoll
/// for data transfer between physics and other systems.
struct RagdollNodeState
{
RagdollNodeState() {}
AZ::Vector3 m_position = AZ::Vector3::CreateZero(); ///< Position in world space.
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); ///< Orientation in world space.
AZ::Vector3 m_linearVelocity = AZ::Vector3::CreateZero(); ///< Linear velocity in world space.
AZ::Vector3 m_angularVelocity = AZ::Vector3::CreateZero(); ///< Angular velocity in world space.
SimulationType m_simulationType = SimulationType::Kinematic; ///< Whether the node is kinematic or simulated.
float m_strength = 0.0f; ///< Controls how powerfully the joint attempts to reach a target orientation.
float m_dampingRatio = 1.0f; ///< Whether the joint is underdamped (below 1.0), critically damped (1.0) or overdamped (above 1.0).
};
using RagdollState = AZStd::vector<RagdollNodeState>;
class RagdollNode;
class Ragdoll;
}
namespace AzFramework
{
/// Messages serviced by character physics ragdolls.
class RagdollPhysicsRequests
: public AZ::ComponentBus
{
public:
virtual ~RagdollPhysicsRequests() {}
/// Inserts the ragdoll into the physics simulation.
/// @param initialState Contains pose and velocity information for initializing the ragdoll.
virtual void EnableSimulation(const Physics::RagdollState& initialState) = 0;
/// Queues inserting the ragdoll into the physics simulation, to be executed before the next physics update.
/// @param initialState State for initializing the ragdoll positions, orientations and velocities.
virtual void EnableSimulationQueued(const Physics::RagdollState& initialState) = 0;
/// Removes the ragdoll from physics simulation.
virtual void DisableSimulation() = 0;
/// Queues removing the ragdoll from the physics simulation, to be executed before the next physics update.
virtual void DisableSimulationQueued() = 0;
/// Gets a pointer to the underlying generic ragdoll object.
virtual Physics::Ragdoll* GetRagdoll() = 0;
/// Writes the state for all of the nodes in the ragdoll to the provided output.
/// The caller owns the output state and can safely manipulate it without affecting the physics simulation.
/// @param[out] ragdollState Output parameter for writing the ragdoll state to.
virtual void GetState(Physics::RagdollState& ragdollState) const = 0;
/// Updates the state for all of the nodes in the ragdoll based on the input state.
/// @param ragdollState Contains the state with which to update the ragdoll.
virtual void SetState(const Physics::RagdollState& ragdollState) = 0;
/// Queues updating the state for all of the bodies in the ragdoll using the input ragdoll state.
/// The new state is applied before the next physics update.
/// @param ragdollState The state with which to update the ragdoll.
virtual void SetStateQueued(const Physics::RagdollState& ragdollState) = 0;
/// Writes the state for an individual node in the ragdoll to the provided output.
/// The caller owns the output state and can safely manipulate it without affecting the physics simulation.
/// @param nodeIndex Index in the physics representation of the character. Note this does not necessarily
/// correspond to indices used in other systems.
/// @param[out] nodeState Output parameter for writing the state for the specified node to.
virtual void GetNodeState(size_t nodeIndex, Physics::RagdollNodeState& nodeState) const = 0;
/// Updates the state for an individual body in the ragdoll based on the input state.
/// @param nodeIndex Index in the physics representation of the character. Note this does not necessarily
/// correspond to indices used in other systems.
/// @param nodeState Contains the state with which to update the individual node.
virtual void SetNodeState(size_t nodeIndex, const Physics::RagdollNodeState& nodeState) = 0;
/// Gets a pointer to an individual rigid body in the ragdoll.
/// @param nodeIndex Index in the physics representation of the character. Note this does not necessarily
/// correspond to indices used in other systems.
virtual Physics::RagdollNode* GetNode(size_t nodeIndex) const = 0;
// deprecated Cry functions
/// @cond EXCLUDE_DOCS
/// @deprecated Please use generic ragdoll functions instead of legacy cry ragdoll functions.
/// Causes an entity with a skinned mesh component to disable its current physics and enable ragdoll physics.
virtual void EnterRagdoll() = 0;
/// @cond EXCLUDE_DOCS
/// @deprecated Please use generic ragdoll functions instead of legacy cry ragdoll functions.
/// This will cause the ragdoll component to deactivate itself and re-enable the entity physics component.
virtual void ExitRagdoll() = 0;
};
using RagdollPhysicsRequestBus = AZ::EBus<RagdollPhysicsRequests>;
class RagdollPhysicsNotifications
: public AZ::ComponentBus
{
public:
virtual ~RagdollPhysicsNotifications() = default;
virtual void OnRagdollActivated() = 0;
virtual void OnRagdollDeactivated() = 0;
};
using RagdollPhysicsNotificationBus = AZ::EBus<RagdollPhysicsNotifications>;
} // namespace AzFramework
@@ -0,0 +1,188 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/RigidBody.h>
#include <AzFramework/Physics/ClassConverters.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
namespace Physics
{
float DefaultRigidBodyConfiguration::m_mass = 1.f;
bool DefaultRigidBodyConfiguration::m_computeInertiaTensor = false;
float DefaultRigidBodyConfiguration::m_linearDamping = 0.05f;
float DefaultRigidBodyConfiguration::m_angularDamping = 0.15f;
float DefaultRigidBodyConfiguration::m_sleepMinEnergy = 0.5f;
float DefaultRigidBodyConfiguration::m_maxAngularVelocity = 100.0f;
void RigidBodyConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<RigidBodyConfiguration, WorldBodyConfiguration>()
->Version(3, &ClassConverters::RigidBodyVersionConverter)
->Field("Initial linear velocity", &Physics::RigidBodyConfiguration::m_initialLinearVelocity)
->Field("Initial angular velocity", &Physics::RigidBodyConfiguration::m_initialAngularVelocity)
->Field("Linear damping", &Physics::RigidBodyConfiguration::m_linearDamping)
->Field("Angular damping", &Physics::RigidBodyConfiguration::m_angularDamping)
->Field("Sleep threshold", &Physics::RigidBodyConfiguration::m_sleepMinEnergy)
->Field("Start Asleep", &Physics::RigidBodyConfiguration::m_startAsleep)
->Field("Interpolate Motion", &Physics::RigidBodyConfiguration::m_interpolateMotion)
->Field("Gravity Enabled", &Physics::RigidBodyConfiguration::m_gravityEnabled)
->Field("Simulated", &Physics::RigidBodyConfiguration::m_simulated)
->Field("Kinematic", &Physics::RigidBodyConfiguration::m_kinematic)
->Field("CCD Enabled", &Physics::RigidBodyConfiguration::m_ccdEnabled)
->Field("Compute Mass", &Physics::RigidBodyConfiguration::m_computeMass)
->Field("Mass", &Physics::RigidBodyConfiguration::m_mass)
->Field("Compute COM", &Physics::RigidBodyConfiguration::m_computeCenterOfMass)
->Field("Centre of mass offset", &RigidBodyConfiguration::m_centerOfMassOffset)
->Field("Compute inertia", &RigidBodyConfiguration::m_computeInertiaTensor)
->Field("Inertia tensor", &RigidBodyConfiguration::m_inertiaTensor)
->Field("Property Visibility Flags", &RigidBodyConfiguration::m_propertyVisibilityFlags)
->Field("Maximum Angular Velocity", &RigidBodyConfiguration::m_maxAngularVelocity)
->Field("Include All Shapes In Mass", &RigidBodyConfiguration::m_includeAllShapesInMassCalculation)
->Field("CCD Min Advance", &RigidBodyConfiguration::m_ccdMinAdvanceCoefficient)
->Field("CCD Friction", &RigidBodyConfiguration::m_ccdFrictionEnabled)
;
}
}
AZ::Crc32 RigidBodyConfiguration::GetPropertyVisibility(PropertyVisibility property) const
{
return (m_propertyVisibilityFlags & property) != 0 ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
void RigidBodyConfiguration::SetPropertyVisibility(PropertyVisibility property, bool isVisible)
{
if (isVisible)
{
m_propertyVisibilityFlags |= property;
}
else
{
m_propertyVisibilityFlags &= ~property;
}
}
AZ::Crc32 RigidBodyConfiguration::GetInitialVelocitiesVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::InitialVelocities);
}
AZ::Crc32 RigidBodyConfiguration::GetInertiaSettingsVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::InertiaProperties);
}
AZ::Crc32 RigidBodyConfiguration::GetInertiaVisibility() const
{
bool visible = ((m_propertyVisibilityFlags & PropertyVisibility::InertiaProperties) != 0) && !m_computeInertiaTensor;
return visible ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
AZ::Crc32 RigidBodyConfiguration::GetCoMVisibility() const
{
bool visible = ((m_propertyVisibilityFlags & PropertyVisibility::InertiaProperties) != 0) && !m_computeCenterOfMass;
return visible ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
AZ::Crc32 RigidBodyConfiguration::GetMassVisibility() const
{
bool visible = ((m_propertyVisibilityFlags & PropertyVisibility::InertiaProperties) != 0) && !m_computeMass;
return visible ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
AZ::Crc32 RigidBodyConfiguration::GetDampingVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::Damping);
}
AZ::Crc32 RigidBodyConfiguration::GetSleepOptionsVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::SleepOptions);
}
AZ::Crc32 RigidBodyConfiguration::GetInterpolationVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::Interpolation);
}
AZ::Crc32 RigidBodyConfiguration::GetGravityVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::Gravity);
}
AZ::Crc32 RigidBodyConfiguration::GetKinematicVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::Kinematic);
}
AZ::Crc32 RigidBodyConfiguration::GetCCDVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::ContinuousCollisionDetection);
}
AZ::Crc32 RigidBodyConfiguration::GetMaxVelocitiesVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::MaxVelocities);
}
Physics::MassComputeFlags RigidBodyConfiguration::GetMassComputeFlags() const
{
using Physics::MassComputeFlags;
MassComputeFlags flags = MassComputeFlags::NONE;
if (m_computeCenterOfMass)
{
flags = flags | MassComputeFlags::COMPUTE_COM;
}
if (m_computeInertiaTensor)
{
flags = flags | MassComputeFlags::COMPUTE_INERTIA;
}
if (m_computeMass)
{
flags = flags | MassComputeFlags::COMPUTE_MASS;
}
if (m_includeAllShapesInMassCalculation)
{
flags = flags | MassComputeFlags::INCLUDE_ALL_SHAPES;
}
return flags;
}
void RigidBodyConfiguration::SetMassComputeFlags(MassComputeFlags flags)
{
using Physics::MassComputeFlags;
m_computeCenterOfMass = MassComputeFlags::COMPUTE_COM == (flags & MassComputeFlags::COMPUTE_COM);
m_computeInertiaTensor = MassComputeFlags::COMPUTE_INERTIA == (flags & MassComputeFlags::COMPUTE_INERTIA);
m_computeMass = MassComputeFlags::COMPUTE_MASS == (flags & MassComputeFlags::COMPUTE_MASS);
m_includeAllShapesInMassCalculation =
MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & MassComputeFlags::INCLUDE_ALL_SHAPES);
}
bool RigidBodyConfiguration::IsCCDEnabled() const
{
return m_ccdEnabled;
}
RigidBody::RigidBody(const RigidBodyConfiguration& settings)
: WorldBody(settings)
{
}
} // namespace Physics
@@ -0,0 +1,239 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzFramework/Physics/WorldBody.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
namespace
{
class ReflectContext;
}
namespace Physics
{
class ShapeConfiguration;
class World;
class Shape;
/// Default values used for initializing RigidBodySettings.
/// These can be modified by Physics Implementation gems.
// LUMBERYARD_DEPRECATED(LY-114472) - DefaultRigidBodyConfiguration values are not shared across modules.
// Use RigidBodyConfiguration default values.
struct DefaultRigidBodyConfiguration
{
static float m_mass;
static bool m_computeInertiaTensor;
static float m_linearDamping;
static float m_angularDamping;
static float m_sleepMinEnergy;
static float m_maxAngularVelocity;
};
enum class MassComputeFlags : AZ::u8
{
NONE = 0,
//! Flags indicating whether a certain mass property should be auto-computed or not.
COMPUTE_MASS = 1,
COMPUTE_INERTIA = 1 << 1,
COMPUTE_COM = 1 << 2,
//! If set, non-simulated shapes will also be included in the mass properties calculation.
INCLUDE_ALL_SHAPES = 1 << 3,
DEFAULT = COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS
};
class RigidBodyConfiguration
: public WorldBodyConfiguration
{
public:
AZ_CLASS_ALLOCATOR(RigidBodyConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBodyConfiguration, "{ACFA8900-8530-4744-AF00-AA533C868A8E}", WorldBodyConfiguration);
static void Reflect(AZ::ReflectContext* context);
enum PropertyVisibility : AZ::u16
{
InitialVelocities = 1 << 0, ///< Whether the initial linear and angular velocities are visible.
InertiaProperties = 1 << 1, ///< Whether the whole category of inertia properties (mass, compute inertia,
///< inertia tensor etc) is visible.
Damping = 1 << 2, ///< Whether linear and angular damping are visible.
SleepOptions = 1 << 3, ///< Whether the sleep threshold and start asleep options are visible.
Interpolation = 1 << 4, ///< Whether the interpolation option is visible.
Gravity = 1 << 5, ///< Whether the effected by gravity option is visible.
Kinematic = 1 << 6, ///< Whether the option to make the body kinematic is visible.
ContinuousCollisionDetection = 1 << 7, ///< Whether the option to enable continuous collision detection is visible.
MaxVelocities = 1 << 8 ///< Whether upper limits on velocities are visible.
};
RigidBodyConfiguration() = default;
RigidBodyConfiguration(const RigidBodyConfiguration& settings) = default;
// Visibility functions.
AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const;
void SetPropertyVisibility(PropertyVisibility property, bool isVisible);
AZ::Crc32 GetInitialVelocitiesVisibility() const;
/// Returns whether the whole category of inertia settings (mass, inertia, center of mass offset etc) is visible.
AZ::Crc32 GetInertiaSettingsVisibility() const;
/// Returns whether the individual inertia tensor field is visible or is hidden because the compute inertia option is selected.
AZ::Crc32 GetInertiaVisibility() const;
/// Returns whether the mass field is visible or is hidden because compute mass option is selected.
AZ::Crc32 GetMassVisibility() const;
/// Returns whether the individual centre of mass offset field is visible or is hidden because compute CoM option is selected.
AZ::Crc32 GetCoMVisibility() const;
AZ::Crc32 GetDampingVisibility() const;
AZ::Crc32 GetSleepOptionsVisibility() const;
AZ::Crc32 GetInterpolationVisibility() const;
AZ::Crc32 GetGravityVisibility() const;
AZ::Crc32 GetKinematicVisibility() const;
AZ::Crc32 GetCCDVisibility() const;
AZ::Crc32 GetMaxVelocitiesVisibility() const;
MassComputeFlags GetMassComputeFlags() const;
void SetMassComputeFlags(MassComputeFlags flags);
bool IsCCDEnabled() const;
// Basic initial settings.
AZ::Vector3 m_initialLinearVelocity = AZ::Vector3::CreateZero();
AZ::Vector3 m_initialAngularVelocity = AZ::Vector3::CreateZero();
AZ::Vector3 m_centerOfMassOffset = AZ::Vector3::CreateZero();
// Simulation parameters.
float m_mass = DefaultRigidBodyConfiguration::m_mass;
AZ::Matrix3x3 m_inertiaTensor = AZ::Matrix3x3::CreateIdentity();
float m_linearDamping = DefaultRigidBodyConfiguration::m_linearDamping;
float m_angularDamping = DefaultRigidBodyConfiguration::m_angularDamping;
float m_sleepMinEnergy = DefaultRigidBodyConfiguration::m_sleepMinEnergy;
float m_maxAngularVelocity = DefaultRigidBodyConfiguration::m_maxAngularVelocity;
// Visibility settings.
AZ::u16 m_propertyVisibilityFlags = (std::numeric_limits<AZ::u16>::max)();
bool m_startAsleep = false;
bool m_interpolateMotion = false;
bool m_gravityEnabled = true;
bool m_simulated = true;
bool m_kinematic = false;
bool m_ccdEnabled = false; ///< Whether continuous collision detection is enabled.
float m_ccdMinAdvanceCoefficient = 0.15f; ///< Coefficient affecting how granularly time is subdivided in CCD.
bool m_ccdFrictionEnabled = false; ///< Whether friction is applied when resolving CCD collisions.
bool m_computeCenterOfMass = true;
bool m_computeInertiaTensor = true;
bool m_computeMass = true;
//! If set, non-simulated shapes will also be included in the mass properties calculation.
bool m_includeAllShapesInMassCalculation = false;
};
/// Dynamic rigid body.
class RigidBody
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(RigidBody, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBody, "{156E459F-7BB7-4B4E-ADA0-2130D96B7E80}", WorldBody);
public:
RigidBody() = default;
explicit RigidBody(const RigidBodyConfiguration& settings);
virtual void AddShape(AZStd::shared_ptr<Shape> shape) = 0;
virtual void RemoveShape(AZStd::shared_ptr<Shape> shape) = 0;
virtual AZ::u32 GetShapeCount() { return 0; }
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
virtual AZ::Vector3 GetCenterOfMassWorld() const = 0;
virtual AZ::Vector3 GetCenterOfMassLocal() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaWorld() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaLocal() const = 0;
virtual float GetMass() const = 0;
virtual float GetInverseMass() const = 0;
virtual void SetMass(float mass) = 0;
virtual void SetCenterOfMassOffset(const AZ::Vector3& comOffset) = 0;
/// Retrieves the velocity at center of mass; only linear velocity, no rotational velocity contribution.
virtual AZ::Vector3 GetLinearVelocity() const = 0;
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
virtual AZ::Vector3 GetAngularVelocity() const = 0;
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) = 0;
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
virtual float GetLinearDamping() const = 0;
virtual void SetLinearDamping(float damping) = 0;
virtual float GetAngularDamping() const = 0;
virtual void SetAngularDamping(float damping) = 0;
virtual bool IsAwake() const = 0;
virtual void ForceAsleep() = 0;
virtual void ForceAwake() = 0;
virtual float GetSleepThreshold() const = 0;
virtual void SetSleepThreshold(float threshold) = 0;
virtual bool IsKinematic() const = 0;
virtual void SetKinematic(bool kinematic) = 0;
virtual void SetKinematicTarget(const AZ::Transform& targetPosition) = 0;
virtual bool IsGravityEnabled() const = 0;
virtual void SetGravityEnabled(bool enabled) = 0;
virtual void SetSimulationEnabled(bool enabled) = 0;
virtual void SetCCDEnabled(bool enabled) = 0;
//! Recalculates mass, inertia and center of mass based on the flags passed.
//! @param flags MassComputeFlags specifying which properties should be recomputed.
//! @param centerOfMassOffsetOverride Optional override of the center of mass. Note: This parameter will be ignored if COMPUTE_COM is passed in flags.
//! @param inertiaTensorOverride Optional override of the inertia. Note: This parameter will be ignored if COMPUTE_INERTIA is passed in flags.
//! @param massOverride Optional override of the mass. Note: This parameter will be ignored if COMPUTE_MASS is passed in flags.
virtual void UpdateMassProperties(MassComputeFlags flags = MassComputeFlags::DEFAULT,
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
const float* massOverride = nullptr) = 0;
};
/// Bitwise operators for MassComputeFlags
inline MassComputeFlags operator|(MassComputeFlags lhs, MassComputeFlags rhs)
{
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) | aznumeric_cast<AZ::u8>(rhs));
}
inline MassComputeFlags operator&(MassComputeFlags lhs, MassComputeFlags rhs)
{
return aznumeric_cast<MassComputeFlags>(aznumeric_cast<AZ::u8>(lhs) & aznumeric_cast<AZ::u8>(rhs));
}
/// Static rigid body.
class RigidBodyStatic
: public WorldBody
{
public:
AZ_CLASS_ALLOCATOR(RigidBodyStatic, AZ::SystemAllocator, 0);
AZ_RTTI(RigidBodyStatic, "{13A677BB-7085-4EDB-BCC8-306548238692}", WorldBody);
virtual void AddShape(const AZStd::shared_ptr<Shape>& shape) = 0;
virtual AZ::u32 GetShapeCount() { return 0; }
virtual AZStd::shared_ptr<Shape> GetShape(AZ::u32 /*index*/) { return nullptr; }
};
} // namespace Physics
@@ -0,0 +1,89 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Physics/Casts.h>
namespace Physics
{
class RigidBody;
class RigidBodyRequests
: public AZ::ComponentBus
{
public:
using MutexType = AZStd::mutex;
virtual void EnablePhysics() = 0;
virtual void DisablePhysics() = 0;
virtual bool IsPhysicsEnabled() const = 0;
virtual AZ::Vector3 GetCenterOfMassWorld() const = 0;
virtual AZ::Vector3 GetCenterOfMassLocal() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaWorld() const = 0;
virtual AZ::Matrix3x3 GetInverseInertiaLocal() const = 0;
virtual float GetMass() const = 0;
virtual float GetInverseMass() const = 0;
virtual void SetMass(float mass) = 0;
virtual void SetCenterOfMassOffset(const AZ::Vector3& comOffset) = 0;
virtual AZ::Vector3 GetLinearVelocity() const = 0;
virtual void SetLinearVelocity(const AZ::Vector3& velocity) = 0;
virtual AZ::Vector3 GetAngularVelocity() const = 0;
virtual void SetAngularVelocity(const AZ::Vector3& angularVelocity) = 0;
virtual AZ::Vector3 GetLinearVelocityAtWorldPoint(const AZ::Vector3& worldPoint) const = 0;
virtual void ApplyLinearImpulse(const AZ::Vector3& impulse) = 0;
virtual void ApplyLinearImpulseAtWorldPoint(const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) = 0;
virtual void ApplyAngularImpulse(const AZ::Vector3& angularImpulse) = 0;
virtual float GetLinearDamping() const = 0;
virtual void SetLinearDamping(float damping) = 0;
virtual float GetAngularDamping() const = 0;
virtual void SetAngularDamping(float damping) = 0;
virtual bool IsAwake() const = 0;
virtual void ForceAsleep() = 0;
virtual void ForceAwake() = 0;
virtual float GetSleepThreshold() const = 0;
virtual void SetSleepThreshold(float threshold) = 0;
virtual bool IsKinematic() const = 0;
virtual void SetKinematic(bool kinematic) = 0;
virtual void SetKinematicTarget(const AZ::Transform& targetPosition) = 0;
virtual bool IsGravityEnabled() const = 0;
virtual void SetGravityEnabled(bool enabled) = 0;
virtual void SetSimulationEnabled(bool enabled) = 0;
virtual AZ::Aabb GetAabb() const = 0;
virtual Physics::RigidBody* GetRigidBody() = 0;
virtual Physics::RayCastHit RayCast(const Physics::RayCastRequest& request) = 0;
};
using RigidBodyRequestBus = AZ::EBus<RigidBodyRequests>;
class RigidBodyNotifications
: public AZ::ComponentBus
{
public:
virtual void OnPhysicsEnabled() = 0;
virtual void OnPhysicsDisabled() = 0;
};
using RigidBodyNotificationBus = AZ::EBus<RigidBodyNotifications>;
}
@@ -0,0 +1,162 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "ScriptCanvasPhysicsUtils.h"
#include <AzFramework/Physics/WorldBody.h>
namespace Physics
{
namespace ReflectionUtils
{
CollisionNotificationBusBehaviorHandler::CollisionNotificationBusBehaviorHandler()
{
m_events.resize(FN_MAX);
SetEvent(&CollisionNotificationBusBehaviorHandler::OnCollisionBeginDummy, "OnCollisionBegin");
SetEvent(&CollisionNotificationBusBehaviorHandler::OnCollisionPersistDummy, "OnCollisionPersist");
SetEvent(&CollisionNotificationBusBehaviorHandler::OnCollisionEndDummy, "OnCollisionEnd");
}
void CollisionNotificationBusBehaviorHandler::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Physics::Contact>()
->Version(1)
->Field("Position", &Physics::Contact::m_position)
->Field("Normal", &Physics::Contact::m_normal)
->Field("Impulse", &Physics::Contact::m_impulse)
->Field("Separation", &Physics::Contact::m_separation)
;
serializeContext->Class<Physics::CollisionEvent>()
->Field("Contacts", &Physics::CollisionEvent::m_contacts)
;
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<Physics::Contact>("Contact")
->Property("Position", BehaviorValueProperty(&Physics::Contact::m_position))
->Property("Normal", BehaviorValueProperty(&Physics::Contact::m_normal))
->Property("Impulse", BehaviorValueProperty(&Physics::Contact::m_impulse))
->Property("Separation", BehaviorValueProperty(&Physics::Contact::m_separation))
;
behaviorContext->Class<Physics::CollisionEvent>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Property("Contacts", BehaviorValueProperty(&Physics::CollisionEvent::m_contacts))
;
behaviorContext->EBus<Physics::CollisionNotificationBus>("CollisionNotificationBus")
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Handler<CollisionNotificationBusBehaviorHandler>()
;
}
}
void CollisionNotificationBusBehaviorHandler::Disconnect()
{
BusDisconnect();
}
bool CollisionNotificationBusBehaviorHandler::Connect(AZ::BehaviorValueParameter* id)
{
return AZ::Internal::EBusConnector<CollisionNotificationBusBehaviorHandler>::Connect(this, id);
}
bool CollisionNotificationBusBehaviorHandler::IsConnected()
{
return AZ::Internal::EBusConnector<CollisionNotificationBusBehaviorHandler>::IsConnected(this);
}
bool CollisionNotificationBusBehaviorHandler::IsConnectedId(AZ::BehaviorValueParameter* id)
{
return AZ::Internal::EBusConnector<CollisionNotificationBusBehaviorHandler>::IsConnectedId(this, id);
}
int CollisionNotificationBusBehaviorHandler::GetFunctionIndex(const char* functionName) const
{
if (strcmp(functionName, "OnCollisionBegin") == 0) return FN_OnCollisionBegin;
if (strcmp(functionName, "OnCollisionPersist") == 0) return FN_OnCollisionPersist;
if (strcmp(functionName, "OnCollisionEnd") == 0) return FN_OnCollisionEnd;
return -1;
}
void CollisionNotificationBusBehaviorHandler::OnCollisionBeginDummy(AZ::EntityId /*entityId*/, const AZStd::vector<Contact>& /*contacts*/)
{
// This is never invoked, and only used for type deduction when calling SetEvent
}
void CollisionNotificationBusBehaviorHandler::OnCollisionPersistDummy(AZ::EntityId /*entityId*/, const AZStd::vector<Contact>& /*contacts*/)
{
// This is never invoked, and only used for type deduction when calling SetEvent
}
void CollisionNotificationBusBehaviorHandler::OnCollisionEndDummy(AZ::EntityId /*entityId*/)
{
// This is never invoked, and only used for type deduction when calling SetEvent
}
void CollisionNotificationBusBehaviorHandler::OnCollisionBegin(const CollisionEvent& collisionEvent)
{
Call(FN_OnCollisionBegin, collisionEvent.m_body2->GetEntityId(), collisionEvent.m_contacts);
}
void CollisionNotificationBusBehaviorHandler::OnCollisionPersist(const CollisionEvent& collisionEvent)
{
Call(FN_OnCollisionPersist, collisionEvent.m_body2->GetEntityId(), collisionEvent.m_contacts);
}
void CollisionNotificationBusBehaviorHandler::OnCollisionEnd(const CollisionEvent& collisionEvent)
{
Call(FN_OnCollisionEnd, collisionEvent.m_body2->GetEntityId());
}
void WorldNotificationBusBehaviorHandler::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<WorldNotificationBus>("WorldNotificationBus")
->Handler<WorldNotificationBusBehaviorHandler>()
;
}
}
void WorldNotificationBusBehaviorHandler::OnPrePhysicsTick(float deltaTime)
{
Call(FN_OnPrePhysicsTick, deltaTime);
}
void WorldNotificationBusBehaviorHandler::OnPrePhysicsSubtick(float fixedDeltaTime)
{
Call(FN_OnPrePhysicsSubtick, fixedDeltaTime);
}
void WorldNotificationBusBehaviorHandler::OnPostPhysicsSubtick(float fixedDeltaTime)
{
Call(FN_OnPostPhysicsSubtick, fixedDeltaTime);
}
void WorldNotificationBusBehaviorHandler::OnPostPhysicsTick(float deltaTime)
{
Call(FN_OnPostPhysicsTick, deltaTime);
}
int WorldNotificationBusBehaviorHandler::GetPhysicsTickOrder()
{
int order = WorldNotifications::Scripting;
CallResult(order, FN_GetPhysicsTickOrder);
return order;
}
} // namespace ReflectionUtils
} // namespace Physics
@@ -0,0 +1,96 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Physics/TriggerBus.h>
#include <AzFramework/Physics/CollisionNotificationBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzFramework/Physics/World.h>
namespace Physics
{
namespace ReflectionUtils
{
/// Behavior handler which forwards CollisionNotificationBus events to script canvas.
/// Note this class is not using the usual AZ_EBUS_BEHAVIOR_BINDER macro as the signature
/// needs to be changed for script canvas
class CollisionNotificationBusBehaviorHandler
: public CollisionNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
AZ_CLASS_ALLOCATOR(CollisionNotificationBusBehaviorHandler, AZ::SystemAllocator, 0);
AZ_RTTI(CollisionNotificationBusBehaviorHandler, "{A28ACB8F-3429-4F92-88DB-481ACF90EF21}", AZ::BehaviorEBusHandler);
static void Reflect(AZ::ReflectContext* context);
CollisionNotificationBusBehaviorHandler();
// Script Canvas Signature
void OnCollisionBeginDummy(AZ::EntityId entityId, const AZStd::vector<Contact>& contacts);
void OnCollisionPersistDummy(AZ::EntityId entityId, const AZStd::vector<Contact>& contacts);
void OnCollisionEndDummy(AZ::EntityId entityId);
using EventFunctionsParameterPack = AZStd::Internal::pack_traits_arg_sequence
<
decltype(&CollisionNotificationBusBehaviorHandler::OnCollisionBeginDummy),
decltype(&CollisionNotificationBusBehaviorHandler::OnCollisionPersistDummy),
decltype(&CollisionNotificationBusBehaviorHandler::OnCollisionEndDummy)
>;
private:
enum
{
FN_OnCollisionBegin,
FN_OnCollisionPersist,
FN_OnCollisionEnd,
FN_MAX
};
// AZ::BehaviorEBusHandler
void Disconnect() override;
bool Connect(AZ::BehaviorValueParameter* id = nullptr) override;
bool IsConnected() override;
bool IsConnectedId(AZ::BehaviorValueParameter* id) override;
int GetFunctionIndex(const char* functionName) const override;
// CollisionNotificationBus
void OnCollisionBegin(const CollisionEvent& triggerEvent) override;
void OnCollisionPersist(const CollisionEvent& triggerEvent) override;
void OnCollisionEnd(const CollisionEvent& triggerEvent) override;
};
class WorldNotificationBusBehaviorHandler
: public WorldNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
static void Reflect(AZ::ReflectContext* context);
AZ_EBUS_BEHAVIOR_BINDER(WorldNotificationBusBehaviorHandler, "{D8B108B8-9126-4C66-B857-377BA5DB3062}", AZ::SystemAllocator
, OnPrePhysicsTick
, OnPrePhysicsSubtick
, OnPostPhysicsSubtick
, OnPostPhysicsTick
, GetPhysicsTickOrder
);
// WorldNotificationBus ...
void OnPrePhysicsTick(float deltaTime) override;
void OnPrePhysicsSubtick(float fixedDeltaTime) override;
void OnPostPhysicsSubtick(float fixedDeltaTime) override;
void OnPostPhysicsTick(float deltaTime) override;
int GetPhysicsTickOrder() override;
};
} // namespace ReflectionUtils
} // namespace Physics
@@ -0,0 +1,138 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "Shape.h"
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Physics/ClassConverters.h>
namespace Physics
{
const float ColliderConfiguration::ContactOffsetDelta = 1e-2f;
void ColliderConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ColliderConfiguration>()
->Version(4, &Physics::ClassConverters::ColliderConfigurationConverter)
->Field("CollisionLayer", &ColliderConfiguration::m_collisionLayer)
->Field("CollisionGroupId", &ColliderConfiguration::m_collisionGroupId)
->Field("Visible", &ColliderConfiguration::m_visible)
->Field("Trigger", &ColliderConfiguration::m_isTrigger)
->Field("Simulated", &ColliderConfiguration::m_isSimulated)
->Field("InSceneQueries", &ColliderConfiguration::m_isInSceneQueries)
->Field("Exclusive", &ColliderConfiguration::m_isExclusive)
->Field("Position", &ColliderConfiguration::m_position)
->Field("Rotation", &ColliderConfiguration::m_rotation)
->Field("MaterialSelection", &ColliderConfiguration::m_materialSelection)
->Field("propertyVisibilityFlags", &ColliderConfiguration::m_propertyVisibilityFlags)
->Field("ColliderTag", &ColliderConfiguration::m_tag)
->Field("RestOffset", &ColliderConfiguration::m_restOffset)
->Field("ContactOffset", &ColliderConfiguration::m_contactOffset)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<ColliderConfiguration>("ColliderConfiguration", "Configuration for a collider")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_isTrigger, "Trigger", "If set, this collider will act as a trigger")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetIsTriggerVisibility)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_isSimulated, "Simulated", "If set, this collider will partake in collision in the physical simulation")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetIsTriggerVisibility)
->Attribute(AZ::Edit::Attributes::ReadOnly, &ColliderConfiguration::m_isTrigger) // Trigger shapes ignore simulated flag, making it read-only in the UI.
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_isInSceneQueries, "In Scene Queries", "If set, this collider will be visible for scene queries")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetIsTriggerVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_collisionLayer, "Collision Layer", "The collision layer assigned to the collider")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetCollisionLayerVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_collisionGroupId, "Collides With", "The collision group containing the layers this collider collides with")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetCollisionLayerVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_position, "Offset", "Local offset from the rigid body")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetOffsetVisibility)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_rotation, "Rotation", "Local rotation relative to the rigid body")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetOffsetVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_materialSelection, "Physics Material", "Select physics material library and which materials to use for the shape")
->Attribute(AZ::Edit::Attributes::Visibility, &ColliderConfiguration::GetMaterialSelectionVisibility)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_tag, "Tag", "Tag used to identify colliders from one another")
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_restOffset, "Rest offset",
"Bodies will come to rest separated by the sum of their rest offset values (must be less than contact offset)")
->Attribute(AZ::Edit::Attributes::Step, 1e-2f)
->Attribute(AZ::Edit::Attributes::Max, 50.0f)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &ColliderConfiguration::OnRestOffsetChanged)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &ColliderConfiguration::m_contactOffset, "Contact offset",
"Bodies will begin to generate contacts when within the sum of their contact offsets (must exceed rest offset)")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 1e-2f)
->Attribute(AZ::Edit::Attributes::Max, 50.0f)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &ColliderConfiguration::OnContactOffsetChanged)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly)
;
}
}
}
void ColliderConfiguration::OnRestOffsetChanged()
{
if (m_restOffset > m_contactOffset - ContactOffsetDelta)
{
m_restOffset = AZ::GetMax(0.0f, m_contactOffset - ContactOffsetDelta);
}
}
void ColliderConfiguration::OnContactOffsetChanged()
{
if (m_contactOffset < m_restOffset + ContactOffsetDelta)
{
m_contactOffset = AZ::GetMin(1.0f, m_restOffset + ContactOffsetDelta);
}
}
AZ::Crc32 ColliderConfiguration::GetPropertyVisibility(PropertyVisibility property) const
{
return (m_propertyVisibilityFlags & property) != 0 ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
void ColliderConfiguration::SetPropertyVisibility(PropertyVisibility property, bool isVisible)
{
if (isVisible)
{
m_propertyVisibilityFlags |= property;
}
else
{
m_propertyVisibilityFlags &= ~property;
}
}
AZ::Crc32 ColliderConfiguration::GetIsTriggerVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::IsTrigger);
}
AZ::Crc32 ColliderConfiguration::GetCollisionLayerVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::CollisionLayer);
}
AZ::Crc32 ColliderConfiguration::GetMaterialSelectionVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::MaterialSelection);
}
AZ::Crc32 ColliderConfiguration::GetOffsetVisibility() const
{
return GetPropertyVisibility(PropertyVisibility::Offset);
}
}
@@ -0,0 +1,146 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Physics/Material.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
namespace AZ
{
class Aabb;
}
namespace Physics
{
class Material;
class ColliderConfiguration
{
public:
AZ_CLASS_ALLOCATOR(ColliderConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(ColliderConfiguration, "{16206828-F867-4DA9-9E4E-549B7B2C6174}");
static void Reflect(AZ::ReflectContext* context);
enum PropertyVisibility : AZ::u8
{
CollisionLayer = 1 << 0,
MaterialSelection = 1 << 1,
IsTrigger = 1 << 2,
IsVisible = 1 << 3, ///< @deprecated This property will be removed in a future release.
Offset = 1 << 4 ///< Whether the rotation and position offsets should be visible.
};
// Delta to ensure that contact offset is slightly larger than rest offset.
static const float ContactOffsetDelta;
ColliderConfiguration() = default;
ColliderConfiguration(const ColliderConfiguration&) = default;
virtual ~ColliderConfiguration() = default;
AZ::Crc32 GetPropertyVisibility(PropertyVisibility property) const;
void SetPropertyVisibility(PropertyVisibility property, bool isVisible);
AZ::Crc32 GetIsTriggerVisibility() const;
AZ::Crc32 GetCollisionLayerVisibility() const;
AZ::Crc32 GetMaterialSelectionVisibility() const;
AZ::Crc32 GetOffsetVisibility() const;
AzPhysics::CollisionLayer m_collisionLayer; ///< Which collision layer is this collider on.
AzPhysics::CollisionGroups::Id m_collisionGroupId; ///< Which layers does this collider collide with.
bool m_isTrigger = false; ///< Should this shape act as a trigger shape.
bool m_isSimulated = true; ///< Should this shape partake in collision in the physical simulation.
bool m_isInSceneQueries = true; ///< Should this shape partake in scene queries (ray casts, overlap tests, sweeps).
bool m_isExclusive = true; ///< Can this collider be shared between multiple bodies?
AZ::Vector3 m_position = AZ::Vector3::CreateZero(); /// Shape offset relative to the connected rigid body.
AZ::Quaternion m_rotation = AZ::Quaternion::CreateIdentity(); ///< Shape rotation relative to the connected rigid body.
Physics::MaterialSelection m_materialSelection; ///< Materials for the collider.
AZ::u8 m_propertyVisibilityFlags = (std::numeric_limits<AZ::u8>::max)(); ///< Visibility flags for collider.
///< Note: added parenthesis for std::numeric_limits is
///< to avoid collision with `max` macro in uber builds.
bool m_visible = false; ///< @deprecated This property will be removed in a future release. Display the collider in editor view.
AZStd::string m_tag; ///< Identification tag for the collider.
float m_restOffset = 0.0f; ///< Bodies will come to rest separated by the sum of their rest offsets.
float m_contactOffset = 0.02f; ///< Bodies will start to generate contacts when closer than the sum of their contact offsets.
private:
void OnRestOffsetChanged();
void OnContactOffsetChanged();
};
using ShapeConfigurationPair = AZStd::pair<AZStd::shared_ptr<ColliderConfiguration>, AZStd::shared_ptr<ShapeConfiguration>>;
using ShapeConfigurationList = AZStd::vector<ShapeConfigurationPair>;
struct RayCastRequest;
struct RayCastHit;
class Shape
{
public:
AZ_CLASS_ALLOCATOR(Shape, AZ::SystemAllocator, 0);
AZ_RTTI(Shape, "{0A47DDD6-2BD7-43B3-BF0D-2E12CC395C13}");
virtual ~Shape() = default;
virtual void SetMaterial(const AZStd::shared_ptr<Material>& material) = 0;
virtual AZStd::shared_ptr<Material> GetMaterial() const = 0;
virtual void SetCollisionLayer(const AzPhysics::CollisionLayer& layer) = 0;
virtual AzPhysics::CollisionLayer GetCollisionLayer() const = 0;
virtual void SetCollisionGroup(const AzPhysics::CollisionGroup& group) = 0;
virtual AzPhysics::CollisionGroup GetCollisionGroup() const = 0;
virtual void SetName(const char* name) = 0;
virtual void SetLocalPose(const AZ::Vector3& offset, const AZ::Quaternion& rotation) = 0;
virtual AZStd::pair<AZ::Vector3, AZ::Quaternion> GetLocalPose() const = 0;
virtual float GetRestOffset() const = 0;
virtual float GetContactOffset() const = 0;
virtual void SetRestOffset(float restOffset) = 0;
virtual void SetContactOffset(float contactOffset) = 0;
virtual void* GetNativePointer() = 0;
virtual AZ::Crc32 GetTag() const = 0;
virtual void AttachedToActor(void* actor) = 0;
virtual void DetachedFromActor() = 0;
//! Raycast against this shape.
//! @param request Ray parameters in world space.
//! @param worldTransform World transform of this shape.
virtual Physics::RayCastHit RayCast(const Physics::RayCastRequest& worldSpaceRequest, const AZ::Transform& worldTransform) = 0;
//! Raycast against this shape using local coordinates.
//! @param request Ray parameters in local space.
virtual Physics::RayCastHit RayCastLocal(const Physics::RayCastRequest& localSpaceRequest) = 0;
//! Retrieve this shape AABB.
//! @param worldTransform World transform of this shape.
virtual AZ::Aabb GetAabb(const AZ::Transform& worldTransform) const = 0;
//! Retrieve this shape AABB using local coordinates
virtual AZ::Aabb GetAabbLocal() const = 0;
//! Fills in the vertices and indices buffers representing this shape.
//! If vertices are returned but not indices you may assume the vertices are in triangle list format.
//! @param vertices A buffer to be filled with vertices
//! @param indices A buffer to be filled with indices
//! @param optionalBounds Optional AABB that, if provided, will limit the mesh returned to that AABB.
//! Currently only supported by the heightfield shape.
virtual void GetGeometry(AZStd::vector<AZ::Vector3>& vertices, AZStd::vector<AZ::u32>& indices, AZ::Aabb* optionalBounds = nullptr) = 0;
};
} // namespace Physics
@@ -0,0 +1,275 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Physics/PropertyTypes.h>
#include <AzFramework/Physics/SystemBus.h>
namespace Physics
{
void ShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ShapeConfiguration>()
->Version(1)
->Field("Scale", &ShapeConfiguration::m_scale)
;
}
}
void SphereShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SphereShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Radius", &SphereShapeConfiguration::m_radius)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<SphereShapeConfiguration>("SphereShapeConfiguration", "Configuration for sphere collider")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &SphereShapeConfiguration::m_radius, "Radius", "The radius of the sphere collider")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
;
}
}
}
SphereShapeConfiguration::SphereShapeConfiguration(float radius)
: m_radius(radius)
{
}
void BoxShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<BoxShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Configuration", &BoxShapeConfiguration::m_dimensions)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<BoxShapeConfiguration>("BoxShapeConfiguration", "Configuration for box collider")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &BoxShapeConfiguration::m_dimensions, "Dimensions", "Lengths of the box sides")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
;
}
}
}
BoxShapeConfiguration::BoxShapeConfiguration(const AZ::Vector3& boxDimensions)
: m_dimensions(boxDimensions)
{
}
void CapsuleShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CapsuleShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Height", &CapsuleShapeConfiguration::m_height)
->Field("Radius", &CapsuleShapeConfiguration::m_radius)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<CapsuleShapeConfiguration>("CapsuleShapeConfiguration", "Configuration for capsule collider")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &CapsuleShapeConfiguration::m_height, "Height", "The height of the capsule, including caps at each end")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &CapsuleShapeConfiguration::OnHeightChanged)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &CapsuleShapeConfiguration::m_radius, "Radius", "The radius of the capsule")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &CapsuleShapeConfiguration::OnRadiusChanged)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::ValuesOnly)
;
}
}
}
CapsuleShapeConfiguration::CapsuleShapeConfiguration(float height, float radius)
: m_height(height)
, m_radius(radius)
{
}
void CapsuleShapeConfiguration::OnHeightChanged()
{
// check that the height is greater than twice the radius
m_height = AZ::GetMax(m_height, 2 * m_radius + AZ::Constants::FloatEpsilon);
}
void CapsuleShapeConfiguration::OnRadiusChanged()
{
// check that the radius is less than half the height
m_radius = AZ::GetMin(m_radius, (0.5f - AZ::Constants::FloatEpsilon) * m_height);
}
void PhysicsAssetShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<PhysicsAssetShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("PhysicsAsset", &PhysicsAssetShapeConfiguration::m_asset)
->Field("AssetScale", &PhysicsAssetShapeConfiguration::m_assetScale)
->Field("UseMaterialsFromAsset", &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<PhysicsAssetShapeConfiguration>("PhysicsAssetShapeConfiguration", "Configuration for asset shape collider")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_assetScale, "Asset Scale", "The scale of the asset shape")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
->DataElement(AZ::Edit::UIHandlers::Default, &PhysicsAssetShapeConfiguration::m_useMaterialsFromAsset, "Physics Materials from Mesh", "Auto-set physics materials using Mesh's material surfaces names")
;
}
}
}
Physics::ShapeType PhysicsAssetShapeConfiguration::GetShapeType() const
{
return ShapeType::PhysicsAsset;
}
void NativeShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<NativeShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("Scale", &NativeShapeConfiguration::m_nativeShapeScale)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<NativeShapeConfiguration>("NativeShapeConfiguration", "Configuration for native shape collider")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &NativeShapeConfiguration::m_nativeShapeScale, "Scale", "The scale of the native shape")
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
->Attribute(AZ::Edit::Attributes::Step, 0.01f)
;
}
}
}
void CookedMeshShapeConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<CookedMeshShapeConfiguration, ShapeConfiguration>()
->Version(1)
->Field("CookedData", &CookedMeshShapeConfiguration::m_cookedData)
->Field("Type", &CookedMeshShapeConfiguration::m_type)
;
}
}
CookedMeshShapeConfiguration::~CookedMeshShapeConfiguration()
{
ReleaseCachedNativeMesh();
}
CookedMeshShapeConfiguration::CookedMeshShapeConfiguration(const CookedMeshShapeConfiguration& other)
: ShapeConfiguration(other)
, m_cookedData(other.m_cookedData)
, m_type(other.m_type)
, m_cachedNativeMesh(nullptr)
{
}
CookedMeshShapeConfiguration& CookedMeshShapeConfiguration::operator=(const CookedMeshShapeConfiguration& other)
{
ShapeConfiguration::operator=(other);
m_cookedData = other.m_cookedData;
m_type = other.m_type;
// Prevent raw pointer from being copied
m_cachedNativeMesh = nullptr;
return *this;
}
ShapeType CookedMeshShapeConfiguration::GetShapeType() const
{
return ShapeType::CookedMesh;
}
void CookedMeshShapeConfiguration::SetCookedMeshData(const AZ::u8* cookedData,
size_t cookedDataSize, MeshType type)
{
// If the new cooked data is being set, make sure we clear the cached mesh
ReleaseCachedNativeMesh();
m_cookedData.clear();
m_cookedData.insert(m_cookedData.end(), cookedData, cookedData + cookedDataSize);
m_type = type;
}
const AZStd::vector<AZ::u8>& CookedMeshShapeConfiguration::GetCookedMeshData() const
{
return m_cookedData;
}
CookedMeshShapeConfiguration::MeshType CookedMeshShapeConfiguration::GetMeshType() const
{
return m_type;
}
void* CookedMeshShapeConfiguration::GetCachedNativeMesh() const
{
return m_cachedNativeMesh;
}
void CookedMeshShapeConfiguration::SetCachedNativeMesh(void* cachedNativeMesh) const
{
m_cachedNativeMesh = cachedNativeMesh;
}
void CookedMeshShapeConfiguration::ReleaseCachedNativeMesh()
{
if (m_cachedNativeMesh)
{
Physics::SystemRequestBus::Broadcast(
&Physics::SystemRequests::ReleaseNativeMeshObject, m_cachedNativeMesh);
m_cachedNativeMesh = nullptr;
}
}
}
@@ -0,0 +1,201 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Physics
{
/// Used to identify shape configuration type from base class.
enum class ShapeType : AZ::u8
{
Sphere,
Box,
Capsule,
Cylinder,
ConvexHull, ///< Not Supported in physx
TriangleMesh, ///< Not Supported in physx
Native, ///< Native shape configuration if user wishes to bypass generic shape configurations.
PhysicsAsset, ///< Shapes configured in the asset.
CookedMesh, ///< Stores a blob of mesh data cooked for the specific engine.
};
class ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(ShapeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(ShapeConfiguration, "{1FD56C72-6055-4B35-9253-07D432B94E91}");
static void Reflect(AZ::ReflectContext* context);
virtual ~ShapeConfiguration() = default;
virtual ShapeType GetShapeType() const = 0;
AZ::Vector3 m_scale = AZ::Vector3::CreateOne();
};
class SphereShapeConfiguration : public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(SphereShapeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(SphereShapeConfiguration, "{0B9F3D2E-0780-4B0B-BFEE-B41C5FDE774A}", ShapeConfiguration);
static void Reflect(AZ::ReflectContext* context);
explicit SphereShapeConfiguration(float radius = 0.5f);
ShapeType GetShapeType() const override { return ShapeType::Sphere; }
float m_radius = 0.5f;
};
class BoxShapeConfiguration : public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(BoxShapeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(BoxShapeConfiguration, "{E58040ED-3E50-4882-B0E9-525E7A548F8D}", ShapeConfiguration);
static void Reflect(AZ::ReflectContext* context);
explicit BoxShapeConfiguration(const AZ::Vector3& boxDimensions = AZ::Vector3::CreateOne());
ShapeType GetShapeType() const override { return ShapeType::Box; }
AZ::Vector3 m_dimensions = AZ::Vector3::CreateOne();
};
class CapsuleShapeConfiguration : public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(CapsuleShapeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(CapsuleShapeConfiguration, "{19C6A07E-5644-46B7-A49E-48703B56ED32}", ShapeConfiguration);
static void Reflect(AZ::ReflectContext* context);
explicit CapsuleShapeConfiguration(float height = 1.0f, float radius = 0.25f);
ShapeType GetShapeType() const override { return ShapeType::Capsule; }
float m_height = 1.0f;
float m_radius = 0.25f;
private:
void OnHeightChanged();
void OnRadiusChanged();
};
class ConvexHullShapeConfiguration : public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(ConvexHullShapeConfiguration, AZ::SystemAllocator, 0);
ShapeType GetShapeType() const override { return ShapeType::ConvexHull; }
const void* m_vertexData = nullptr;
AZ::u32 m_vertexCount = 0;
AZ::u32 m_vertexStride = 4;
const void* m_planeData = nullptr;
AZ::u32 m_planeCount = 0;
AZ::u32 m_planeStride = 4;
const void* m_adjacencyData = nullptr;
AZ::u32 m_adjacencyCount = 0;
AZ::u32 m_adjacencyStride = 4;
bool m_copyData = true; ///< If set, vertex buffer will be copied in the native physics implementation,
};
class TriangleMeshShapeConfiguration : public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(TriangleMeshShapeConfiguration, AZ::SystemAllocator, 0);
ShapeType GetShapeType() const override { return ShapeType::TriangleMesh; }
const void* m_vertexData = nullptr;
AZ::u32 m_vertexCount = 0;
AZ::u32 m_vertexStride = 4; ///< Data size of a given vertex, e.g. float * 3 = 12.
const void* m_indexData = nullptr;
AZ::u32 m_indexCount = 0;
AZ::u32 m_indexStride = 12; ///< Data size of indices for a given triangle, e.g. AZ::u32 * 3 = 12.
bool m_copyData = true; ///< If set, vertex/index buffers will be copied in the native physics implementation,
///< and don't need to be kept alive by the caller;
};
class PhysicsAssetShapeConfiguration
: public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(PhysicsAssetShapeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(PhysicsAssetShapeConfiguration, "{1C0046D9-BC9E-4F93-9F0E-D62654FB18EA}", ShapeConfiguration);
static void Reflect(AZ::ReflectContext* context);
ShapeType GetShapeType() const override;
AZ::Data::Asset<AZ::Data::AssetData> m_asset{ AZ::Data::AssetLoadBehavior::PreLoad };
AZ::Vector3 m_assetScale = AZ::Vector3::CreateOne();
bool m_useMaterialsFromAsset = true;
};
class NativeShapeConfiguration : public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(NativeShapeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(NativeShapeConfiguration, "{6CB8FE4A-A577-49AF-81F4-4F1AD245859A}", ShapeConfiguration);
static void Reflect(AZ::ReflectContext* context);
ShapeType GetShapeType() const override { return ShapeType::Native; }
void* m_nativeShapePtr = nullptr; ///< Native shape ptr. This will not be serialised
AZ::Vector3 m_nativeShapeScale = AZ::Vector3::CreateOne(); ///< Native shape scale. This will be serialised
};
class CookedMeshShapeConfiguration
: public ShapeConfiguration
{
public:
AZ_CLASS_ALLOCATOR(CookedMeshShapeConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(CookedMeshShapeConfiguration, "{D9E58241-36BB-4A4F-B50C-1736EB7E841F}", ShapeConfiguration);
static void Reflect(AZ::ReflectContext* context);
enum class MeshType : AZ::u8
{
TriangleMesh = 0,
Convex
};
CookedMeshShapeConfiguration() = default;
CookedMeshShapeConfiguration(const CookedMeshShapeConfiguration&);
CookedMeshShapeConfiguration& operator=(const CookedMeshShapeConfiguration&);
~CookedMeshShapeConfiguration();
ShapeType GetShapeType() const override;
//! Sets the cooked data. This will release the cached mesh.
//! Input data has to be in the physics engine specific format.
//! (e.g. in PhysX: result of cookTriangleMesh or cookConvexMesh).
void SetCookedMeshData(const AZ::u8* cookedData, size_t cookedDataSize, MeshType type);
const AZStd::vector<AZ::u8>& GetCookedMeshData() const;
MeshType GetMeshType() const;
void* GetCachedNativeMesh() const;
void SetCachedNativeMesh(void* cachedNativeMesh) const;
private:
void ReleaseCachedNativeMesh();
AZStd::vector<AZ::u8> m_cookedData;
MeshType m_type = MeshType::TriangleMesh;
//! Cached native mesh object (e.g. PxConvexMesh or PxTriangleMesh). This data is not serialized.
mutable void* m_cachedNativeMesh = nullptr;
};
} // namespace Physics
@@ -0,0 +1,316 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Interface/Interface.h>
#include <AzFramework/Asset/GenericAssetHandler.h>
namespace AZ
{
class Vector3;
}
namespace Physics
{
class World;
class WorldBody;
class RigidBody;
class RigidBodyStatic;
class Shape;
class Material;
class MaterialSelection;
class MaterialConfiguration;
class MaterialLibraryAsset;
class WorldBodyConfiguration;
class RigidBodyConfiguration;
class ColliderConfiguration;
class ShapeConfiguration;
class JointLimitConfiguration;
class Joint;
struct RayCastRequest;
struct RayCastResult;
struct ShapeCastRequest;
struct ShapeCastResult;
class CharacterConfiguration;
class Character;
/// Represents a debug vertex (position & color).
struct DebugDrawVertex
{
AZ::Vector3 m_position;
AZ::Color m_color;
DebugDrawVertex(const AZ::Vector3& v, const AZ::Color& c)
: m_position(v)
, m_color(c)
{}
static AZ::Color GetGhostColor() { return AZ::Color(1.0f, 0.7f, 0.0f, 1.0f); }
static AZ::Color GetRigidBodyColor() { return AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); }
static AZ::Color GetSleepingBodyColor() { return AZ::Color(0.5f, 0.5f, 0.5f, 1.0f); }
static AZ::Color GetCharacterColor() { return AZ::Color(0.0f, 1.0f, 1.0f, 1.0f); }
static AZ::Color GetRayColor() { return AZ::Color(0.8f, 0.4f, 0.2f, 1.0f); }
static AZ::Color GetRed() { return AZ::Color(1.0f, 0.0f, 0.0f, 1.0f); }
static AZ::Color GetGreen() { return AZ::Color(0.0f, 1.0f, 0.0f, 1.0f); }
static AZ::Color GetBlue() { return AZ::Color(0.0f, 0.0f, 1.0f, 1.0f); }
static AZ::Color GetWhite() { return AZ::Color(1.0f, 1.0f, 1.0f, 1.0f); }
};
/// Settings structure provided to DebugDrawPhysics to drive debug drawing behavior.
struct DebugDrawSettings
{
using DebugDrawLineCallback = AZStd::function<void(const DebugDrawVertex& from, const DebugDrawVertex& to, const AZStd::shared_ptr<WorldBody>& body, float thickness, void* udata)>;
using DebugDrawTriangleCallback = AZStd::function<void(const DebugDrawVertex& a, const DebugDrawVertex& b, const DebugDrawVertex& c, const AZStd::shared_ptr<WorldBody>& body, void* udata)>;
using DebugDrawTriangleBatchCallback = AZStd::function<void(const DebugDrawVertex* verts, AZ::u32 numVerts, const AZ::u32* indices, AZ::u32 numIndices, const AZStd::shared_ptr<WorldBody>& body, void* udata)>;
DebugDrawLineCallback m_drawLineCB; ///< Required user callback for line drawing.
DebugDrawTriangleBatchCallback m_drawTriBatchCB; ///< User callback for triangle batch drawing. Required if \ref m_isWireframe is false.
bool m_isWireframe = false; ///< Specifies whether or not physics shapes should be draw as wireframe (lines only) or solid triangles.
AZ::u32 m_objectLayers = static_cast<AZ::u32>(~0); ///< Mask specifying which \ref AzFramework::Physics::StandardObjectLayers should be drawn.
AZ::Vector3 m_cameraPos = AZ::Vector3::CreateZero(); ///< Camera position, for limiting objects based on \ref m_drawDistance.
float m_drawDistance = 500.f; ///< Distance from \ref m_cameraPos within which objects will be drawn.
bool m_drawBodyTransforms = false; ///< If enabled, draws transform axes for each body.
void* m_udata = nullptr; ///< Platform specific and/or gem specific optional user data pointer.
void DrawLine(const DebugDrawVertex& from, const DebugDrawVertex& to, const AZStd::shared_ptr<WorldBody>& body, float thickness = 1.0f) { m_drawLineCB(from, to, body, thickness, m_udata); }
void DrawTriangleBatch(const DebugDrawVertex* verts, AZ::u32 numVerts, const AZ::u32* indices, AZ::u32 numIndices, const AZStd::shared_ptr<WorldBody>& body) { m_drawTriBatchCB(verts, numVerts, indices, numIndices, body, m_udata); }
};
/// An interface to get the default physics world for systems that do not support multiple worlds.
class DefaultWorldRequests
: public AZ::EBusTraits
{
public:
using MutexType = AZStd::mutex;
/// Returns the Default world managed by a relevant system.
virtual AZStd::shared_ptr<World> GetDefaultWorld() = 0;
};
typedef AZ::EBus<DefaultWorldRequests> DefaultWorldBus;
/// An interface to get the editor physics world for doing edit time physics queries
class EditorWorldRequests
: public AZ::EBusTraits
{
public:
using MutexType = AZStd::mutex;
/// Returns the Editor world managed editor system component.
virtual AZStd::shared_ptr<World> GetEditorWorld() = 0;
};
using EditorWorldBus = AZ::EBus<EditorWorldRequests>;
class SystemRequestsTraits
: public AZ::EBusTraits
{
public:
using MutexType = AZStd::recursive_mutex;
// EBusTraits
// singleton pattern
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
};
/// Physics system global requests.
class System
{
public:
// Required for AZ::Interface
AZ_TYPE_INFO(System, "{35965894-BFBC-437C-A4FB-E22F3DB09ACF}")
System() = default;
virtual ~System() = default;
// AZ::Interface requires these to be deleted.
System(System&&) = delete;
System& operator=(System&&) = delete;
//////////////////////////////////////////////////////////////////////////
//// General Physics
virtual AZStd::unique_ptr<RigidBodyStatic> CreateStaticRigidBody(const WorldBodyConfiguration& configuration) = 0;
virtual AZStd::unique_ptr<RigidBody> CreateRigidBody(const RigidBodyConfiguration& configuration) = 0;
virtual AZStd::shared_ptr<Shape> CreateShape(const ColliderConfiguration& colliderConfiguration, const ShapeConfiguration& configuration) = 0;
/// Adds an appropriate collider component to the entity based on the provided shape configuration.
/// @param entity Entity where the component should be added to.
/// @param colliderConfiguration Configuration of the collider.
/// @param shapeConfiguration Configuration of the shape of the collider.
/// @param addEditorComponents Tells whether to add the Editor version of the collider component or the Game one.
virtual void AddColliderComponentToEntity(AZ::Entity* entity, const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& shapeConfiguration, bool addEditorComponents = false) = 0;
/// Releases the mesh object created by the physics backend.
/// @param nativeMeshObject Pointer to the mesh object.
virtual void ReleaseNativeMeshObject(void* nativeMeshObject) = 0;
//////////////////////////////////////////////////////////////////////////
//// Physics Materials
virtual AZStd::shared_ptr<Material> CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) = 0;
virtual AZStd::shared_ptr<Material> GetDefaultMaterial() = 0;
virtual AZStd::vector<AZStd::shared_ptr<Material>> CreateMaterialsFromLibrary(const Physics::MaterialSelection& materialSelection) = 0;
/// Updates the collider material selection from the physics asset or sets it to default if there's no asset provided.
/// @param shapeConfiguration The shape information
/// @param colliderConfiguration The collider information
virtual bool UpdateMaterialSelection(const Physics::ShapeConfiguration& shapeConfiguration,
Physics::ColliderConfiguration& colliderConfiguration) = 0;
//////////////////////////////////////////////////////////////////////////
//// Joints
virtual AZStd::vector<AZ::TypeId> GetSupportedJointTypes() = 0;
virtual AZStd::shared_ptr<JointLimitConfiguration> CreateJointLimitConfiguration(AZ::TypeId jointType) = 0;
virtual AZStd::shared_ptr<Joint> CreateJoint(const AZStd::shared_ptr<JointLimitConfiguration>& configuration,
Physics::WorldBody* parentBody, Physics::WorldBody* childBody) = 0;
/// Generates joint limit visualization data in appropriate format to pass to DebugDisplayRequests draw functions.
/// @param configuration The joint configuration to generate visualization data for.
/// @param parentRotation The rotation of the joint's parent body (in the same frame as childRotation).
/// @param childRotation The rotation of the joint's child body (in the same frame as parentRotation).
/// @param scale Scale factor for the output display data.
/// @param angularSubdivisions Level of detail in the angular direction (may be clamped in the implementation).
/// @param radialSubdivisions Level of detail in the radial direction (may be clamped in the implementation).
/// @param[out] vertexBufferOut Used with indexBufferOut to define triangles to be displayed.
/// @param[out] indexBufferOut Used with vertexBufferOut to define triangles to be displayed.
/// @param[out] lineBufferOut Used to define lines to be displayed.
/// @param[out] lineValidityBufferOut Whether each line in the line buffer is part of a valid or violated limit.
virtual void GenerateJointLimitVisualizationData(
const JointLimitConfiguration& configuration,
const AZ::Quaternion& parentRotation,
const AZ::Quaternion& childRotation,
float scale,
AZ::u32 angularSubdivisions,
AZ::u32 radialSubdivisions,
AZStd::vector<AZ::Vector3>& vertexBufferOut,
AZStd::vector<AZ::u32>& indexBufferOut,
AZStd::vector<AZ::Vector3>& lineBufferOut,
AZStd::vector<bool>& lineValidityBufferOut) = 0;
/// Computes parameters such as joint limit local rotations to give the desired initial joint limit orientation.
/// @param jointLimitTypeId The type ID used to identify the particular kind of joint limit configuration to be created.
/// @param parentWorldRotation The rotation in world space of the parent world body associated with the joint.
/// @param childWorldRotation The rotation in world space of the child world body associated with the joint.
/// @param axis Axis used to define the centre for limiting angular degrees of freedom.
/// @param exampleLocalRotations A vector (which may be empty) containing example valid rotations in the local space
/// of the child world body relative to the parent world body, which may optionally be used to help estimate the extents
/// of the joint limit.
virtual AZStd::unique_ptr<JointLimitConfiguration> ComputeInitialJointLimitConfiguration(
const AZ::TypeId& jointLimitTypeId,
const AZ::Quaternion& parentWorldRotation,
const AZ::Quaternion& childWorldRotation,
const AZ::Vector3& axis,
const AZStd::vector<AZ::Quaternion>& exampleLocalRotations) = 0;
//////////////////////////////////////////////////////////////////////////
//// Cooking
/// Cooks a convex mesh and writes it to a file.
/// @param filePath Path to the output file.
/// @param vertices Pointer to beginning of vertex data.
/// @param vertexCount Number of vertices in the mesh.
/// @return Succeeded cooking.
virtual bool CookConvexMeshToFile(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount) = 0;
/// Cooks a convex mesh to a memory buffer.
/// @param vertices Pointer to beginning of vertex data.
/// @param vertexCount Number of vertices in the mesh.
/// @param result The resulting memory buffer.
/// @return Succeeded cooking.
virtual bool CookConvexMeshToMemory(const AZ::Vector3* vertices, AZ::u32 vertexCount, AZStd::vector<AZ::u8>& result) = 0;
/// Cooks a triangular mesh and writes it to a file.
/// @param filePath Path to the output file.
/// @param vertices Pointer to beginning of vertex data.
/// @param vertexCount Number of vertices in the mesh.
/// @param indices Pointer to beginning of index data.
/// @param indexCount Number of indices in the mesh.
/// @return Succeeded cooking.
virtual bool CookTriangleMeshToFile(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount,
const AZ::u32* indices, AZ::u32 indexCount) = 0;
/// Cook a triangular mesh to a memory buffer.
/// @param vertices Pointer to beginning of vertex data.
/// @param vertexCount Number of vertices in the mesh.
/// @param indices Pointer to beginning of index data.
/// @param indexCount Number of indices in the mesh.
/// @param result The resulting memory buffer.
/// @return Succeeded cooking.
virtual bool CookTriangleMeshToMemory(const AZ::Vector3* vertices, AZ::u32 vertexCount,
const AZ::u32* indices, AZ::u32 indexCount, AZStd::vector<AZ::u8>& result) = 0;
};
using SystemRequests = System;
using SystemRequestBus = AZ::EBus<SystemRequests, SystemRequestsTraits>;
/// Physics character system global requests.
class CharacterSystemRequests
: public AZ::EBusTraits
{
public:
// EBusTraits
// singleton pattern
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual ~CharacterSystemRequests() = default;
/// Creates the physics representation used to handle basic character interactions (also known as a character
/// controller).
virtual AZStd::unique_ptr<Character> CreateCharacter(const CharacterConfiguration& characterConfig,
const ShapeConfiguration& shapeConfig, World& world) = 0;
/// Performs any updates related to character controllers which are per-world and not per-character, such as
/// computing character-character interactions.
virtual void UpdateCharacters(World& world, float deltaTime) = 0;
};
typedef AZ::EBus<CharacterSystemRequests> CharacterSystemRequestBus;
/// Physics system global debug requests.
class SystemDebugRequests
: public AZ::EBusTraits
{
public:
// EBusTraits
// singleton pattern
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
/// Draw physics system state.
/// \param settings see \ref DebugDrawSettings.
virtual void DebugDrawPhysics(const DebugDrawSettings& settings) { (void)settings; }
/// Exports an entity's physics body(ies) to the specified filename, if supported by the physics backend.
virtual void ExportEntityPhysics(const AZStd::vector<AZ::EntityId>& ids, const AZStd::string& filename) { (void)ids; (void)filename; }
};
typedef AZ::EBus<SystemDebugRequests> SystemDebugRequestBus;
class SystemNotifications
: public AZ::EBusTraits
{
public:
virtual ~SystemNotifications() {}
virtual void OnWorldCreated(World* /*world*/) {};
virtual void OnPreWorldDestroy(World* /*world*/) {};
};
using SystemNotificationBus = AZ::EBus<SystemNotifications>;
} // namespace Physics
@@ -0,0 +1,288 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Quaternion.h>
namespace Physics
{
constexpr const char * const AZ_TOUCH_BENDING_WINDOW = "AzTouchBending";
// Bone orientation
//
// _ TOP Point Z+ up
// | | ^
// | | |
// |*| (0,0,0) X- <------|--------> X+
// | | |
// |_| BOTTOM Point |
// Z-
/// SpinePoint contains properties of mass, thickness, damping and stiffness
/// for the bone that will be made. The SpinePoint is the BOTTOM point
/// of a Bone.
struct SpinePoint
{
///mass in Kg.
float m_mass;
///If you imagine the Bone to be a cylinder, this is its radius in meters.
float m_thickness;
///A value from 0.0 to 1.0. 0.0 means no damping, lots of back and forth movement around its original pose.
///1.0 means maximum damping, the segment will quickly converge back to its original pose.
float m_damping;
///A value from 0.0 to 1.0. 0.0 means no stiffness, the segment will look like a sad willow,
///It would never return to its original pose.
float m_stiffness;
///Position is in Model Space.
AZ::Vector3 m_position;
};
struct Spine
{
///Index of parent spine. -1 if no parent.
int m_parentSpineIndex;
///Index of the point within the parent spine array of segments.
///-1 if no parent.
int m_parentPointIndex;
///Array of segments.
AZStd::vector<SpinePoint> m_points;
};
typedef void* SpineTreeIDType;
///SpineTree is an archetype. This is basically the AzFramework version
///of CStatObj.SSpine.
struct SpineTree
{
///Unique Identifier Of this SpineTree.
SpineTreeIDType m_spineTreeId;
///A SpineTree ALWAYS contains at least one spine.
AZStd::vector<Spine> m_spines;
///Helper method.
size_t CalculateTotalNumberOfBones() const
{
size_t numberOfBones = 0;
for (const Spine& spine : m_spines)
{
numberOfBones += spine.m_points.size() - 1;
}
return numberOfBones;
}
};
///The Engine side of Touch bending uses this as an opaque handle.
///Only the TouchBending Gem knows what's inside.
///This handle corresponds one-to-one with a unique Vegetation Render Node instance.
struct TouchBendingTriggerHandle;
///The Engine side of Touch bending uses this as an opaque handle.
///Only the TouchBending Gem knows what's inside.
///This handle corresponds one-to-one with a unique CStatObjFoliage instance.
struct TouchBendingSkeletonHandle;
///Used by TouchBending Gem to talk back with the Engine.
class ITouchBendingCallback
{
public:
ITouchBendingCallback() = default;
virtual ~ITouchBendingCallback() = default;
/** @brief Checks if a render node is within e_CullVegActivation radius from the camera
*
* @param privateData Pointer to the Render Node inside the Engine that represents
* the touch bendable entity. From the point of view of the TouchBending Gem this
* is an opaque pointer, but from the point of view of the engine this is a
* CVegetation render node.
* @returns Returns a non-zero SpineTreeIDType if the Render Node is within
* e_CullVegActivation radius from the center of the main camera.
* Otherwise returns zero.
*/
virtual SpineTreeIDType CheckDistanceToCamera(const void* privateData) = 0;
/** @brief Builds a SpineTree archetype object using its SpineTreeIDType.
*
* \p privateData is a CVegetation*
* \p spineTreeId is a CStatObj*
*
* @param privateData Pointer to the Render Node inside the Engine that represents
* the touch bendable entity.
* @param spineTreeId Spine Tree Archetype Identifier as given previously by the Engine.
* @param spineTreeOut Output SpineTree archetype object.
* @returns TRUE if such \p spineTreeId is valid and a SpineTree archetype was successfully built.
* Otherwise returns FALSE.
*/
virtual bool BuildSpineTree(const void* privateData, SpineTreeIDType spineTreeId, SpineTree& spineTreeOut) = 0;
/** TouchBending Gem calls this to notify the Engine that a unique PhysicalizedSkeleton instance was built
* on behalf of \p privateData.
*
* The Engine uses this event to build a CStatObjFoliage to keep track of active touch bendable objects.
* The Engine will keep CStatObjFoliage alive as long as it is touched or for a specific lifetime in seconds
* defined by the CVar e_FoliageBranchesTimeout.
*
* @param privateData Pointer to the Render Node inside the Engine that represents
* the touch bendable entity.
* @param skeletonHandle Opaque pointer that the CStatObjFoliage must keep a copy to. The engine
* should should use it later when calling *Skeleton*() named methods of the TouchBendingBus.
* @returns true if the CStatObjFoliage was created successfully. It may return false only for cases where the CStatObj
* was removed and CStatObjFoliage can only be created if CStatObj is not null.
*/
virtual bool OnPhysicalizedTouchBendingSkeleton(const void* privateData, TouchBendingSkeletonHandle* skeletonHandle) = 0;
}; //class ITouchBendingCallback
//Exact same memory format as QuatTS
//CStatObjFoliage::ComputeSkinningTransformations() uses:
//QuatTS.q[x,y,z] as TOP joint position.
//QuatTS.t[x,y,z] as BOTTOM joint position.
//QuatTS.s CStatObjFoliage::GetSkinningData() reads this value for the first bone of each spine
// as marker for valid data, if less than zero, the spine is skipped by the Skinning code.
// A bone has two joints, TOP and BOTTOM:
//
// _ TOP Z+ up
// | | ^
// | | |
// |*| (0,0,0) X- <------|--------> X+
// | | |
// |_| BOTTOM |
// Z-
struct JointPositions
{
float m_TopJointLocation[3]; //Equivalent to QuatTS.q.xyz (ijk)
float m_qw; //Equivalent to QuatTS.q.w
float m_BottomJointLocation[3]; //Equivalent to QuatTS.t
float m_hasNewData; //Equivalent to QuatTS.s (See description above about CStatObjFoliage::GetSkinningData()).
};
/**
* Replacement of CryPhysics Touch Bending simulation.
*/
class TouchBendingRequest
: public AZ::EBusTraits
{
public:
AZ_RTTI(TouchBendingRequest, "{4E9DE1BE-F0C7-47E7-B315-9302F62D044C}");
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
virtual ~TouchBendingRequest() = default;
/// If the EBUS implementation (aka TouchBending Gem) returns TRUE
/// all of the Physics simulation for Touch Bendable CVegetation is done
/// by the TouchBending Gem with PhysX. If it returns FALSE the engine
/// will default to CryPhysics.
virtual bool IsTouchBendingEnabled() const = 0;
/** @brief Creates a TouchBending Trigger with a simple trigger box.
*
* Initially a TouchBending Trigger is nothing more than a trigger volume. There's
* no skeleton, etc. It will serve as the trigger, that when touched, builds a unique physicalized
* skeleton with the same amount of bones as a SpineTree. Recall that SpineTree is an archetype.
* The TouchBending Gem Builds a TouchBendingSkeletonHandle based on a SpineTree when TouchBendingTriggerHandle is touched.
*
* When the User adds an object via the Vegetation panel of the "Terrain Tool" UI
* this method will be called by the engine.
*
* If the User has enabled the Dynamic Vegetation Gem this method can be called
* at runtime as CVegetation nodes appear within the Camera Frustum.
*
* @param worldTransform This transform includes the scale factor. It is the position of the root
* of the CVegetation node.
* @param worldAabb Axis Aligned Bounding Box in world coordinates of the CVegetation node.
* @param callback The engine gives this callback to the TouchBending Gem for further communication.
* @param callbackPrivateData The Engine gives this opaque handle to TouchBending Gem so the Gem it can properly address it
* address the right Render Node instance when using the \p callback.
* @returns An opaque handle of a TouchBending Trigger Instance created by the TouchBending Gem.
*/
virtual TouchBendingTriggerHandle* CreateTouchBendingTrigger(const AZ::Transform& worldTransform,
const AZ::Aabb&worldAabb, ITouchBendingCallback* callback, const void * callbackPrivateData) = 0;
/** @brief Used by the engine to notify TouchBending Gem about the visibility status of the physicalized skeleton.
*
* @param skeletonHandle Opaque pointer to the physicalized skeleton created by TouchBending Gem.
* @param isVisible if TRUE the engine finds out that the skeleton is visible. If FALSE the engine calculated
* that the skeleton is either totally outside of the Camera Frustum or its distance
* from the camera exceeds the CVAR e_CullVegActivation.
* @param skeletonBoneCountOut It is the responsibility of the TouchBending Gem to fill this out
* with the number of bones available for skinning.
* @param triggerTouchCountOut It is the responsibility of the TouchBending Gem to fill this out
* with the number of objects that are touching the touch bending trigger.
* @returns void
*/
virtual void SetTouchBendingSkeletonVisibility(Physics::TouchBendingSkeletonHandle* skeletonHandle,
bool isVisible, AZ::u32& skeletonBoneCountOut, AZ::u32& triggerTouchCountOut) = 0;
/** @brief The engine calls this when it is deleting the Render Node.
*
* When the User deletes an object via the Vegetation panel of the Rollup Bar (Legacy) UI
* this method will be called by the engine.
*
* If the User has enabled the Dynamic Vegetation Gem this method can be called
* at runtime as CVegetation nodes disappear from the Camera Frustum.
*
* @param handle Opaque handle of the TouchBending trigger instance as created by the
* TouchBending Gem.
* @returns void
*/
virtual void DeleteTouchBendingTrigger(TouchBendingTriggerHandle* handle) = 0;
/** @brief The engine calls this to destroy a physicalized skeleton.
*
* The touch bending trigger remains active.
* This means that in the future something may touch the trigger
* and the skeleton is created again.
*
* @param skeletonHandle Opaque handle of the TouchBending Skeleton as created by the
* TouchBending Gem. The skeleton will be removed from the Physics World.
* @returns
*/
virtual void DephysicalizeTouchBendingSkeleton(TouchBendingSkeletonHandle* skeletonHandle) = 0;
/** Reads the current position of the pair-of-joints per bone of the Skeleton into the \p jointPositions
* buffer.
*
* @param skeletonHandle Opaque handle of the physicalized skeleton instance as created by the
* TouchBending Gem.
* @param jointPositions Buffer where the Top and Bottom Joint positions for each bone
* is written to. Please read the documentation of "struct JointPositions" for clarification.
* @returns void
*/
virtual void ReadJointPositionsOfSkeleton(TouchBendingSkeletonHandle* skeletonHandle, JointPositions* jointPositions) = 0;
};
using TouchBendingBus = AZ::EBus<TouchBendingRequest>;
/// A helper method to test if there's a Gem implementing the TouchBendingBus.
AZ_INLINE bool IsTouchBendingEnabled()
{
bool isEnabled = false;
TouchBendingBus::BroadcastResult(isEnabled, &TouchBendingBus::Events::IsTouchBendingEnabled);
return isEnabled;
}
}
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
namespace Physics
{
struct TriggerEvent;
/// Services provided by the PhysX Trigger Area Component.
class TriggerNotifications
: public AZ::ComponentBus
{
public:
// Ebus Traits. ID'd on trigger entity Id
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const bool EnableEventQueue = true;
using BusIdType = AZ::EntityId;
virtual ~TriggerNotifications() {}
/// Dispatched when an entity enters a trigger. The bus message is ID'd on the triggers entity Id.
virtual void OnTriggerEnter(const TriggerEvent& /*triggerEvent*/) {};
/// Dispatched when an entity exits a trigger. The bus message is ID'd on the triggers entity Id.
virtual void OnTriggerExit(const TriggerEvent& /*triggerEvent*/) {};
};
/// Bus to service the PhysX Trigger Area Component event group.
using TriggerNotificationBus = AZ::EBus<TriggerNotifications>;
} // namespace PhysX
@@ -0,0 +1,274 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "Utils.h"
#include "RigidBody.h"
#include "World.h"
#include "Material.h"
#include "Shape.h"
#include <AzFramework/Physics/AnimationConfiguration.h>
#include <AzFramework/Physics/Character.h>
#include <AzFramework/Physics/Ragdoll.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Physics/CollisionNotificationBus.h>
#include <AzFramework/Physics/TriggerBus.h>
#include <AzFramework/Physics/ScriptCanvasPhysicsUtils.h>
#include <AzFramework/Physics/CollisionBus.h>
#include <AzFramework/Physics/WorldBodyBus.h>
#include <AzFramework/Physics/WindBus.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Collision/CollisionLayers.h>
#include <AzFramework/Physics/Configuration/CollisionConfiguration.h>
#include <AzFramework/Physics/Configuration/SceneConfiguration.h>
namespace Physics
{
namespace ReflectionUtils
{
/// Behavior handler which forwards TriggerNotificationBus events to script canvas.
/// Note this class is not using the usual AZ_EBUS_BEHAVIOR_BINDER macro as the signature
/// needs to be changed for script canvas
class TriggerNotificationBusBehaviorHandler
: public TriggerNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
AZ_CLASS_ALLOCATOR(TriggerNotificationBusBehaviorHandler, AZ::SystemAllocator, 0);
AZ_RTTI(TriggerNotificationBusBehaviorHandler, "{0519A121-16F9-4A97-8D54-092BCD963B95}", AZ::BehaviorEBusHandler);
TriggerNotificationBusBehaviorHandler() {
m_events.resize(FN_MAX);
SetEvent(&TriggerNotificationBusBehaviorHandler::OnTriggerEnterDummy, "OnTriggerEnter");
SetEvent(&TriggerNotificationBusBehaviorHandler::OnTriggerExitDummy, "OnTriggerExit");
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Physics::TriggerNotificationBus>("TriggerNotificationBus")
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Handler<TriggerNotificationBusBehaviorHandler>()
;
}
}
void OnTriggerEnterDummy(AZ::EntityId /*entityId*/)
{
// This is never invoked, and only used for type deduction when calling SetEvent
}
void OnTriggerExitDummy(AZ::EntityId /*entityId*/)
{
// This is never invoked, and only used for type deduction when calling SetEvent
}
using EventFunctionsParameterPack = AZStd::Internal::pack_traits_arg_sequence
<
decltype(&TriggerNotificationBusBehaviorHandler::OnTriggerEnterDummy),
decltype(&TriggerNotificationBusBehaviorHandler::OnTriggerExitDummy)
>;
private:
enum
{
FN_OnTriggerEnter,
FN_OnTriggerExit,
FN_MAX
};
void Disconnect() override
{
BusDisconnect();
}
bool Connect(AZ::BehaviorValueParameter * id = nullptr) override
{
return AZ::Internal::EBusConnector<TriggerNotificationBusBehaviorHandler>::Connect(this, id);
}
bool IsConnected() override
{
return AZ::Internal::EBusConnector<TriggerNotificationBusBehaviorHandler>::IsConnected(this);
}
bool IsConnectedId(AZ::BehaviorValueParameter * id) override
{
return AZ::Internal::EBusConnector<TriggerNotificationBusBehaviorHandler>::IsConnectedId(this, id);
}
int GetFunctionIndex(const char * functionName) const override
{
if (strcmp(functionName, "OnTriggerEnter") == 0) return FN_OnTriggerEnter;
if (strcmp(functionName, "OnTriggerExit") == 0) return FN_OnTriggerExit;
return -1;
}
void OnTriggerEnter(const TriggerEvent& triggerEvent) override
{
Call(FN_OnTriggerEnter, triggerEvent.m_otherBody->GetEntityId());
}
void OnTriggerExit(const TriggerEvent& triggerEvent) override
{
Call(FN_OnTriggerExit, triggerEvent.m_otherBody->GetEntityId());
}
};
void ReflectWorldBus(AZ::ReflectContext* context)
{
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Physics::WorldRequestBus>("WorldRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Event("GetGravity", &Physics::WorldRequestBus::Events::GetGravity)
->Event("SetGravity", &Physics::WorldRequestBus::Events::SetGravity)
;
}
}
void ReflectWorldBodyBus(AZ::ReflectContext* context)
{
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<Physics::WorldBodyRequestBus>("WorldBodyRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Event("EnablePhysics", &WorldBodyRequests::EnablePhysics)
->Event("DisablePhysics", &WorldBodyRequests::DisablePhysics)
->Event("IsPhysicsEnabled", &WorldBodyRequests::IsPhysicsEnabled)
->Event("GetAabb", &WorldBodyRequests::GetAabb)
->Event("RayCast", &WorldBodyRequests::RayCast)
;
}
}
void ReflectWindBus(AZ::ReflectContext* context)
{
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
using WindPositionFuncPtr = AZ::Vector3(WindRequests::*)(const AZ::Vector3&) const;
using WindAabbFuncPtr = AZ::Vector3(WindRequests::*)(const AZ::Aabb&) const;
behaviorContext->EBus<WindRequestsBus>("WindRequestsBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "physics")
->Attribute(AZ::Script::Attributes::Category, "PhysX")
->Event("GetGlobalWind", &WindRequests::GetGlobalWind)
->Event("GetWindAtPosition", static_cast<WindPositionFuncPtr>(&WindRequests::GetWind))
->Event("GetWindInsideAabb", static_cast<WindAabbFuncPtr>(&WindRequests::GetWind))
;
}
}
void ReflectPhysicsApi(AZ::ReflectContext* context)
{
ShapeConfiguration::Reflect(context);
ColliderConfiguration::Reflect(context);
BoxShapeConfiguration::Reflect(context);
CapsuleShapeConfiguration::Reflect(context);
SphereShapeConfiguration::Reflect(context);
PhysicsAssetShapeConfiguration::Reflect(context);
NativeShapeConfiguration::Reflect(context);
CookedMeshShapeConfiguration::Reflect(context);
AzPhysics::CollisionLayer::Reflect(context);
AzPhysics::CollisionGroup::Reflect(context);
AzPhysics::CollisionLayers::Reflect(context);
AzPhysics::CollisionGroups::Reflect(context);
AzPhysics::CollisionConfiguration::Reflect(context);
AzPhysics::SceneConfiguration::Reflect(context);
MaterialConfiguration::Reflect(context);
MaterialLibraryAsset::Reflect(context);
MaterialLibraryAssetReflectionWrapper::Reflect(context);
DefaultMaterialLibraryAssetReflectionWrapper::Reflect(context);
JointLimitConfiguration::Reflect(context);
WorldBodyConfiguration::Reflect(context);
RigidBodyConfiguration::Reflect(context);
RagdollNodeConfiguration::Reflect(context);
RagdollConfiguration::Reflect(context);
CharacterColliderNodeConfiguration::Reflect(context);
CharacterColliderConfiguration::Reflect(context);
AnimationConfiguration::Reflect(context);
CharacterConfiguration::Reflect(context);
ReflectWorldBus(context);
ReflectWorldBodyBus(context);
CollisionFilteringRequests::Reflect(context);
TriggerNotificationBusBehaviorHandler::Reflect(context);
CollisionNotificationBusBehaviorHandler::Reflect(context);
RayCastHit::Reflect(context);
WorldNotificationBusBehaviorHandler::Reflect(context);
ReflectWindBus(context);
}
}
namespace Utils
{
void MakeUniqueString(const AZStd::unordered_set<AZStd::string>& stringSet
, AZStd::string& stringInOut
, AZ::u64 maxStringLength)
{
AZStd::string originalString = stringInOut;
for (size_t nameIndex = 1; nameIndex <= stringSet.size(); ++nameIndex)
{
AZStd::string postFix;
to_string(postFix, nameIndex);
postFix = "_" + postFix;
AZ::u64 trimLength = (originalString.length() + postFix.length()) - maxStringLength;
if (trimLength > 0)
{
stringInOut = originalString.substr(0, originalString.length() - trimLength) + postFix;
}
else
{
stringInOut = originalString + postFix;
}
if (stringSet.find(stringInOut) == stringSet.end())
{
break;
}
}
}
void DeferDelete(AZStd::unique_ptr<Physics::WorldBody> worldBody)
{
if (!worldBody)
{
return;
}
// If the body is in a world, remove it from the world and defer
// the deletion until after the next update to ensure trigger exit events get raised.
if (Physics::World* world = worldBody->GetWorld())
{
world->RemoveBody(*worldBody);
world->DeferDelete(AZStd::move(worldBody));
}
}
bool FilterTag(AZ::Crc32 tag, AZ::Crc32 filterTag)
{
// If the filter tag is empty, then ignore it
return !filterTag || tag == filterTag;
}
}
}
@@ -0,0 +1,59 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
namespace AZ
{
class ReflectContext;
}
namespace Physics
{
class WorldBody;
namespace ReflectionUtils
{
void ReflectPhysicsApi(AZ::ReflectContext* context);
}
namespace Utils
{
/// Reusable unordered set of string names.
using NameSet = AZStd::unordered_set<AZStd::string>;
/// Helper routine for certain physics engines that don't directly expose this property on rigid bodies.
AZ_INLINE AZ::Matrix3x3 InverseInertiaLocalToWorld(const AZ::Vector3& diag, const AZ::Matrix3x3& rotationToWorld)
{
return rotationToWorld * AZ::Matrix3x3::CreateDiagonal(diag) * rotationToWorld.GetTranspose();
}
/// Makes the input string unique for the input set
void MakeUniqueString(const AZStd::unordered_set<AZStd::string>& stringSet
, AZStd::string& stringInOut
, AZ::u64 maxStringLength);
/// Defers the deletion of the body until after the next world update.
/// The body is first removed from the world, and then deleted.
/// This ensures trigger exit events are raised correctly on deleted
/// objects.
void DeferDelete(AZStd::unique_ptr<Physics::WorldBody> body);
//! Returns true if the tag matches the filter tag, or the filter tag is empty
bool FilterTag(AZ::Crc32 tag, AZ::Crc32 filter);
}
}
@@ -0,0 +1,73 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Vector3.h>
namespace Physics
{
//! An interface to get wind values.
class WindRequests
{
public:
AZ_TYPE_INFO(WindRequests, "{87CB77B6-BE47-45FD-AA98-C42C19600EE6}");
WindRequests() = default;
//! AZ::Interface requires these to be deleted.
WindRequests(WindRequests&&) = delete;
WindRequests& operator=(WindRequests&&) = delete;
virtual AZ::Vector3 GetGlobalWind() const = 0;
//! Get accumulated wind value at given world position.
virtual AZ::Vector3 GetWind(const AZ::Vector3& worldPosition) const = 0;
//! Get accumulated wind value inside given AABB volume.
virtual AZ::Vector3 GetWind(const AZ::Aabb& aabb) const = 0;
protected:
~WindRequests() = default;
};
//! Wind requests bus traits. Singleton pattern.
class WindRequestsTraits
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
};
using WindRequestsBus = AZ::EBus<WindRequests, WindRequestsTraits>;
//! Broadcasts notifications when wind state changes - wind providers implement WindRequests bus.
class WindNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
//! Dispatched when global wind value is changed.
virtual void OnGlobalWindChanged() {}
//! Dispatched when local wind volume is moved or changes value.
virtual void OnWindChanged([[maybe_unused]] const AZ::Aabb& aabb) {}
protected:
~WindNotifications() = default;
};
using WindNotificationsBus = AZ::EBus<WindNotifications>;
} // namespace Physics
@@ -0,0 +1,335 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/World.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzCore/Serialization/EditContext.h>
namespace
{
const float TimestepMin = 0.001f; //1000fps
const float TimestepMax = 0.05f; //20fps
}
namespace Physics
{
bool WorldConfiguration::VersionConverter(AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement)
{
// conversion from version 1:
// - remove AutoSimulate
// - remove TerrainGroup
// - remove TerrainLayer
if (classElement.GetVersion() <= 1)
{
classElement.RemoveElementByName(AZ_CRC("AutoSimulate", 0xcce85fd9));
classElement.RemoveElementByName(AZ_CRC("TerrainGroup", 0xca808c89));
classElement.RemoveElementByName(AZ_CRC("TerrainLayer", 0x439be956));
}
// conversion from version 2:
// - remove TerrainMaterials
if (classElement.GetVersion() <= 2)
{
classElement.RemoveElementByName(AZ_CRC("TerrainMaterials", 0x6da24f86));
}
if (classElement.GetVersion() <= 3)
{
classElement.RemoveElementByName(AZ_CRC("HandleSimulationEvents", 0xba508787));
}
//clamping of time steps
if (classElement.GetVersion() <= 4)
{
if (AZ::SerializeContext::DataElementNode* maxTimeStepElement = classElement.FindSubElement(AZ_CRC("MaxTimeStep", 0x34e83795)))
{
float maxTimeStep = TimestepMax;
const bool foundMaxTimeStep = maxTimeStepElement->GetData<float>(maxTimeStep);
if (foundMaxTimeStep)
{
//clamp maxTimeStep between max and min
maxTimeStep = AZ::GetClamp(maxTimeStep, TimestepMin, TimestepMax);
maxTimeStepElement->SetData<float>(context, maxTimeStep);
}
if (AZ::SerializeContext::DataElementNode* fixedTimeStepElement = classElement.FindSubElement(AZ_CRC("FixedTimeStep", 0xd748ea77)))
{
float fixedTimeStep = TimestepMax;
bool foundFixedTimeStep = fixedTimeStepElement->GetData<float>(fixedTimeStep);
if (foundFixedTimeStep)
{
//clamp fixedTimeStep between maxTimeStep and min
fixedTimeStep = AZ::GetClamp(fixedTimeStep, TimestepMin, maxTimeStep);
fixedTimeStepElement->SetData<float>(context, fixedTimeStep);
}
}
}
}
return true;
}
AZ::u32 WorldConfiguration::OnMaxTimeStepChanged()
{
m_fixedTimeStep = AZStd::GetMin(m_fixedTimeStep, GetFixedTimeStepMax()); //since m_maxTimeStep has changed, m_fixedTimeStep might be larger then the max.
return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
}
float WorldConfiguration::GetFixedTimeStepMax() const
{
return m_maxTimeStep;
}
AZ::Crc32 WorldConfiguration::GetCcdVisibility() const
{
return m_enableCcd ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
}
void WorldConfiguration::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<WorldConfiguration>()
->Version(5, &VersionConverter)
->Field("WorldBounds", &WorldConfiguration::m_worldBounds)
->Field("MaxTimeStep", &WorldConfiguration::m_maxTimeStep)
->Field("FixedTimeStep", &WorldConfiguration::m_fixedTimeStep)
->Field("Gravity", &WorldConfiguration::m_gravity)
->Field("RaycastBufferSize", &WorldConfiguration::m_raycastBufferSize)
->Field("SweepBufferSize", &WorldConfiguration::m_sweepBufferSize)
->Field("OverlapBufferSize", &WorldConfiguration::m_overlapBufferSize)
->Field("EnableCcd", &WorldConfiguration::m_enableCcd)
->Field("MaxCcdPasses", &WorldConfiguration::m_maxCcdPasses)
->Field("EnableCcdResweep", &WorldConfiguration::m_enableCcdResweep)
->Field("EnableActiveActors", &WorldConfiguration::m_enableActiveActors)
->Field("EnablePcm", &WorldConfiguration::m_enablePcm)
->Field("BounceThresholdVelocity", &WorldConfiguration::m_bounceThresholdVelocity)
;
if (auto editContext = serializeContext->GetEditContext())
{
editContext->Class<WorldConfiguration>("World Configuration", "Default world configuration")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_worldBounds, "World Bounds", "World bounds")
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_maxTimeStep, "Max Time Step (sec)", "Max time step in seconds")
->Attribute(AZ::Edit::Attributes::Min, TimestepMin)
->Attribute(AZ::Edit::Attributes::Max, TimestepMax)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &WorldConfiguration::OnMaxTimeStepChanged)//need to clamp m_fixedTimeStep if this value changes
->Attribute(AZ::Edit::Attributes::Decimals, 8)
->Attribute(AZ::Edit::Attributes::DisplayDecimals, 8)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_fixedTimeStep, "Fixed Time Step (sec)", "Fixed time step in seconds. Limited by 'Max Time Step'")
->Attribute(AZ::Edit::Attributes::Min, TimestepMin)
->Attribute(AZ::Edit::Attributes::Max, &WorldConfiguration::GetFixedTimeStepMax)
->Attribute(AZ::Edit::Attributes::Decimals, 8)
->Attribute(AZ::Edit::Attributes::DisplayDecimals, 8)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_gravity, "Gravity", "Gravity")
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_raycastBufferSize, "Raycast Buffer Size", "Maximum number of hits from a raycast")
->Attribute(AZ::Edit::Attributes::Min, 1u)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_sweepBufferSize, "Shapecast Buffer Size", "Maximum number of hits from a shapecast")
->Attribute(AZ::Edit::Attributes::Min, 1u)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_overlapBufferSize, "Overlap Query Buffer Size", "Maximum number of hits from a overlap query")
->Attribute(AZ::Edit::Attributes::Min, 1u)
->ClassElement(AZ::Edit::ClassElements::Group, "Continuous Collision Detection")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_enableCcd, "Enable CCD", "Enabled continuous collision detection in the world")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_maxCcdPasses,
"Max CCD Passes", "Maximum number of continuous collision detection passes")
->Attribute(AZ::Edit::Attributes::Visibility, &WorldConfiguration::GetCcdVisibility)
->Attribute(AZ::Edit::Attributes::Min, 1u)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_enableCcdResweep,
"Enable CCD Resweep", "Enable a more accurate but more expensive continuous collision detection method")
->Attribute(AZ::Edit::Attributes::Visibility, &WorldConfiguration::GetCcdVisibility)
->ClassElement(AZ::Edit::ClassElements::Group, "") // end previous group by starting new unnamed expanded group
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_enablePcm, "Persistent Contact Manifold", "Enabled the persistent contact manifold narrow-phase algorithm")
->DataElement(AZ::Edit::UIHandlers::Default, &WorldConfiguration::m_bounceThresholdVelocity,
"Bounce Threshold Velocity", "Relative velocity below which colliding objects will not bounce")
->Attribute(AZ::Edit::Attributes::Min, 0.01f)
;
}
}
}
bool WorldConfiguration::operator==(const WorldConfiguration& other) const
{
constexpr const float timeStepTolerance = 0.0001f;
return m_enableCcd == other.m_enableCcd
&& m_enableCcdResweep == other.m_enableCcdResweep
&& m_enableActiveActors == other.m_enableActiveActors
&& m_enablePcm == other.m_enablePcm
&& m_kinematicFiltering == other.m_kinematicFiltering
&& m_kinematicStaticFiltering == other.m_kinematicStaticFiltering
&& m_customUserData == other.m_customUserData
&& m_raycastBufferSize == other.m_raycastBufferSize
&& m_sweepBufferSize == other.m_sweepBufferSize
&& m_overlapBufferSize == other.m_overlapBufferSize
&& m_maxCcdPasses == other.m_maxCcdPasses
&& AZ::IsClose(m_maxTimeStep, other.m_maxTimeStep, timeStepTolerance)
&& AZ::IsClose(m_fixedTimeStep, other.m_fixedTimeStep, timeStepTolerance)
&& AZ::IsClose(m_bounceThresholdVelocity, other.m_bounceThresholdVelocity)
&& m_gravity.IsClose(other.m_gravity)
&& m_worldBounds == other.m_worldBounds
;
}
bool WorldConfiguration::operator!=(const WorldConfiguration& other) const
{
return !(*this == other);
}
AZStd::vector<OverlapHit> World::OverlapSphere(float radius, const AZ::Transform& pose,
OverlapFilterCallback filterCallback)
{
SphereShapeConfiguration shapeConfiguration;
shapeConfiguration.m_radius = radius;
OverlapRequest overlapRequest;
overlapRequest.m_pose = pose;
overlapRequest.m_shapeConfiguration = &shapeConfiguration;
overlapRequest.m_filterCallback = filterCallback;
return Overlap(overlapRequest);
}
AZStd::vector<OverlapHit> World::OverlapBox(const AZ::Vector3& dimensions, const AZ::Transform& pose,
OverlapFilterCallback filterCallback)
{
BoxShapeConfiguration shapeConfiguration;
shapeConfiguration.m_dimensions = dimensions;
OverlapRequest overlapRequest;
overlapRequest.m_pose = pose;
overlapRequest.m_shapeConfiguration = &shapeConfiguration;
overlapRequest.m_filterCallback = filterCallback;
return Overlap(overlapRequest);
}
AZStd::vector<OverlapHit> World::OverlapCapsule(float height, float radius, const AZ::Transform& pose,
OverlapFilterCallback filterCallback)
{
CapsuleShapeConfiguration shapeConfiguration;
shapeConfiguration.m_height = height;
shapeConfiguration.m_radius = radius;
OverlapRequest overlapRequest;
overlapRequest.m_pose = pose;
overlapRequest.m_shapeConfiguration = &shapeConfiguration;
overlapRequest.m_filterCallback = filterCallback;
return Overlap(overlapRequest);
}
Physics::RayCastHit World::SphereCast(float radius, const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType, AzPhysics::CollisionGroup collisionGroup, FilterCallback filterCallback)
{
SphereShapeConfiguration shapeConfiguration;
shapeConfiguration.m_radius = radius;
ShapeCastRequest request;
request.m_distance = distance;
request.m_start = startPose;
request.m_direction = direction;
request.m_shapeConfiguration = &shapeConfiguration;
request.m_queryType = queryType;
request.m_collisionGroup = collisionGroup;
request.m_filterCallback = filterCallback;
return ShapeCast(request);
}
AZStd::vector<Physics::RayCastHit> World::SphereCastMultiple(float radius, const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType, AzPhysics::CollisionGroup collisionGroup, FilterCallback filterCallback)
{
SphereShapeConfiguration shapeConfiguration;
shapeConfiguration.m_radius = radius;
ShapeCastRequest request;
request.m_distance = distance;
request.m_start = startPose;
request.m_direction = direction;
request.m_shapeConfiguration = &shapeConfiguration;
request.m_queryType = queryType;
request.m_collisionGroup = collisionGroup;
request.m_filterCallback = filterCallback;
return ShapeCastMultiple(request);
}
Physics::RayCastHit World::BoxCast(const AZ::Vector3& boxDimensions, const AZ::Transform& startPose,
const AZ::Vector3& direction, float distance, QueryType queryType, AzPhysics::CollisionGroup collisionGroup, FilterCallback filterCallback)
{
BoxShapeConfiguration shapeConfiguration;
shapeConfiguration.m_dimensions = boxDimensions;
ShapeCastRequest request;
request.m_distance = distance;
request.m_start = startPose;
request.m_direction = direction;
request.m_shapeConfiguration = &shapeConfiguration;
request.m_queryType = queryType;
request.m_collisionGroup = collisionGroup;
request.m_filterCallback = filterCallback;
return ShapeCast(request);
}
AZStd::vector<Physics::RayCastHit> World::BoxCastMultiple(const AZ::Vector3& boxDimensions, const AZ::Transform& startPose,
const AZ::Vector3& direction, float distance, QueryType queryType, AzPhysics::CollisionGroup collisionGroup, FilterCallback filterCallback)
{
BoxShapeConfiguration shapeConfiguration;
shapeConfiguration.m_dimensions = boxDimensions;
ShapeCastRequest request;
request.m_distance = distance;
request.m_start = startPose;
request.m_direction = direction;
request.m_shapeConfiguration = &shapeConfiguration;
request.m_queryType = queryType;
request.m_collisionGroup = collisionGroup;
request.m_filterCallback = filterCallback;
return ShapeCastMultiple(request);
}
Physics::RayCastHit World::CapsuleCast(float capsuleRadius, float capsuleHeight, const AZ::Transform& startPose,
const AZ::Vector3& direction, float distance, QueryType queryType, AzPhysics::CollisionGroup collisionGroup, FilterCallback filterCallback)
{
CapsuleShapeConfiguration shapeConfiguration;
shapeConfiguration.m_height = capsuleHeight;
shapeConfiguration.m_radius = capsuleRadius;
ShapeCastRequest request;
request.m_distance = distance;
request.m_start = startPose;
request.m_direction = direction;
request.m_shapeConfiguration = &shapeConfiguration;
request.m_queryType = queryType;
request.m_collisionGroup = collisionGroup;
request.m_filterCallback = filterCallback;
return ShapeCast(request);
}
AZStd::vector<Physics::RayCastHit> World::CapsuleCastMultiple(float capsuleRadius, float capsuleHeight, const AZ::Transform& startPose,
const AZ::Vector3& direction, float distance, QueryType queryType, AzPhysics::CollisionGroup collisionGroup, FilterCallback filterCallback)
{
CapsuleShapeConfiguration shapeConfiguration;
shapeConfiguration.m_height = capsuleHeight;
shapeConfiguration.m_radius = capsuleRadius;
ShapeCastRequest request;
request.m_distance = distance;
request.m_start = startPose;
request.m_direction = direction;
request.m_shapeConfiguration = &shapeConfiguration;
request.m_queryType = queryType;
request.m_collisionGroup = collisionGroup;
request.m_filterCallback = filterCallback;
return ShapeCastMultiple(request);
}
} // namespace Physics
@@ -0,0 +1,296 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <functional>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Component/EntityId.h>
#include <AzFramework/Physics/WorldBody.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
#include <AzFramework/Physics/Casts.h>
#include <AzFramework/Physics/Configuration/SystemConfiguration.h>
namespace Physics
{
static AZ::Crc32 DefaultPhysicsWorldId = AZ_CRC("AZPhysicalWorld", 0x18f33e24);
static AZ::Crc32 EditorPhysicsWorldId = AZ_CRC("EditorWorld", 0x8d93f191);
class RigidBody;
class WorldBody;
class WorldEventHandler;
class ITriggerEventCallback;
//! Default world configuration.
class WorldConfiguration
{
public:
AZ_CLASS_ALLOCATOR(WorldConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(WorldConfiguration, "{3C87DF50-AD02-4746-B19F-8B7453A86243}")
static void Reflect(AZ::ReflectContext* context);
virtual ~WorldConfiguration() = default;
AZ::Crc32 GetCcdVisibility() const;
AZ::Aabb m_worldBounds = AZ::Aabb::CreateFromMinMax(-AZ::Vector3(1000.f, 1000.f, 1000.f), AZ::Vector3(1000.f, 1000.f, 1000.f));
float m_maxTimeStep = 1.f / 20.f;
float m_fixedTimeStep = AzPhysics::SystemConfiguration::DefaultFixedTimestep;
AZ::Vector3 m_gravity = AZ::Vector3(0.f, 0.f, -9.81f);
void* m_customUserData = nullptr;
AZ::u64 m_raycastBufferSize = 32; //!< Maximum number of hits that will be returned from a raycast.
AZ::u64 m_sweepBufferSize = 32; //!< Maximum number of hits that can be returned from a shapecast.
AZ::u64 m_overlapBufferSize = 32; //!< Maximum number of overlaps that can be returned from an overlap query.
bool m_enableCcd = false; //!< Enables continuous collision detection in the world.
AZ::u32 m_maxCcdPasses = 1; //!< Maximum number of continuous collision detection passes.
bool m_enableCcdResweep = true; //!< Use a more accurate but more expensive continuous collision detection method.
bool m_enableActiveActors = false; //!< Enables pxScene::getActiveActors method.
bool m_enablePcm = true; //!< Enables the persistent contact manifold algorithm to be used as the narrow phase algorithm.
bool m_kinematicFiltering = true; //!< Enables filtering between kinematic/kinematic objects.
bool m_kinematicStaticFiltering = true; //!< Enables filtering between kinematic/static objects.
float m_bounceThresholdVelocity = 2.0f; //!< Relative velocity below which colliding objects will not bounce.
bool operator==(const WorldConfiguration& other) const;
bool operator!=(const WorldConfiguration& other) const;
private:
static bool VersionConverter(AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement);
AZ::u32 OnMaxTimeStepChanged();
float GetFixedTimeStepMax() const;
};
//! Callback for unbounded world queries. These are queries which don't require
//! building the entire result vector, and so saves memory for very large numbers of hits.
//! Called with '{ hit }' repeatedly until there are no more hits, then called with '{}', then never called again.
//! Returns 'true' to continue processing more hits, or 'false' otherwise. If the function ever returns
//! 'false', it is unspecified if the finalizing call '{}' occurs.
template<class HitType>
using HitCallback = AZStd::function<bool(AZStd::optional<HitType>&&)>;
//! Physics world.
class World
: public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = AZ::Crc32;
using MutexType = AZStd::recursive_mutex;
AZ_CLASS_ALLOCATOR(World, AZ::SystemAllocator, 0);
AZ_RTTI(World, "{61832612-9F5C-4A2E-8E11-00655A6DDDD2}");
virtual ~World() = default;
virtual void Update(float deltaTime) = 0;
//! Start the simulation process. This will spawn physics jobs.
virtual void StartSimulation(float deltaTime) = 0;
//! Complete the simulation process. This will wait for the simulation jobs to complete, swap the buffers and process events.
virtual void FinishSimulation() = 0;
//! Perform a raycast in the world returning the closest object that intersected.
virtual RayCastHit RayCast(const RayCastRequest& request) = 0;
//! Perform a raycast in the world returning all objects that intersected.
virtual AZStd::vector<Physics::RayCastHit> RayCastMultiple(const RayCastRequest& request) = 0;
//! Perform a shapecast in the world returning the closest object that intersected.
virtual RayCastHit ShapeCast(const ShapeCastRequest& request) = 0;
//! Perform a shapecast in the world returning all objects that intersected.
virtual AZStd::vector<RayCastHit> ShapeCastMultiple(const ShapeCastRequest& request) = 0;
//! Perform a spherecast in the world returning the closest object that intersected.
Physics::RayCastHit SphereCast(float radius,
const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType = QueryType::StaticAndDynamic,
AzPhysics::CollisionGroup collisionGroup = AzPhysics::CollisionGroup::All,
FilterCallback filterCallback = nullptr);
//! Perform a spherecast in the world returning all objects that intersected.
AZStd::vector<Physics::RayCastHit> SphereCastMultiple(float radius,
const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType = QueryType::StaticAndDynamic,
AzPhysics::CollisionGroup collisionGroup = AzPhysics::CollisionGroup::All,
FilterCallback filterCallback = nullptr);
//! Perform a boxcast in the world returning the closest object that intersected.
Physics::RayCastHit BoxCast(const AZ::Vector3& boxDimensions,
const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType = QueryType::StaticAndDynamic,
AzPhysics::CollisionGroup collisionGroup = AzPhysics::CollisionGroup::All,
FilterCallback filterCallback = nullptr);
//! Perform a boxcast in the world returning all objects that intersected.
AZStd::vector<Physics::RayCastHit> BoxCastMultiple(const AZ::Vector3& boxDimensions,
const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType = QueryType::StaticAndDynamic,
AzPhysics::CollisionGroup collisionGroup = AzPhysics::CollisionGroup::All,
FilterCallback filterCallback = nullptr);
//! Perform a capsule in the world returning all objects that intersected.
Physics::RayCastHit CapsuleCast(float capsuleRadius, float capsuleHeight,
const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType = QueryType::StaticAndDynamic,
AzPhysics::CollisionGroup collisionGroup = AzPhysics::CollisionGroup::All,
FilterCallback filterCallback = nullptr);
//! Perform a capsule in the world returning all objects that intersected.
AZStd::vector<Physics::RayCastHit> CapsuleCastMultiple(float capsuleRadius, float capsuleHeight,
const AZ::Transform& startPose, const AZ::Vector3& direction, float distance,
QueryType queryType = QueryType::StaticAndDynamic,
AzPhysics::CollisionGroup collisionGroup = AzPhysics::CollisionGroup::All,
FilterCallback filterCallback = nullptr);
//! Perform an overlap query returning all objects that overlapped.
virtual AZStd::vector<OverlapHit> Overlap(const OverlapRequest& request) = 0;
//! Perform an unbounded overlap query, calling the provided callback for each
virtual void OverlapUnbounded(const OverlapRequest& request, const HitCallback<OverlapHit>& cb) = 0;
//! Perform an overlap sphere query returning all objects that overlapped.
AZStd::vector<OverlapHit> OverlapSphere(float radius, const AZ::Transform& pose, OverlapFilterCallback filterCallback = nullptr);
//! Perform an overlap box query returning all objects that overlapped.
AZStd::vector<OverlapHit> OverlapBox(const AZ::Vector3& dimensions, const AZ::Transform& pose, OverlapFilterCallback filterCallback = nullptr);
//! Perform an overlap capsule query returning all objects that overlapped.
AZStd::vector<OverlapHit> OverlapCapsule(float height, float radius, const AZ::Transform& pose, OverlapFilterCallback filterCallback = nullptr);
//! Registers a pair of world bodies for which collisions should be suppressed.
virtual void RegisterSuppressedCollision(const WorldBody& body0, const WorldBody& body1) = 0;
//! Unregisters a pair of world bodies for which collisions should be suppressed.
virtual void UnregisterSuppressedCollision(const WorldBody& body0, const WorldBody& body1) = 0;
virtual void AddBody(WorldBody& body) = 0;
virtual void RemoveBody(WorldBody& body) = 0;
virtual AZ::Crc32 GetNativeType() const { return AZ::Crc32(); }
virtual void* GetNativePointer() const { return nullptr; }
virtual void SetSimFunc(std::function<void(void*)> func) = 0;
virtual void SetEventHandler(WorldEventHandler* eventHandler) = 0;
virtual AZ::Vector3 GetGravity() const = 0;
virtual void SetGravity(const AZ::Vector3& gravity) = 0;
virtual void SetMaxDeltaTime(float maxDeltaTime) = 0;
virtual void SetFixedDeltaTime(float fixedDeltaTime) = 0;
virtual void DeferDelete(AZStd::unique_ptr<WorldBody> worldBody) = 0;
//! @brief Similar to SetEventHandler, relevant for Touch Bending.
//!
//! SetEventHandler is useful to catch onTrigger events when the bodies
//! involved were created with the standard physics Components attached to
//! entities. On the other hand, this method was added since Touch Bending, and it is useful
//! for the touch bending simulator to catch onTrigger events of Actors that
//! don't have valid AZ:EntityId.
//!
//! @param triggerCallback Pointer to the callback object that will get the On
//! @returns Nothing.
virtual void SetTriggerEventCallback(ITriggerEventCallback* triggerCallback) = 0;
//! Returns this world's ID.
virtual AZ::Crc32 GetWorldId() const = 0;
};
using WorldRequestBus = AZ::EBus<World>;
using WorldRequests = World;
//! Broadcasts notifications for a specific Physics::World.
//! This bus is addressed on the id of the world.
//! Subscribe to the bus using Physics::DefaultPhysicsWorldId for the default world,
//! or Physics::EditorPhysicsWorldId for the editor world.
class WorldNotifications
: public AZ::EBusTraits
{
public:
enum PhysicsTickOrder
{
Physics = 0, //!< The physics system itself. Should always be first.
Animation = 100, //!< Animation system (ragdolls).
Components = 200, //!< C++ components (force region).
Scripting = 300, //!< Scripting systems (script canvas).
Audio = 400, //!< Audio systems (occlusion).
Default = 1000 //!< All other systems (Game code).
};
virtual ~WorldNotifications() = default;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::MultipleAndOrdered;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = AZ::Crc32;
using MutexType = AZStd::recursive_mutex;
//! Broadcast before each world simulation tick.
//! Each tick may include multiple fixed timestep subticks. For example, if the fixed timestep was set at 10ms,
//! and a game tick of 25ms occurred, 2 fixed timestep subticks would be performed this tick, and the
//! remaining 5ms would be accumulated for the subsequent tick. So, in this example, the events fired would be:
//! OnPrePhysicsTick (for the whole 25ms tick)
//! OnPrePhysicsSubtick (for the first 10ms subtick)
//! OnPostPhysicsSubtick (for the first 10ms subtick)
//! OnPrePhysicsSubtick (for the second 10ms subtick)
//! OnPostPhysicsSubtick (for the second 10ms subtick)
//! OnPostPhysicsTick (for the whole 25ms tick)
//! @param deltaTime The duration of the tick as a whole (which may contain multiple fixed timestep subticks).
virtual void OnPrePhysicsTick([[maybe_unused]] float deltaTime) {}
//! Broadcast before each fixed timestep subtick.
//! @param fixedDeltaTime The duration for fixed timestep subticks.
virtual void OnPrePhysicsSubtick([[maybe_unused]] float fixedDeltaTime) {}
//! Broadcast after each fixed timestep subtick.
//! @param fixedDeltaTime The duration for fixed timestep subticks.
virtual void OnPostPhysicsSubtick([[maybe_unused]] float fixedDeltaTime) {}
//! Broadcast after each world simulation tick.
//! Each tick may include multiple fixed timestep subticks.
//! @param deltaTime The duration of the tick as a whole (which may contain multiple fixed timestep subticks).
virtual void OnPostPhysicsTick([[maybe_unused]] float deltaTime) {}
//! Event fired when the gravity for a world is changed.
//! @param gravity The world's new value for gravity acceleration.
virtual void OnGravityChanged([[maybe_unused]] const AZ::Vector3& gravity) {}
//! Specified the order in which a handler receives WorldNotification events.
//! Users subscribing to this bus can override this function to change
//! the order events are received relative to other systems.
//! @return a value specifying this handler'S relative order.
virtual int GetPhysicsTickOrder() { return Default; }
//! Determines the order in which handlers receive events.
struct BusHandlerOrderCompare
{
//! Compare function used to control physics update order.
//! @param left an instance of the handler to compare.
//! @param right another instance of the handler to compare.
//! @return True if the priority of left is greater than right, false otherwise.
AZ_FORCE_INLINE bool operator()(WorldNotifications* left, WorldNotifications* right) const
{
return left->GetPhysicsTickOrder() < right->GetPhysicsTickOrder();
}
};
};
using WorldNotificationBus = AZ::EBus<WorldNotifications>;
} // namespace Physics
@@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Physics/World.h>
#include <AzFramework/Physics/WorldBody.h>
namespace Physics
{
void WorldBodyConfiguration::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<WorldBodyConfiguration>()
->Version(1)
->Field("name", &WorldBodyConfiguration::m_debugName)
;
}
}
void WorldBody::SetUserData(void* userData)
{
m_customUserData = userData;
}
} // namespace Physics
@@ -0,0 +1,99 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Component/Entity.h>
#include <AzFramework/Physics/Casts.h>
namespace Physics
{
class WorldBody;
class World;
struct RayCastRequest;
struct RayCastHit;
class WorldBodyConfiguration
{
public:
AZ_CLASS_ALLOCATOR(WorldBodyConfiguration, AZ::SystemAllocator, 0);
AZ_RTTI(WorldBodyConfiguration, "{6EEB377C-DC60-4E10-AF12-9626C0763B2D}");
WorldBodyConfiguration() = default;
WorldBodyConfiguration(const WorldBodyConfiguration& settings) = default;
virtual ~WorldBodyConfiguration() = default;
static void Reflect(AZ::ReflectContext* context);
// Basic initial settings.
AZ::Vector3 m_position = AZ::Vector3::CreateZero();
AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity();
AZ::Vector3 m_scale = AZ::Vector3::CreateOne();
// Entity/object association.
AZ::EntityId m_entityId;
void* m_customUserData = nullptr;
// For debugging/tracking purposes only.
AZStd::string m_debugName;
};
class WorldBody
{
public:
AZ_CLASS_ALLOCATOR(WorldBody, AZ::SystemAllocator, 0);
AZ_RTTI(WorldBody, "{4F1D9B44-FC21-4E93-83F0-41B6A78D9B4B}");
friend class World;
public:
WorldBody() = default;
WorldBody(const WorldBodyConfiguration& /*settings*/) {};
virtual ~WorldBody() = default;
virtual AZ::EntityId GetEntityId() const = 0;
void SetUserData(void* userData);
template<typename T>
T* GetUserData() const;
virtual Physics::World* GetWorld() const = 0;
virtual AZ::Transform GetTransform() const = 0;
virtual void SetTransform(const AZ::Transform& transform) = 0;
virtual AZ::Vector3 GetPosition() const = 0;
virtual AZ::Quaternion GetOrientation() const = 0;
virtual AZ::Aabb GetAabb() const = 0;
virtual Physics::RayCastHit RayCast(const RayCastRequest& request) = 0;
virtual AZ::Crc32 GetNativeType() const = 0;
virtual void* GetNativePointer() const = 0;
virtual void AddToWorld(Physics::World&) = 0;
virtual void RemoveFromWorld(Physics::World&) = 0;
private:
void* m_customUserData = nullptr;
};
template<typename T>
T* WorldBody::GetUserData() const
{
return static_cast<T*>(m_customUserData);
}
} // namespace Physics
@@ -0,0 +1,58 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Physics/Casts.h>
namespace Physics
{
class WorldBody;
//! Requests for generic physical world bodies
class WorldBodyRequests
: public AZ::ComponentBus
{
public:
using MutexType = AZStd::recursive_mutex;
//! Enable physics for this body
virtual void EnablePhysics() = 0;
//! Disable physics for this body
virtual void DisablePhysics() = 0;
//! Retrieve whether physics is enabled for this body
virtual bool IsPhysicsEnabled() const = 0;
//! Retrieves the AABB(aligned-axis bounding box) for this body
virtual AZ::Aabb GetAabb() const = 0;
//! Retrieves current WorldBody* for this body. Note: Do not hold a reference to Physics::WorldBody* as could be deleted
virtual Physics::WorldBody* GetWorldBody() = 0;
//! Perform a single-object raycast against this body
virtual Physics::RayCastHit RayCast(const Physics::RayCastRequest& request) = 0;
};
using WorldBodyRequestBus = AZ::EBus<WorldBodyRequests>;
//! Notifications for generic physical world bodies
class WorldBodyNotifications
: public AZ::ComponentBus
{
public:
//! Notification for physics enabled
virtual void OnPhysicsEnabled() = 0;
//! Notification for physics disabled
virtual void OnPhysicsDisabled() = 0;
};
using WorldBodyNotificationBus = AZ::EBus<WorldBodyNotifications>;
}
@@ -0,0 +1,81 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Vector3.h>
namespace Physics
{
class WorldBody;
class Shape;
/// Trigger event raised when an object enters/exits a trigger shape.
struct TriggerEvent
{
Physics::WorldBody* m_triggerBody; ///< The trigger body
Physics::Shape* m_triggerShape; ///< The trigger shape
Physics::WorldBody* m_otherBody; ///< The other body that entered the trigger
Physics::Shape* m_otherShape; ///< The other shape that entered the trigger
};
/// Stores information about the contacts between two overlapping shapes.
struct Contact
{
AZ_CLASS_ALLOCATOR(Contact, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(Contact, "{D7439508-ED10-4395-9D48-1FC3D7815361}");
AZ::Vector3 m_position; ///< The position of the contact
AZ::Vector3 m_normal; ///< The normal of the contact
AZ::Vector3 m_impulse; ///< The impulse force applied to separate the bodies
AZ::u32 m_internalFaceIndex01; ///< Intenal face index of the first shape
AZ::u32 m_internalFaceIndex02; ///< Internal face index of the second shape
float m_separation; ///< The separation
};
/// A collision event raised when two objects, neither of which can be triggers, overlap.
struct CollisionEvent
{
AZ_CLASS_ALLOCATOR(CollisionEvent, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(CollisionEvent, "{7602AA36-792C-4BDC-BDF8-AA16792151A3}");
Physics::WorldBody* m_body1; ///< The first body
Physics::Shape* m_shape1; ///< The shape on the first body
Physics::WorldBody* m_body2; ///< The second body
Physics::Shape* m_shape2; ///< The shape on the second body
AZStd::vector<Contact> m_contacts; ///< The contacts between the two shapes
};
/// Implement this interface and call SetEventHandler on Physics::World
/// to receive events from that world.
/// CActionGame is the default handler for the default physics world which
/// translates these events into bus events.
class WorldEventHandler
{
public:
/// Raised when an object starts overlapping with a trigger shape.
virtual void OnTriggerEnter(const TriggerEvent& triggerEvent) = 0;
/// Raised when an object stops overlapping with a trigger shape.
virtual void OnTriggerExit(const TriggerEvent& triggerEvent) = 0;
/// Raised when two shapes come into contact with each other.
virtual void OnCollisionBegin(const CollisionEvent& collisionEvent) = 0;
/// Raised when two shapes continue contact with each other.
virtual void OnCollisionPersist(const CollisionEvent& collisionEvent) = 0;
/// Raised when two shapes stop contacting each other.
virtual void OnCollisionEnd(const CollisionEvent& collisionEvent) = 0;
};
} //namespace Physics