diff options
Diffstat (limited to 'Runtime/Dynamics/CapsuleCollider.cpp')
-rw-r--r-- | Runtime/Dynamics/CapsuleCollider.cpp | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/Runtime/Dynamics/CapsuleCollider.cpp b/Runtime/Dynamics/CapsuleCollider.cpp new file mode 100644 index 0000000..acfadde --- /dev/null +++ b/Runtime/Dynamics/CapsuleCollider.cpp @@ -0,0 +1,369 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "CapsuleCollider.h" +#include "Runtime/Graphics/Transform.h" +#include "RigidBody.h" +#include "PhysicsManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Filters/AABBUtility.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "Runtime/Misc/BuildSettings.h" + +#define GET_SHAPE() ((class NxCapsuleShape*)m_Shape) + +/* + - i am not sure about the getscaled extents calculation. + +*/ + +using namespace std; + +CapsuleCollider::CapsuleCollider (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +CapsuleCollider::~CapsuleCollider () +{ +} + +void CapsuleCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if (m_Shape) + { + // Apply changed values + SetRadius(m_Radius); + SetHeight(m_Height); + SetCenter(m_Center); + SetDirection (m_Direction); + } + + Super::AwakeFromLoad (awakeMode); +} + +void CapsuleCollider::SmartReset () +{ + Super::SmartReset(); + AABB aabb; + if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb)) + { + Vector3f extents = aabb.GetExtent (); + SetRadius (max (extents.x, extents.z)); + SetHeight (extents.y * 2.0F); + SetCenter (aabb.GetCenter ()); + } + else + { + SetRadius (0.5F); + SetHeight (1.0F); + SetCenter (Vector3f::zero); + } +} + +void CapsuleCollider::Reset () +{ + Super::Reset (); + m_Radius = 0.5F; + m_Height = 1.0F; + m_Center = Vector3f::zero; + m_Direction = 1; +} + + +Vector2f CapsuleCollider::GetGlobalExtents () const +{ + const float kMinSize = 0.00001F; + Vector3f scale = GetComponent (Transform).GetWorldScaleLossy (); + + float absoluteHeight = max (Abs (m_Height * scale.y), kMinSize); + float absoluteRadius = max (Abs (scale.x), Abs (scale.z)) * m_Radius; + + float height = absoluteHeight - absoluteRadius * 2.0F; + + height = max (height, kMinSize); + absoluteRadius = max (absoluteRadius, kMinSize); + + return Vector2f (absoluteRadius, height); +} + +Vector3f CapsuleCollider::GetGlobalCenter () const +{ + return GetComponent (Transform).TransformPoint (m_Center); +} + +AABB CapsuleCollider::GetBounds () +{ + if (m_Shape) + { + // AABB reported by PhysX is inaccurate, as PhysX will just transform the local AABB. + // For Capsules it's very easy to do better. + + Vector2f extents = GetGlobalExtents(); + Matrix4x4f m = CalculateTransform (); + + Vector3f center1 = m.MultiplyPoint3 (Vector3f(0, extents.y * 0.5, 0)); + Vector3f center2 = m.MultiplyPoint3 (Vector3f(0, -extents.y * 0.5, 0)); + + // Make AABB of both global centers + AABB aabb (center1, Vector3f::zero); + aabb.Encapsulate (center2); + + // Expand by global radius + aabb.m_Extent += Vector3f(extents.x, extents.x, extents.x); + return aabb; + } + else + return Super::GetBounds (); +} + +void CapsuleCollider::Create (const Rigidbody* ignoreRigidbody) +{ + if (m_Shape) + Cleanup (); + + NxCapsuleShapeDesc shapeDesc; + Vector2f extents = GetGlobalExtents (); + shapeDesc.radius = extents.x; + shapeDesc.height = extents.y; + + FinalizeCreate (shapeDesc, true, ignoreRigidbody); +} + +void CapsuleCollider::SetRadius (float radius) +{ + if (m_Radius != radius) + { + SetDirty (); + m_Radius = radius; + } + + PROFILE_MODIFY_STATIC_COLLIDER + + if (GET_SHAPE ()) + { + GET_SHAPE ()->setRadius (GetGlobalExtents ().x); + RigidbodyMassDistributionChanged (); + RefreshPhysicsInEditMode(); + UpdateCCDSkeleton (); + } +} + +void CapsuleCollider::SetHeight (float height) +{ + if (m_Height != height) + { + SetDirty (); + m_Height = height; + } + + PROFILE_MODIFY_STATIC_COLLIDER + + if (GET_SHAPE ()) + { + GET_SHAPE ()->setHeight (GetGlobalExtents ().y); + RigidbodyMassDistributionChanged (); + RefreshPhysicsInEditMode(); + UpdateCCDSkeleton (); + } +} + +void CapsuleCollider::SetCenter (const Vector3f& center) +{ + if (m_Center != center) + { + m_Center = center; + SetDirty (); + } + + if (GET_SHAPE ()) + { + TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged | kForceUpdateMass); + RefreshPhysicsInEditMode(); + } +} + +void CapsuleCollider::ScaleChanged () +{ + NxCapsuleShape* shape = GET_SHAPE (); + Vector2f extents = GetGlobalExtents (); + shape->setRadius (extents.x); + shape->setHeight (extents.y); + + UpdateCCDSkeleton (); + + PROFILE_MODIFY_STATIC_COLLIDER +} + +void CapsuleCollider::SetDirection (int dir) +{ + if (m_Direction != dir) + { + SetDirty (); + m_Direction = dir; + } + + TransformChanged (kForceUpdateMass); +} + +Matrix4x4f CapsuleCollider::CalculateTransform () const +{ + Transform& transform = GetComponent (Transform); + Vector3f p = transform.TransformPoint (m_Center); + + Quaternionf rotation = transform.GetRotation (); + if (m_Direction == 2) + rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (90)); + else if (m_Direction == 0) + rotation *= AxisAngleToQuaternion (Vector3f::zAxis, Deg2Rad (90)); + else + rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180)); + + Matrix4x4f matrix; + matrix.SetTR (p, rotation); + + return matrix; +} + +void CapsuleCollider::FetchPoseFromTransform () +{ + AssertIf (HasActorRigidbody ()); + + Transform& transform = GetComponent (Transform); + Vector3f p = transform.TransformPoint (m_Center); + m_Shape->getActor ().setGlobalPosition ((const NxVec3&)p); + + Quaternionf rotation = transform.GetRotation (); + if (m_Direction == 2) + rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (90)); + else if (m_Direction == 0) + rotation *= AxisAngleToQuaternion (Vector3f::zAxis, Deg2Rad (90)); + else + rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180)); + + m_Shape->getActor ().setGlobalOrientationQuat ((const NxQuat&)rotation); +} + +bool CapsuleCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix) +{ + Matrix4x4f childMatrix = CalculateTransform (); + Matrix4x4f parentMatrix = anyParent.GetWorldToLocalMatrixNoScale (); + MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix); + ErrorFiniteParameterReturnFalse(matrix) + return true; +} + +void CapsuleCollider::TransformChanged (int changeMask) +{ + Super::TransformChanged (changeMask); + if (m_Shape) + { + if (m_Shape->getActor ().userData == NULL) + { + PROFILER_AUTO(gStaticColliderMove, this) + FetchPoseFromTransform (); + } + else + { + Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData; + Matrix4x4f matrix; + if (GetRelativeToParentPositionAndRotation (GetComponent (Transform), body->GetComponent (Transform), matrix)) + { + NxMat34 shapeMatrix; + shapeMatrix.setColumnMajor44 (matrix.GetPtr ()); + m_Shape->setLocalPose (shapeMatrix); + } + + if (body->GetGameObjectPtr() != GetGameObjectPtr() || changeMask & (Transform::kScaleChanged | kForceUpdateMass)) + RigidbodyMassDistributionChanged (); + } + + if (changeMask & Transform::kScaleChanged) + ScaleChanged (); + + RefreshPhysicsInEditMode(); + } +} + +NxCCDSkeleton* CapsuleCollider::CreateCCDSkeleton(float scale) +{ + // This is a very simple "approximation" of a capuse, of only 10 vertices. + // Since CCD only kicks in when normal collisions fail, any is probably mostly + // interesting for small objects (as those tend to move faster), I believe that this + // is a reasonable choice for common expected use. NVidia even recommends using + // single vertex meshes for very small objects. + float radius; + float height; + + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + { + Vector2f extents = GetGlobalExtents (); + radius = extents.x * scale; + height = extents.y * scale; + } + else + { + // Prior to 4.0 we incorrectly ignored object scale here. Keep this behaviour for backwards compatibility. + radius = m_Radius * scale; + height = m_Height * 0.5f * scale; + } + + NxU32 triangles[3 * 16] = { + 0,1,2, + 0,2,3, + 0,3,4, + 0,4,1, + + 1,5,6, + 1,6,2, + 2,6,7, + 2,7,3, + 3,7,8, + 3,8,4, + 4,8,5, + 4,5,1, + + 9,6,5, + 9,7,6, + 9,8,7, + 9,5,8, + }; + + Vector3f points[10]; + points[0].Set( 0, -radius - height, 0); + points[1].Set( -radius, -height, 0); + points[2].Set( 0, -height, -radius); + points[3].Set( radius, -height, 0); + points[4].Set( 0, -height, radius); + points[5].Set( -radius, height, 0); + points[6].Set( 0, height, -radius); + points[7].Set( radius, height, 0); + points[8].Set( 0, height, radius); + points[9].Set( 0, radius + height, 0); + + NxSimpleTriangleMesh stm; + stm.numVertices = 10; + stm.numTriangles = 16; + stm.pointStrideBytes = sizeof(Vector3f); + stm.triangleStrideBytes = sizeof(NxU32)*3; + + stm.points = points; + stm.triangles = triangles; + return GetDynamicsSDK().createCCDSkeleton(stm); +} + +template<class TransferFunction> +void CapsuleCollider::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.Align(); + TRANSFER_SIMPLE (m_Radius); + TRANSFER_SIMPLE (m_Height); + TRANSFER (m_Direction); + TRANSFER (m_Center); +} + +IMPLEMENT_CLASS (CapsuleCollider) +IMPLEMENT_OBJECT_SERIALIZE (CapsuleCollider) + +#undef GET_SHAPE +#endif //ENABLE_PHYSICS
\ No newline at end of file |