diff options
author | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
commit | 15740faf9fe9fe4be08965098bbf2947e096aeeb (patch) | |
tree | a730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Dynamics |
Diffstat (limited to 'Runtime/Dynamics')
68 files changed, 15326 insertions, 0 deletions
diff --git a/Runtime/Dynamics/BoxCollider.cpp b/Runtime/Dynamics/BoxCollider.cpp new file mode 100644 index 0000000..c874f12 --- /dev/null +++ b/Runtime/Dynamics/BoxCollider.cpp @@ -0,0 +1,256 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "BoxCollider.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 "NxWrapperUtility.h" +#include "Runtime/Misc/BuildSettings.h" + +#define GET_SHAPE() static_cast<NxBoxShape*> (m_Shape) +BoxCollider::BoxCollider (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +BoxCollider::~BoxCollider () +{ +} + +void BoxCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if (m_Shape) + { + // Apply changed values + SetSize (m_Size); + SetCenter (m_Center); + } + + Super::AwakeFromLoad (awakeMode); +} + +void BoxCollider::SmartReset () +{ + Super::SmartReset(); + + AABB aabb; + if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb)) + { + SetSize (aabb.GetExtent () * 2.0F); + SetCenter (aabb.GetCenter ()); + } + else + { + SetSize (Vector3f::one); + SetCenter (Vector3f::zero); + } +} + +void BoxCollider::Reset () +{ + Super::Reset (); + m_Center = Vector3f::zero; + m_Size = Vector3f::one; +} + +Vector3f BoxCollider::GetGlobalExtents () const +{ + Vector3f extents = GetComponent (Transform).GetWorldScaleLossy (); + extents.Scale (m_Size); + extents *= 0.5F; + extents = Abs (extents); + return extents; +} + +Vector3f BoxCollider::GetGlobalCenter () const +{ + return GetComponent (Transform).TransformPoint (m_Center); +} + +void BoxCollider::Create (const Rigidbody* ignoreRigidbody) +{ + if (m_Shape) + Cleanup (); + + NxBoxShapeDesc shapeDesc; + (Vector3f&)shapeDesc.dimensions = GetGlobalExtents (); + + FinalizeCreate (shapeDesc, true, ignoreRigidbody); +} + +void BoxCollider::SetSize (const Vector3f& size) +{ + if (size != m_Size) + { + SetDirty (); + m_Size = size; + } + + PROFILE_MODIFY_STATIC_COLLIDER + + if (GET_SHAPE ()) + { + GET_SHAPE ()->setDimensions (Vec3ToNx(GetGlobalExtents ())); + RigidbodyMassDistributionChanged (); + RefreshPhysicsInEditMode(); + UpdateCCDSkeleton (); + } +} + +void BoxCollider::SetCenter (const Vector3f& pos) +{ + if (pos != m_Center) + { + SetDirty (); + m_Center = pos; + } + + if (GET_SHAPE ()) + TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged | kForceUpdateMass); +} + +void BoxCollider::ScaleChanged () +{ + PROFILE_MODIFY_STATIC_COLLIDER + + NxBoxShape* shape = GET_SHAPE (); + shape->setDimensions (Vec3ToNx(GetGlobalExtents ())); + + UpdateCCDSkeleton (); +} + +void BoxCollider::FetchPoseFromTransform () +{ + FetchPoseFromTransformUtility (m_Center); +} + +bool BoxCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix) +{ + return GetRelativeToParentPositionAndRotationUtility (transform, anyParent, m_Center, matrix); +} + +void BoxCollider::TransformChanged (int changeMask) +{ + Super::TransformChanged (changeMask); + if (m_Shape) + { + if (m_Shape->getActor ().userData == NULL) + { + PROFILER_AUTO(gStaticColliderMove, this) + FetchPoseFromTransformUtility (m_Center); +/* + AssertIf (m_Shape == NULL); + AssertIf (HasActorRigidbody ()); + Transform& transform = GetComponent (Transform); + Vector3f p = transform.TransformPoint (m_Center); + m_Shape->getActor ().setGlobalPosition ((const NxVec3&)p); + Matrix3x3f m = transform.GetWorldRotationAndScale (); + //OrthoNormalize (m); + m_Shape->getActor ().setGlobalOrientation ((NxMat33&)m); +// m_Shape->getActor ().setGlobalOrientationQuat ((const NxQuat&)transform.GetRotation ()); +*/ + } + else + { + Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData; + Matrix4x4f matrix; + if (GetRelativeToParentPositionAndRotationUtility (GetComponent (Transform), body->GetComponent (Transform), m_Center, 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* BoxCollider::CreateCCDSkeleton(float scale) +{ + Vector3f size = Vector3f::one * scale; + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + size.Scale(GetGlobalExtents()); + else + // Prior to 4.0 we incorrectly ignored object scale here. Keep this behaviour for backwards compatibility. + size.Scale(m_Size * 0.5f); + + NxU32 triangles[3 * 12] = { + 0,1,3, + 0,3,2, + 3,7,6, + 3,6,2, + 1,5,7, + 1,7,3, + 4,6,7, + 4,7,5, + 1,0,4, + 5,1,4, + 4,0,2, + 4,2,6 + }; + + NxVec3 points[8]; + // Static mesh + points[0].set( size.x, -size.y, -size.z); + points[1].set( size.x, -size.y, size.z); + points[2].set( size.x, size.y, -size.z); + points[3].set( size.x, size.y, size.z); + + points[4].set(-size.x, -size.y, -size.z); + points[5].set(-size.x, -size.y, size.z); + points[6].set(-size.x, size.y, -size.z); + points[7].set(-size.x, size.y, size.z); + + if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + { + // This is wrong, as the m_Center transformation is already applied at the shape matrix. + // Keep for backward compatibility. + NxVec3 center(m_Center.x, m_Center.y, m_Center.z); + for (int i=0;i<8;i++) + points[i] += center; + } + + NxSimpleTriangleMesh stm; + stm.numVertices = 8; + stm.numTriangles = 6*2; + stm.pointStrideBytes = sizeof(NxVec3); + stm.triangleStrideBytes = sizeof(NxU32)*3; + + stm.points = points; + stm.triangles = triangles; + return GetDynamicsSDK().createCCDSkeleton(stm); +} + +template<class TransferFunction> +void BoxCollider::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.SetVersion (2); + transfer.Align(); + if (transfer.IsCurrentVersion ()) + { + TRANSFER_SIMPLE (m_Size); + } + else + { + transfer.Transfer (m_Size, "m_Extents"); + m_Size*=2.0F; + } + TRANSFER (m_Center); +} + +IMPLEMENT_CLASS (BoxCollider) +IMPLEMENT_OBJECT_SERIALIZE (BoxCollider) + +#undef GET_SHAPE +#endif //ENABLE_PHYSICS
\ No newline at end of file diff --git a/Runtime/Dynamics/BoxCollider.h b/Runtime/Dynamics/BoxCollider.h new file mode 100644 index 0000000..078467a --- /dev/null +++ b/Runtime/Dynamics/BoxCollider.h @@ -0,0 +1,45 @@ +#ifndef BOXCOLLIDER_H +#define BOXCOLLIDER_H + +#include "Collider.h" +#include "Runtime/Math/Vector3.h" + +class BoxCollider : public Collider +{ + public: + REGISTER_DERIVED_CLASS (BoxCollider, Collider) + DECLARE_OBJECT_SERIALIZE (BoxCollider) + + BoxCollider (MemLabelId label, ObjectCreationMode mode); + + virtual void Reset (); + virtual void SmartReset (); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + const Vector3f& GetSize () const { return m_Size; } + void SetSize (const Vector3f& extents); + + const Vector3f& GetCenter () const { return m_Center; } + void SetCenter (const Vector3f& pos); + + Vector3f GetGlobalExtents () const; + Vector3f GetGlobalCenter () const; + + virtual void TransformChanged (int changeMask); + + protected: + + virtual void FetchPoseFromTransform (); + virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix); + + + virtual void Create (const Rigidbody* ignoreRigidbody); + virtual void ScaleChanged (); + + virtual NxCCDSkeleton* CreateCCDSkeleton(float scale); + + Vector3f m_Center; + Vector3f m_Size; +}; + +#endif 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 diff --git a/Runtime/Dynamics/CapsuleCollider.h b/Runtime/Dynamics/CapsuleCollider.h new file mode 100644 index 0000000..9f579bf --- /dev/null +++ b/Runtime/Dynamics/CapsuleCollider.h @@ -0,0 +1,59 @@ +#ifndef CAPSULECOLLIDER_H +#define CAPSULECOLLIDER_H + +#include "Collider.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Vector2.h" + +class CapsuleCollider : public Collider +{ + public: + REGISTER_DERIVED_CLASS (CapsuleCollider, Collider) + DECLARE_OBJECT_SERIALIZE (CapsuleCollider) + + CapsuleCollider (MemLabelId label, ObjectCreationMode mode); + + virtual void Reset (); + virtual void SmartReset (); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + void SetRadius (float radius); + float GetRadius () const { return m_Radius; } + + float GetHeight () const { return m_Height; } + void SetHeight (float height); + + Vector3f GetCenter () const { return m_Center; } + void SetCenter (const Vector3f& center); + + int GetDirection () const { return m_Direction; } + void SetDirection (int dir); + + Vector2f GetGlobalExtents () const; + Vector3f GetGlobalCenter () const; + + virtual void TransformChanged (int changeMask); + + Matrix4x4f CalculateTransform () const; + + virtual AABB GetBounds (); + + protected: + + + virtual void FetchPoseFromTransform (); + virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix); + + virtual void Create (const Rigidbody* ignoreRigidbody); + void ScaleChanged (); + + virtual NxCCDSkeleton* CreateCCDSkeleton(float scale); + + float m_Radius;///< range { 0, infinity } + float m_Height;///< range { 0, infinity } + int m_Direction;///< enum { X-Axis = 0, Y-Axis = 1, Z-Axis = 2 } + + Vector3f m_Center; +}; + +#endif diff --git a/Runtime/Dynamics/CharacterController.cpp b/Runtime/Dynamics/CharacterController.cpp new file mode 100644 index 0000000..a1ecf20 --- /dev/null +++ b/Runtime/Dynamics/CharacterController.cpp @@ -0,0 +1,567 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#if ENABLE_PHYSICS +#include "../Dynamics/CharacterController.h" +#include "External/PhysX/builds/SDKs/NxCharacter/include/NxController.h" +#include "External/PhysX/builds/SDKs/NxCharacter/include/Controller.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "External/PhysX/builds/SDKs/NxCharacter/include/NxCapsuleController.h" +#include "External/PhysX/builds/SDKs/NxCharacter/include/ControllerManager.h" +#include "PhysicsManager.h" +#include "Runtime/Graphics/Transform.h" +#include "Collider.h" +#include "Runtime/Filters/AABBUtility.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "NxWrapperUtility.h" +#include "Runtime/BaseClasses/MessageHandler.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/ScriptingManager.h" +#include "Runtime/GameCode/RootMotionData.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Scripting/Scripting.h" + +using namespace std; + +#include "Runtime/Mono/MonoBehaviour.h" + +///@TODO: Take advantage of sweep caching by storing when static objects colliders +/// move and only in that case reset the sweep cache. + +///@TODO: DO A PROPER SendMessage for the character controller + +inline CharacterController* GetUnityController(NxController* controller) +{ + return (CharacterController*)((Controller*)controller->getUserData()); +} + +struct ControllerHitReport : NxUserControllerHitReport +{ + struct RecordedControllerColliderHit + { + Collider* collider; + Vector3f point; + Vector3f normal; + Vector3f motionDirection; + float motionLength; + }; + + typedef std::vector<RecordedControllerColliderHit> RecordedControllerColliderHits; + RecordedControllerColliderHits m_Record; + + virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) + { + CharacterController* controller = GetUnityController(hit.controller); + GameObject* go = controller->GetGameObjectPtr (); + + // Do not allocate the mono message if the game object is not interested, to keep GC down. + // See Case #366569. + if (go && go->WillHandleMessage(kControllerColliderHit)) + { + m_Record.push_back(RecordedControllerColliderHit()); + RecordedControllerColliderHit& controllerHit = m_Record.back(); + + controllerHit.point = NxExtendedToVec3(hit.worldPos); + controllerHit.normal = (const Vector3f&)hit.worldNormal; + controllerHit.motionDirection = (const Vector3f&)hit.dir; + controllerHit.motionLength = hit.length; + controllerHit.collider = (Collider*)hit.shape->userData; + } + return NX_ACTION_NONE; + } + + virtual NxControllerAction onControllerHit(const NxControllersHit& hit) + { + return NX_ACTION_NONE; + } +}; + + +#if ENABLE_PHYSICS +static ControllerHitReport gControllerHitReport; +// This needs to be a pointer, so we can control initialization and destruction order. +static ControllerManager *gControllerManager = NULL; +#endif + +CharacterController::CharacterController(MemLabelId label, ObjectCreationMode mode) + : Super(label, mode) +{ + m_Controller = NULL; + m_VerticalSpeed = 0.0F; + m_LastCollisionFlags = 0; + m_Velocity = Vector3f(0,0,0); + m_LastSimpleVelocity = Vector3f(0,0,0); + m_DetectCollision = true; +} + +CharacterController::~CharacterController () +{ +} + +void CharacterController::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if(IsActive()) + Create(NULL); + + Super::AwakeFromLoad (awakeMode); +} + +void CharacterController::SmartReset() +{ + Super::SmartReset(); + AABB aabb; + if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb)) + { + Vector3f extents = aabb.GetCenter () + aabb.GetExtent (); + SetRadius (max (extents.x, extents.z)); + SetHeight (extents.y * 2.0F); + } + else + { + SetRadius (0.5F); + SetHeight (2.0F); + } +} + + +void CharacterController::Reset() +{ + Super::Reset(); + + m_Center = Vector3f::zero; + m_Radius = 0.5F; + m_Height = 2.0F; + + m_MinMoveDistance = .00F; + m_SkinWidth = max(GetRadius() * 0.16F, 0.01F); + m_StepOffset = 0.3f; + m_Height = 2.0F; + m_Radius = 0.3f; + m_SlopeLimit = 45.0F; +} + +int CharacterController::Move (const Vector3f& movement) +{ + if( !m_Controller ) + { + ScriptWarning("CharacterController.Move called on inactive controller", GetGameObjectPtr()); + return 0; + } + unsigned int touching = 0; + Vector3f oldPos = NxExtendedToVec3(m_Controller->getDebugPosition()); + + m_Controller->reportSceneChanged(); + + Assert(gControllerHitReport.m_Record.empty()); + + m_Controller->move((const NxVec3&)movement, GetPhysicsManager().GetLayerCollisionMask(GetGameObject ().GetLayer ()), m_MinMoveDistance, touching, 1.0F); + + bool oldDisableDestruction = GetDisableImmediateDestruction(); + SetDisableImmediateDestruction (true); + + // Swap hits to get consistent callbacks + // CharacterController might call CharacterController.Move from inside a OnControllerColliderHit callback. + ControllerHitReport::RecordedControllerColliderHits hits; + hits.swap(gControllerHitReport.m_Record); + Assert(gControllerHitReport.m_Record.empty()); + + for (ControllerHitReport::RecordedControllerColliderHits::iterator i = hits.begin(); i != hits.end(); i++) + { + #if ENABLE_SCRIPTING + const ControllerHitReport::RecordedControllerColliderHit& recordedHit = *i; + + ControllerColliderHit controllerHit; + + controllerHit.point = recordedHit.point; + controllerHit.normal = recordedHit.normal; + controllerHit.motionDirection = recordedHit.motionDirection; + controllerHit.motionLength = recordedHit.motionLength; + controllerHit.controller = Scripting::ScriptingWrapperFor(this); + controllerHit.collider = Scripting::ScriptingWrapperFor(recordedHit.collider); + controllerHit.push = false; + + ScriptingObjectPtr mono = CreateScriptingObjectFromNativeStruct(GetScriptingManager ().GetCommonClasses ().controllerColliderHit, controllerHit); + + MessageData data; + data.SetScriptingObjectData(mono); + + SendMessageAny (kControllerColliderHit, data); + #endif + + if (!m_Controller) + { + SetDisableImmediateDestruction (oldDisableDestruction); + return touching; + } + } + + SetDisableImmediateDestruction (oldDisableDestruction); + + m_LastCollisionFlags = touching; + + // Stop gravity when touching ground + if ((touching & NXCC_COLLISION_DOWN) && m_VerticalSpeed < 0.0F) + m_VerticalSpeed = 0.0F; + + // At least sync the actual position. Update controller will then reset the position to the filtered position! + Transform& transform = GetComponent(Transform); + Vector3f pos = NxExtendedToVec3(m_Controller->getFilteredPosition()); + + m_Velocity = (pos - oldPos) * GetInvDeltaTime(); + + GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(CharacterController), kTransformChanged.messageID, false); + transform.SetPositionWithLocalOffset (pos, m_Center); + GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(CharacterController), kTransformChanged.messageID, true); + + return touching; +} + +bool CharacterController::IsGrounded () +{ + return m_LastCollisionFlags & NXCC_COLLISION_DOWN; +} + +Vector3f CharacterController::GetVelocity() +{ + return m_Velocity; +} + +void CharacterController::Create (const Rigidbody* ignoreAttachRigidbody) +{ + NxCapsuleControllerDesc desc; + desc.slopeLimit = Cos (Deg2Rad (m_SlopeLimit)); + + if(desc.slopeLimit<0.0f) + desc.slopeLimit=0.0f; + + desc.skinWidth = m_SkinWidth; + desc.stepOffset = m_StepOffset; + desc.userData = this; + Vector2f extents = GetGlobalExtents (); + desc.radius = extents.x; + desc.height = extents.y; + desc.interactionFlag = NXIF_INTERACTION_USE_FILTER; + + desc.callback = &gControllerHitReport; + desc.position = Vec3ToNxExtended(GetWorldCenterPosition()); + if (m_Controller) + { + gControllerManager->releaseController(*m_Controller); + } + + m_Controller = (NxCapsuleController*)gControllerManager->createController(&GetDynamicsScene(), desc); + m_Shape = m_Controller->getActor()->getShapes()[0]; + m_Shape->userData = this; + m_Shape->setGroup (GetGameObject ().GetLayer ()); + m_Controller->setCollision(m_DetectCollision); + +// printf_console ("Create shape %d userData: %d (%s, %s)\n", m_Shape, m_Shape->userData, GetName().c_str(), GetClassName().c_str()); + +} + +Vector3f CharacterController::GetWorldCenterPosition() const +{ + const Transform& transform = GetComponent(Transform); + return transform.TransformPoint (m_Center); + +} + +AABB CharacterController::GetBounds () +{ + if (m_Shape) + { + // AABB reported by PhysX is inaccurate, as PhysX will just transform the local AABB. + // For Spheres it's very easy to do better. Also needed for Editor selection bounds, + // as PhysX only updates Character Controller bounds in play mode. + Transform& transform = GetComponent (Transform); + Vector3f p = transform.TransformPoint (m_Center); + + Vector2f extents = GetGlobalExtents(); + + Vector3f center1 = p + Vector3f(0, extents.y * 0.5, 0); + Vector3f center2 = p + 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 CharacterController::Cleanup () +{ + if (m_Controller) + { + gControllerManager->releaseController(*m_Controller); + m_Controller = NULL; + m_Shape = NULL; + } +} + +Vector2f CharacterController::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); +} + +void CharacterController::TransformChanged(int change) +{ + // Do not call Super::TransformChanged (change) here. The Controller base class + // uses transformChanged to check for reparenting to a new rigidbody. In the CharacterController + // case there is no rigibody attachment, which causes it to always reparent, breaking + // trigger interactions. + + if (m_Controller == NULL) + return; + + if (change & Transform::kScaleChanged) + { + Vector2f extents = GetGlobalExtents (); + m_Controller->setRadius(extents.x); + m_Controller->setHeight(extents.y); + } + + // Teleport + if (change & Transform::kPositionChanged) + { + m_Controller->setPosition (Vec3ToNxExtended(GetWorldCenterPosition())); + m_VerticalSpeed = 0.0F; + } +} + +template<class TransferFunction> +void CharacterController::Transfer(TransferFunction& transfer) +{ + Super::Super::Transfer (transfer); + + transfer.SetVersion (2); + + TRANSFER_SIMPLE (m_Height); + TRANSFER_SIMPLE (m_Radius); + TRANSFER_SIMPLE (m_SlopeLimit); + TRANSFER_SIMPLE (m_StepOffset); + TRANSFER_SIMPLE (m_SkinWidth); + + TRANSFER (m_MinMoveDistance); + TRANSFER (m_Center); + + if (transfer.IsVersionSmallerOrEqual (1)) + { + // The PhysX character controller has been fixed so it works properly. + // Before the fix, the character controller was unable to climb any + // wall above 45 degress, regardless of the slope limit setting. + // The slope limit is therefore clamped to 45 degrees when loading old + // projects to mimic the old behaviour. + m_SlopeLimit = std::min (45.0F, m_SlopeLimit); + } +} + +void CharacterController::ScaleChanged() +{ + if (m_Controller) + { + Vector2f extents = GetGlobalExtents(); + m_Controller->setRadius(extents.x); + m_Controller->setHeight(extents.y); + } +} + +void CharacterController::SetRadius(float radius) +{ + m_Radius = radius; + SetDirty(); + if (m_Controller) + { + Vector2f extents = GetGlobalExtents(); + m_Controller->setRadius(extents.x); + m_Controller->setHeight(extents.y); + } +} + +void CharacterController::SetHeight(float height) +{ + m_Height = height; + SetDirty(); + if (m_Controller) + { + Vector2f extents = GetGlobalExtents(); + m_Controller->setRadius(extents.x); + m_Controller->setHeight(extents.y); + } +} + +bool CharacterController::SimpleMove (const Vector3f& speed) +{ + float dt = GetDeltaTime(); + + m_VerticalSpeed += GetPhysicsManager().GetGravity().y * dt; + Vector3f offset; + + if (IsGrounded()) + { + offset = Vector3f(speed.x, m_VerticalSpeed, speed.z); + m_LastSimpleVelocity = offset; + } + else + offset = Vector3f(m_LastSimpleVelocity.x, m_VerticalSpeed, m_LastSimpleVelocity.z); + + offset *= dt; + Move(offset); + + return IsGrounded(); +} + +void CharacterController::ApplyRootMotionBuiltin (RootMotionData* rootMotion) +{ + if(!GetEnabled()) + return; + + float deltaTime = GetDeltaTime(); + + // Get the Y velocity according to rigidbody or own Y speed for CharacterController + m_VerticalSpeed += GetPhysicsManager().GetGravity().y * deltaTime; + + // Get the velocity from root motion. + // Blend physics velocity with animation velocity on y-axis + Vector3f deltaMotion; + deltaMotion = rootMotion->deltaPosition; + deltaMotion.y = Lerp (deltaMotion.y, m_VerticalSpeed * deltaTime, clamp01(rootMotion->gravityWeight)); + + // Apply velocity and rotation + Move (deltaMotion); + + GetComponent(Transform).SetRotation(rootMotion->targetRotation); + + rootMotion->didApply = true; +} + +void CharacterController::InitializeClass () +{ + REGISTER_MESSAGE_PTR (CharacterController, kAnimatorMoveBuiltin, ApplyRootMotionBuiltin, RootMotionData); +} + +void CharacterController::CleanupClass () +{ +} + + +/* +Jumpheight equation: +v = -(currentVelocity + sqrt(2 * h * g)); +*/ +/* +bool CharacterController::SimpleJump (const Vector3f& movement, float jumpHeight) +{ + float dt = GetDeltaTime(); + + bool didJump = IsGrounded(); + if (didJump) + { +// height = 0.5 * Sqr(GetPhysicsManager().GetGravity().y) + velocity; + m_LastCollisionFlags &= ~NXCC_COLLISION_DOWN; + +// 0 = g * t + velocity + +// velocity * time + 0.5 * GetPhysicsManager().GetGravity().y * t ^ 2 + } + + SimpleMove(movement); + + if (didJump) + m_LastCollisionFlags &= ~NXCC_COLLISION_DOWN; + + return didJump; +} +*/ + +float CharacterController::GetSlopeLimit () +{ + return m_SlopeLimit; +} + +void CharacterController::SetSlopeLimit (float limit) +{ + m_SlopeLimit = limit; + SetDirty(); + if (m_Controller) + Create (NULL); +} + +float CharacterController::GetStepOffset () +{ + return m_StepOffset; +} + +void CharacterController::SetStepOffset (float limit) +{ + m_StepOffset = limit; + SetDirty(); + if (m_Controller) + m_Controller->setStepOffset(limit); +} + +Vector3f CharacterController::GetCenter () +{ + return m_Center; +} + +void CharacterController::SetCenter(const Vector3f& center) +{ + m_Center = center; + if (m_Controller) + Create(NULL); + SetDirty(); +} + +void CharacterController::SetDetectCollisions (bool detect) +{ + m_DetectCollision = detect; + if (m_Controller) + m_Controller->setCollision(m_DetectCollision); +} + +void CharacterController::SetIsTrigger (bool trigger) +{ + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1)) + { + if (trigger) + ErrorStringObject ("A Character Controller cannot be a trigger.", this); + m_IsTrigger = false; + } + else + Super::SetIsTrigger (trigger); +} + + +void CharacterController::CreateControllerManager () +{ + Assert (gControllerManager == NULL); + gControllerManager = new ControllerManager(); +} + +void CharacterController::CleanupControllerManager () +{ + Assert (gControllerManager != NULL); + delete gControllerManager; +} + +IMPLEMENT_CLASS_HAS_INIT(CharacterController) +IMPLEMENT_OBJECT_SERIALIZE(CharacterController) + +#endif diff --git a/Runtime/Dynamics/CharacterController.h b/Runtime/Dynamics/CharacterController.h new file mode 100644 index 0000000..d069c7d --- /dev/null +++ b/Runtime/Dynamics/CharacterController.h @@ -0,0 +1,112 @@ +#pragma once + +#include "Runtime/BaseClasses/GameObject.h" +#include "Collider.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Vector2.h" + +class NxCapsuleController; +struct MonoObject; +struct RootMotionData; + +class CharacterController : public Collider +{ + public: + + REGISTER_DERIVED_CLASS(CharacterController, Collider) + DECLARE_OBJECT_SERIALIZE(CharacterController) + + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + CharacterController (MemLabelId label, ObjectCreationMode mode); + virtual void Reset(); + virtual void SmartReset (); + + int Move (const Vector3f& movement); + + Vector2f GetGlobalExtents () const; + + virtual void TransformChanged(int mask); + + void SetRadius(float radius); + float GetRadius() { return m_Radius; } + void SetHeight(float height); + float GetHeight() { return m_Height; } + + virtual AABB GetBounds (); + + Vector3f GetCenter (); + void SetCenter(const Vector3f& center); + + float GetSlopeLimit (); + void SetSlopeLimit (float limit); + + float GetStepOffset (); + void SetStepOffset (float limit); + + virtual void SetIsTrigger (bool trigger); + + static void CreateControllerManager (); + static void CleanupControllerManager (); + + Vector3f GetWorldCenterPosition() const; + + bool SimpleMove (const Vector3f& movement); +// bool SimpleJump (const Vector3f& movement, float jumpHeight); + + Vector3f GetVelocity(); + + bool IsGrounded (); + int GetCollisionFlags() { return m_LastCollisionFlags; } + + bool GetDetectCollisions () { return m_DetectCollision; } + void SetDetectCollisions (bool detect); + + static void InitializeClass (); + static void CleanupClass (); + +private: + + virtual void Create(const Rigidbody* ignoreAttachRigidbody); + virtual void Cleanup(); + virtual void ScaleChanged (); + + void ApplyRootMotionBuiltin (RootMotionData* rootMotion); + + NxCapsuleController* m_Controller; + + float m_MinMoveDistance;///< range { 0, infinity } + float m_SkinWidth;///< range { 0.0001, infinity } + float m_SlopeLimit;///< range { 0, 180 } + float m_StepOffset;///< range { 0, infinity } + float m_Height;///< range { 0, infinity } + float m_Radius;///< range { 0, infinity } + Vector3f m_Center; + bool m_DetectCollision; + + float m_VerticalSpeed; + Vector3f m_Velocity; + Vector3f m_LastSimpleVelocity; + + int m_LastCollisionFlags; + + friend class PhysicsManager; +}; + +struct ControllerColliderHit +{ + ScriptingObjectPtr controller; + ScriptingObjectPtr collider; + Vector3f point; + Vector3f normal; + Vector3f motionDirection; + float motionLength; + int push; +}; + +struct ControllerControllerHit +{ + ScriptingObjectPtr controller; + ScriptingObjectPtr other; + short push; +}; diff --git a/Runtime/Dynamics/CharacterJoint.cpp b/Runtime/Dynamics/CharacterJoint.cpp new file mode 100644 index 0000000..38dd0aa --- /dev/null +++ b/Runtime/Dynamics/CharacterJoint.cpp @@ -0,0 +1,340 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "CharacterJoint.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "PhysicsManager.h" +#include "Runtime/Utilities/Utility.h" + +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" + +using namespace std; + +namespace Unity +{ + +#define GET_JOINT() static_cast<NxD6Joint*> (m_Joint) + +inline void FixupNovodexLimitBug (NxD6JointDesc& desc) +{ + desc.twistMotion = desc.swing2Motion = desc.swing1Motion = NX_D6JOINT_MOTION_LIMITED; +} + + +/* +- We awake the hingejoint only once. (AwakeFromLoad) + At this point we setup the axes. They are never changed afterwards + -> The perfect solution remembers the old position/rotation of the rigid bodies. + Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state! +*/ + +CharacterJoint::CharacterJoint (MemLabelId label, ObjectCreationMode mode) + : Super(label, mode) +{ + m_TargetRotation = Quaternionf::identity(); +// m_TargetAngularVelocity = Vector3f::zero; + m_UseTargetRotation = false; + m_SwingAxis = Vector3f::yAxis; +} + +CharacterJoint::~CharacterJoint () +{ +} + +void CharacterJoint::CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const +{ + const Transform& transform = GetComponent (Transform); + + Vector3f localAxis = m_Axis; + if (SqrMagnitude (localAxis) < Vector3f::epsilon) + localAxis = Vector3f (1.0F, 0.0F, 0.0F); + Vector3f localNormal = m_SwingAxis; + + OrthoNormalize (&localAxis, &localNormal); + + globalAnchor = transform.TransformPoint (m_Anchor); +// Vector3f globalRigidbodyPos = transform.GetPosition (); + globalAxis = transform.TransformDirection (localAxis); + + globalNormal = transform.TransformDirection (localNormal); + + Matrix3x3f m; + m.SetOrthoNormalBasisInverse(globalAxis, globalNormal, Cross (globalAxis, globalNormal)); +// m.SetOrthoNormalBasisInverse(Vector3f::xAxis, globalNormal, Cross (globalAxis, globalNormal)); +// m.SetOrthoNormalBasisInverse(globalNormal, Cross (globalAxis, globalNormal), globalAxis); +// OrthoNormalize(m); + Quaternionf q; + MatrixToQuaternion(m, q); + m_ConfigurationSpace = q * Inverse(transform.GetRotation ()); +} + +void CharacterJoint::Reset () +{ + Super::Reset(); + + m_RotationDrive.maximumForce = 20; + m_RotationDrive.positionSpring = 50; + m_RotationDrive.positionDamper = 5; + + InitSoftJointLimit (m_LowTwistLimit); + InitSoftJointLimit (m_HighTwistLimit); + InitSoftJointLimit (m_Swing1Limit); + InitSoftJointLimit (m_Swing2Limit); + + m_LowTwistLimit.limit = -20; + m_HighTwistLimit.limit = 70; + + m_Swing1Limit.limit = 40; + m_Swing2Limit.limit = 0; +} + +void CharacterJoint::CheckConsistency () +{ + Super::CheckConsistency(); + m_LowTwistLimit.limit = clamp<float> (m_LowTwistLimit.limit, -180, 180); + m_HighTwistLimit.limit = clamp<float> (m_HighTwistLimit.limit, -180, 180); + m_Swing1Limit.limit = clamp<float> (m_Swing1Limit.limit, 0, 180); + m_Swing2Limit.limit = clamp<float> (m_Swing2Limit.limit, 0, 180); +} + +void CharacterJoint::UpdateTargetRotation () +{ + NxD6Joint* joint = GET_JOINT (); + if (joint) + { + + Quaternionf temp = m_ConfigurationSpace * Inverse(m_TargetRotation) * Inverse(m_ConfigurationSpace); + NxQuat targetRotation = (const NxQuat&)temp; +// NxActor *a0, *a1; +// joint->getActors(&a0, &a1); +// if (a1) +// targetRotation = a1->getGlobalOrientationQuat () * targetRotation; +//NxQuat id; +//id.id(); + joint->setDriveOrientation(targetRotation); + } +} + +Quaternionf CharacterJoint::GetTargetRotation () +{ + return m_TargetRotation; +} + +void CharacterJoint::SetTargetRotation (const Quaternionf& rot) +{ + SetDirty (); + if (m_Joint) + { + NxActor* a0, *a1; + m_Joint->getActors(&a0, &a1); +/* if (a1) + { + Quaternionf q = (const Quaternionf&)a1->getGlobalOrientationQuat(); + m_TargetRotation = NormalizeSafe(Inverse(q) * rot); + } + else*/ + m_TargetRotation = NormalizeSafe (rot); + } + + UpdateTargetRotation(); +} + +Vector3f CharacterJoint::GetTargetAngularVelocity () +{ + return m_TargetAngularVelocity; +} + +void CharacterJoint::SetTargetAngularVelocity (const Vector3f& rot) +{ + SetDirty (); + m_TargetAngularVelocity = rot; + if (m_Joint) + GET_JOINT ()->setDriveAngularVelocity ((const NxVec3&)m_TargetAngularVelocity); +} + +JointDrive CharacterJoint::GetRotationDrive () +{ + return m_RotationDrive; +} + +void CharacterJoint::SetRotationDrive (const JointDrive& drive) +{ + SetDirty (); + m_RotationDrive = drive; + if (GET_JOINT()) + { + NxD6JointDesc desc; + GET_JOINT()->saveToDesc (desc); + FixupNovodexLimitBug(desc); + + int flag = 0; + if (m_UseTargetRotation) + flag |= NX_D6JOINT_DRIVE_POSITION; + + ConvertDrive (m_RotationDrive, desc.slerpDrive, flag); + ConvertDrive (m_RotationDrive, desc.twistDrive, flag); + ConvertDrive (m_RotationDrive, desc.swingDrive, flag); + GET_JOINT()->loadFromDesc (desc); + } +} + +void CharacterJoint::SetLowTwistLimit (const SoftJointLimit& limit) +{ + SetDirty (); + m_LowTwistLimit = limit; + if (GET_JOINT()) + { + NxD6JointDesc desc; + GET_JOINT()->saveToDesc (desc); + FixupNovodexLimitBug(desc); + + ConvertSoftLimit (m_LowTwistLimit, desc.twistLimit.low); + ConvertSoftLimit (m_HighTwistLimit, desc.twistLimit.high); + if (desc.twistLimit.low.value > desc.twistLimit.high.value) + swap (desc.twistLimit.low, desc.twistLimit.high); + GET_JOINT()->loadFromDesc (desc); + } +} + +void CharacterJoint::SetHighTwistLimit (const SoftJointLimit& limit) +{ + SetDirty (); + m_HighTwistLimit = limit; + if (GET_JOINT()) + { + NxD6JointDesc desc; + GET_JOINT()->saveToDesc (desc); + FixupNovodexLimitBug(desc); + + + ConvertSoftLimit (m_LowTwistLimit, desc.twistLimit.low); + ConvertSoftLimit (m_HighTwistLimit, desc.twistLimit.high); + if (desc.twistLimit.low.value > desc.twistLimit.high.value) + swap (desc.twistLimit.low, desc.twistLimit.high); + GET_JOINT()->loadFromDesc (desc); + } +} + +void CharacterJoint::SetSwing1Limit (const SoftJointLimit& limit) +{ + SetDirty (); + m_Swing1Limit = limit; + if (GET_JOINT()) + { + NxD6JointDesc desc; + GET_JOINT()->saveToDesc (desc); + FixupNovodexLimitBug(desc); + + ConvertSoftLimit (m_Swing1Limit, desc.swing1Limit); + GET_JOINT()->loadFromDesc (desc); + } +} + +void CharacterJoint::SetSwing2Limit (const SoftJointLimit& limit) +{ + SetDirty (); + m_Swing2Limit = limit; + if (GET_JOINT()) + { + NxD6JointDesc desc; + GET_JOINT()->saveToDesc (desc); + FixupNovodexLimitBug(desc); + + ConvertSoftLimit (m_Swing2Limit, desc.swing2Limit); + GET_JOINT()->loadFromDesc (desc); + } +} + +/* +- When the rigid body which is attached with the hinge joint is activated after the joint is loaded + It will not be connected with the joint! +*/ + +template<class TransferFunction> +void CharacterJoint::Transfer (TransferFunction& transfer) +{ + JointTransferPre (transfer); + TRANSFER (m_SwingAxis); +// TRANSFER (m_UseTargetRotation); +// TRANSFER (m_RotationDrive); + TRANSFER (m_LowTwistLimit); + TRANSFER (m_HighTwistLimit); + TRANSFER (m_Swing1Limit); + TRANSFER (m_Swing2Limit); + + JointTransferPost (transfer); +} + +void CharacterJoint::Create () +{ + AssertIf (!IsActive ()); + + NxD6JointDesc desc; + + if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING) + GET_JOINT()->saveToDesc (desc); + + desc.xMotion = NX_D6JOINT_MOTION_LOCKED; + desc.yMotion = NX_D6JOINT_MOTION_LOCKED; + desc.zMotion = NX_D6JOINT_MOTION_LOCKED; + + ConvertSoftLimit (m_LowTwistLimit, desc.twistLimit.low); + ConvertSoftLimit (m_HighTwistLimit, desc.twistLimit.high); + + if (desc.twistLimit.low.value > desc.twistLimit.high.value) + swap (desc.twistLimit.low, desc.twistLimit.high); + + ConvertSoftLimit (m_Swing1Limit, desc.swing1Limit); + ConvertSoftLimit (m_Swing2Limit, desc.swing2Limit); + + desc.swing1Motion = NX_D6JOINT_MOTION_LIMITED; + desc.swing2Motion = NX_D6JOINT_MOTION_LIMITED; + desc.twistMotion = NX_D6JOINT_MOTION_LIMITED; + +// desc.driveOrientation = (const NxQuat&)m_TargetRotation; + desc.driveAngularVelocity = (const NxVec3&)m_TargetAngularVelocity; + + desc.flags = NX_D6JOINT_SLERP_DRIVE; + + int flag = 0; + if (m_UseTargetRotation) + flag |= NX_D6JOINT_DRIVE_POSITION; + + ConvertDrive (m_RotationDrive, desc.slerpDrive, flag); + ConvertDrive (m_RotationDrive, desc.twistDrive, flag); + ConvertDrive (m_RotationDrive, desc.swingDrive, flag); + + FINALIZE_CREATE (desc, NxD6Joint); + + ///////// DO WE WANT THIS????????? + SetTargetRotation (GetComponent(Transform).GetRotation()); +} + +void CharacterJoint::SetSwingAxis (const Vector3f& axis)\ +{ + SetDirty (); + m_SwingAxis = axis; + ApplySetupAxesToDesc (kChangeAxis); +} + +void CharacterJoint::ApplySetupAxesToDesc (int option) +{ + if (IsActive () && m_Joint) + { + NxD6JointDesc desc; + AssertIf (m_Joint->getState () == NX_JS_BROKEN); + GET_JOINT()->saveToDesc (desc); + FixupNovodexLimitBug(desc); + + SetupAxes (desc, option); + GET_JOINT()->loadFromDesc (desc); + AssertIf (m_Joint->getState () == NX_JS_BROKEN); + } +} + +} + + +IMPLEMENT_CLASS (CharacterJoint) +IMPLEMENT_OBJECT_SERIALIZE (CharacterJoint) +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/CharacterJoint.h b/Runtime/Dynamics/CharacterJoint.h new file mode 100644 index 0000000..ce94155 --- /dev/null +++ b/Runtime/Dynamics/CharacterJoint.h @@ -0,0 +1,83 @@ +#ifndef CHARACTERJOINT_H +#define CHARACTERJOINT_H + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Quaternion.h" +#include "JointDescriptions.h" +#include "Joint.h" +class Rigidbody; +class NxJointDesc; +class NxRevoluteJoint; + +namespace Unity +{ + +class CharacterJoint : public Joint +{ + public: + + REGISTER_DERIVED_CLASS (CharacterJoint, Joint) + DECLARE_OBJECT_SERIALIZE (CharacterJoint) + + CharacterJoint (MemLabelId label, ObjectCreationMode mode); + + JointDrive GetRotationDrive (); + void SetRotationDrive (const JointDrive& drive); + + void SetTargetRotation (const Quaternionf& rot); + Quaternionf GetTargetRotation (); + + void SetTargetAngularVelocity (const Vector3f& angular); + Vector3f GetTargetAngularVelocity (); + + virtual void ApplySetupAxesToDesc (int option); + + virtual void SetSwingAxis (const Vector3f& axis); + Vector3f GetSwingAxis () { return m_SwingAxis; } + + void SetLowTwistLimit (const SoftJointLimit& limit); + SoftJointLimit GetLowTwistLimit () { return m_LowTwistLimit; } + + void SetHighTwistLimit (const SoftJointLimit& limit); + SoftJointLimit GetHighTwistLimit () { return m_HighTwistLimit; } + + void SetSwing1Limit (const SoftJointLimit& limit); + SoftJointLimit GetSwing1Limit () { return m_Swing1Limit; } + + void SetSwing2Limit (const SoftJointLimit& limit); + SoftJointLimit GetSwing2Limit () { return m_Swing2Limit; } + + virtual void Reset(); + + void UpdateTargetRotation (); + virtual void CheckConsistency (); + + ////// THIS IS NOT GOOD!!!!!!!!!!! + void CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const; + + private: + + virtual void Create (); + void SetupDriveType (); + + bool m_UseTargetRotation; + Quaternionf m_TargetRotation; + Vector3f m_TargetAngularVelocity; + + mutable Quaternionf m_ConfigurationSpace; + + ////// THIS IS NOT GOOD!!!!!!!!!!! + Vector3f m_SwingAxis; + + JointDrive m_RotationDrive; + + SoftJointLimit m_LowTwistLimit; + SoftJointLimit m_HighTwistLimit; + SoftJointLimit m_Swing1Limit; + SoftJointLimit m_Swing2Limit; +}; + +} + +#endif diff --git a/Runtime/Dynamics/Cloth.cpp b/Runtime/Dynamics/Cloth.cpp new file mode 100644 index 0000000..52a1ad7 --- /dev/null +++ b/Runtime/Dynamics/Cloth.cpp @@ -0,0 +1,402 @@ +#include "UnityPrefix.h" +#include "Cloth.h" + +#if ENABLE_CLOTH + +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "PhysicsManager.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "Collider.h" +#include "Runtime/BaseClasses/IsPlaying.h" + +namespace Unity +{ + +InteractiveCloth::InteractiveCloth (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +, m_FixedUpdateNode (this) +{ + // configuration + m_Friction = 0.5f; + m_Density = 1.0f; + m_Pressure = 0.0f; + m_CollisionResponse = 0.0f; + m_TearFactor = 0.0f; + m_AttachmentTearFactor = 0.5f; + m_AttachmentResponse = 0.2f; + + // state + m_IsTeared = false; +} + +InteractiveCloth::~InteractiveCloth () +{ +} + +void InteractiveCloth::Reset () +{ + Super::Reset(); + + // configuration + m_Friction = 0.5f; + m_Density = 1.0f; + m_Pressure = 0.0f; + m_CollisionResponse = 0.0f; + m_TearFactor = 0.0f; + m_AttachmentTearFactor = 0.5f; + m_AttachmentResponse = 0.2f; +} + +#if ENABLE_CLOTH + +void InteractiveCloth::Create() +{ + Cleanup (); + + m_CachedMesh = m_Mesh; + + if (!m_Mesh.IsValid()) + return; + + m_IsTeared = false; + + int tearMemoryFactor = (m_TearFactor > 0.0f) ? 2 : 1; + if (!SetupMeshData (true, false, tearMemoryFactor)) + return; + +#if UNITY_EDITOR + if (IsWorldPlaying()) + { +#endif + NxClothDesc clothDesc; + SetupClothDesc (clothDesc, true); + clothDesc.density = m_Density; + clothDesc.pressure = m_Pressure; + clothDesc.friction = m_Friction; + if (m_TearFactor > 0) + clothDesc.tearFactor = m_TearFactor+1; + + clothDesc.collisionResponseCoefficient = m_CollisionResponse; + clothDesc.attachmentTearFactor = m_AttachmentTearFactor+1; + clothDesc.attachmentResponseCoefficient = m_AttachmentResponse; + if (m_Pressure > 0) + clothDesc.flags |= NX_CLF_PRESSURE; + if (m_CollisionResponse > 0) + clothDesc.flags |= NX_CLF_COLLISION_TWOWAY; + if (m_TearFactor > 0) + clothDesc.flags |= NX_CLF_TEARABLE; + + m_ClothScene = &GetDynamicsScene (); + m_Cloth = m_ClothScene->createCloth(clothDesc); + + if (m_Cloth) + { + for(std::vector<ClothAttachment>::iterator i = m_AttachedColliders.begin(); i != m_AttachedColliders.end(); i++) + { + ClothAttachment& attach = *i; + AttachToCollider(attach.m_Collider, attach.m_Tearable, attach.m_TwoWayInteraction); + } + } + else + GetDynamicsSDK().releaseClothMesh(*clothDesc.clothMesh); +#if UNITY_EDITOR + } +#endif +} + +void InteractiveCloth::AddForceAtPosition (const Vector3f& force, const Vector3f& position, float radius, int mode) +{ + AssertFiniteParameter(force) + AssertFiniteParameter(position) + AssertIf (m_Cloth == NULL); + + if (!m_IsSuspended) + m_Cloth->wakeUp(); + m_Cloth->addDirectedForceAtPos ((const NxVec3&)position, (const NxVec3&)force, radius ,(NxForceMode)mode); +} + +void InteractiveCloth::AttachToCollider (Collider *collider, bool tearable, bool twoWayInteraction) +{ + AssertIf (m_Cloth == NULL); + NxShape* shape = collider ? collider->CreateShapeIfNeeded() : NULL; + if (shape) + { + NxU32 attachmentFlags = 0; + if (twoWayInteraction) + attachmentFlags |= NX_CLOTH_ATTACHMENT_TWOWAY; + if (tearable) + attachmentFlags |= NX_CLOTH_ATTACHMENT_TEARABLE; + m_Cloth->attachToShape(shape, attachmentFlags); + m_NeedToWakeUp = true; + } +} + +void InteractiveCloth::DetachFromCollider (Collider *collider) +{ + AssertIf (m_Cloth == NULL); + if (collider && collider->IsActive()) + { + m_Cloth->detachFromShape(collider->m_Shape); + m_NeedToWakeUp = true; + } +} + +void InteractiveCloth::CheckTearing() +{ + if (m_NumVerticesFromPhysX > m_NumVertices) + { + m_IsTeared = true; + m_Cloth->setFlags(m_Cloth->getFlags() & ~NX_CLF_PRESSURE); + } +} + +void InteractiveCloth::ProcessMeshForRenderer () +{ + CheckTearing(); + Super::ProcessMeshForRenderer(); +} + +////@TODO: TEST IF THIS ACTUALLLY CAUSES THE CLOTH PIECE TO EVER FALL ASLEEP??? +void InteractiveCloth::FixedUpdate() +{ + Super::FixedUpdate (); + if (m_Cloth) + { + CheckTearing(); + } +} + +void InteractiveCloth::AddToManager () +{ + GetFixedBehaviourManager ().AddBehaviour (m_FixedUpdateNode, -1); +} + +void InteractiveCloth::RemoveFromManager () +{ + GetFixedBehaviourManager ().RemoveBehaviour (m_FixedUpdateNode); +} + +void InteractiveCloth::PauseSimulation () { + Super::PauseSimulation (); + m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_DISABLE_COLLISION | NX_CLF_STATIC); +} + +void InteractiveCloth::ResumeSimulation () { + Super::ResumeSimulation (); + m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_DISABLE_COLLISION | NX_CLF_STATIC)); +} + +#define ENFORCE_MINEQ(x) {if (value < x) { value = x; ErrorString("value must be greater than or equal to " #x);}} +#define ENFORCE_MIN(x) {if (value <= x) { value = x; ErrorString("value must be greater than " #x);}} +#define ENFORCE_MAXEQ(x) {if (value > x) { value = x; ErrorString("value must be smaller than or equal to " #x);}} +#define ENFORCE_MAX(x) {if (value >= x) { value = x; ErrorString("value must be smaller than " #x);}} + +void InteractiveCloth::SetMesh (PPtr<Mesh> value) +{ + if (value != m_CachedMesh) + { + m_Mesh = value; + Create(); + SetDirty(); + } +} + +void InteractiveCloth::SetFriction (float value) +{ + ENFORCE_MINEQ(0); + ENFORCE_MAXEQ(1); + + if (value != m_Friction) + { + m_NeedToWakeUp = true; + m_Friction = value; + } + if (m_Cloth) + m_Cloth->setFriction(value); + SetDirty(); +} + +void InteractiveCloth::SetDensity (float value) +{ + ENFORCE_MIN(0); + + if (value != m_Density) + { + m_NeedToWakeUp = true; + m_Density = value; + } + if (m_Cloth) + { + if (m_Density != m_Cloth->getDensity()) + Create(); + } + SetDirty(); +} + +void InteractiveCloth::SetPressure (float value) +{ + ENFORCE_MINEQ(0); + + if (value != m_Pressure) + { + m_NeedToWakeUp = true; + m_Pressure = value; + } + if (m_Cloth) + { + if (value > 0 && !m_IsTeared) + { + m_Cloth->setPressure(value); + m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_PRESSURE); + } + else + m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_PRESSURE)); + } + SetDirty(); +} + +void InteractiveCloth::SetCollisionResponse (float value) +{ + ENFORCE_MINEQ(0); + + if (value != m_CollisionResponse) + { + m_NeedToWakeUp = true; + m_CollisionResponse = value; + } + if (m_Cloth) + { + if (value > 0) + { + m_Cloth->setCollisionResponseCoefficient(value); + m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_COLLISION_TWOWAY); + } + else + m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_COLLISION_TWOWAY)); + } + SetDirty(); +} + +void InteractiveCloth::SetTearFactor (float value) +{ + ENFORCE_MINEQ(0); + + if (value != m_TearFactor) + { + m_NeedToWakeUp = true; + m_TearFactor = value; + } + if (m_Cloth) + { + if ((m_TearFactor>0) != ((m_Cloth->getFlags() & NX_CLF_TEARABLE) != 0)) + Create(); + else if (m_TearFactor>0) + m_Cloth->setTearFactor(m_TearFactor + 1); + } + SetDirty(); +} + +void InteractiveCloth::SetAttachmentTearFactor (float value) +{ + ENFORCE_MINEQ(0); + + if (value != m_AttachmentTearFactor) + { + m_NeedToWakeUp = true; + m_AttachmentTearFactor = value; + } + if (m_Cloth && m_AttachmentTearFactor>0) + m_Cloth->setAttachmentTearFactor(m_AttachmentTearFactor + 1); + SetDirty(); +} + +void InteractiveCloth::SetAttachmentResponse (float value) +{ + ENFORCE_MINEQ(0); + ENFORCE_MAXEQ(1); + + if (value != m_AttachmentResponse) + { + m_NeedToWakeUp = true; + m_AttachmentResponse = value; + } + if (m_Cloth) + m_Cloth->setAttachmentResponseCoefficient(value); + SetDirty(); +} + +#else //ENABLE_CLOTH + +void InteractiveCloth::Create() {} +void InteractiveCloth::AddForceAtPosition (const Vector3f& force, const Vector3f& position, float radius, int mode) {} +void InteractiveCloth::AttachToCollider (Collider *collider, bool tearable, bool twoWayInteraction) {} +void InteractiveCloth::DetachFromCollider (Collider *collider) {} +void InteractiveCloth::CheckTearing() {} +void InteractiveCloth::ProcessMeshForRenderer () {} +void InteractiveCloth::FixedUpdate() {} +void InteractiveCloth::PauseSimulation () {} +void InteractiveCloth::ResumeSimulation () {} +void InteractiveCloth::SetMesh (PPtr<Mesh> value) {} +void InteractiveCloth::SetFriction (float value) {} +void InteractiveCloth::SetDensity (float value) {} +void InteractiveCloth::SetPressure (float value) {} +void InteractiveCloth::SetCollisionResponse (float value) {} +void InteractiveCloth::SetTearFactor (float value) {} +void InteractiveCloth::SetAttachmentTearFactor (float value) {} +void InteractiveCloth::SetAttachmentResponse (float value) {} +void InteractiveCloth::AddToManager () {} +void InteractiveCloth::RemoveFromManager (){} +#endif //ENABLE_CLOTH + +void InteractiveCloth::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ +#if ENABLE_CLOTH + if (m_Cloth) + { + // Apply changed values + SetMesh (m_Mesh); + SetFriction (m_Friction); + SetPressure (m_Pressure); + SetCollisionResponse (m_CollisionResponse); + SetAttachmentTearFactor (m_AttachmentTearFactor); + SetAttachmentResponse (m_AttachmentResponse); + SetTearFactor (m_TearFactor); + SetDensity (m_Density); + } +#endif + + Super::AwakeFromLoad (awakeMode); +} + +template<class TransferFunction> +void InteractiveCloth::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + TRANSFER (m_Mesh); + TRANSFER (m_Friction); + TRANSFER (m_Density); + TRANSFER (m_Pressure); + TRANSFER (m_CollisionResponse); + TRANSFER (m_AttachmentTearFactor); + TRANSFER (m_AttachmentResponse); + TRANSFER (m_TearFactor); + TRANSFER (m_AttachedColliders); +} + +template<class TransferFunction> inline +void ClothAttachment::Transfer (TransferFunction& transfer) +{ + TRANSFER(m_Collider); + TRANSFER(m_TwoWayInteraction); + TRANSFER(m_Tearable); +} + +IMPLEMENT_CLASS (InteractiveCloth) +IMPLEMENT_OBJECT_SERIALIZE (InteractiveCloth) + +} + +void RegisterClass_InteractiveCloth () { Unity::RegisterClass_InteractiveCloth(); } + +#endif // ENABLE_CLOTH diff --git a/Runtime/Dynamics/Cloth.h b/Runtime/Dynamics/Cloth.h new file mode 100644 index 0000000..06341c8 --- /dev/null +++ b/Runtime/Dynamics/Cloth.h @@ -0,0 +1,101 @@ +#pragma once +#include "Configuration/UnityConfigure.h" + +#if ENABLE_CLOTH || DOXYGEN + +#include "DeformableMesh.h" + +class Collider; + +namespace Unity +{ + +struct ClothAttachment +{ + PPtr<Collider> m_Collider; + bool m_TwoWayInteraction; + bool m_Tearable; + + DECLARE_SERIALIZE (ClothAttachment) + + ClothAttachment() : + m_TwoWayInteraction(false), + m_Tearable(false) + { } + +}; + +class InteractiveCloth : public Cloth +{ +public: + REGISTER_DERIVED_CLASS (InteractiveCloth, Cloth) + DECLARE_OBJECT_SERIALIZE (InteractiveCloth) + + InteractiveCloth (MemLabelId label, ObjectCreationMode mode); + + virtual void ProcessMeshForRenderer (); + + virtual void FixedUpdate (); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + virtual void Reset (); + + void AddForceAtPosition (const Vector3f& force, const Vector3f& position, float radius, int mode); + void AttachToCollider (Collider *collider, bool tearable, bool twoWayInteraction); + void DetachFromCollider (Collider *collider); + + PPtr<Mesh> GetMesh () { return m_Mesh; } + void SetMesh (PPtr<Mesh> value); + + float GetFriction () { return m_Friction; } + void SetFriction (float value); + + float GetDensity () { return m_Density; } + void SetDensity (float value); + + float GetPressure () { return m_Pressure; } + void SetPressure (float value); + + float GetCollisionResponse () { return m_CollisionResponse; } + void SetCollisionResponse (float value); + + float GetTearFactor () { return m_TearFactor; } + void SetTearFactor (float value); + + float GetAttachmentTearFactor () { return m_AttachmentTearFactor; } + void SetAttachmentTearFactor (float value); + + float GetAttachmentResponse () { return m_AttachmentResponse; } + void SetAttachmentResponse (float value); + + bool GetIsTeared () { return m_IsTeared; } + + virtual void AddToManager (); + virtual void RemoveFromManager (); + +protected: + + virtual void Create (); + virtual void PauseSimulation (); + virtual void ResumeSimulation (); + + void CheckTearing(); + + // configuration + float m_Friction; ///<Friction. range { 0, 1 } + float m_Density; ///<Density (mass per area). range { 0.001, 10000 } + float m_Pressure; ///<Air pressure inside a closed cloth mesh. 0 = disabled. 1 = same pressure as outside atmosphere. range { 0, 10000 } + float m_CollisionResponse; ///<Force to apply back to colliding rigidbodies. 0 = disabled. range { 0, 10000 } + float m_TearFactor; ///<How far vertices need to stretch until they tear. 0 = disabled. range { 0, 10000 } + float m_AttachmentTearFactor; ///<How far vertices need to stretch until attachments tear off, if tearing is enabled for the attachment. range { 0.001, 10000 } + float m_AttachmentResponse; ///<Force to apply back to attached rigidbodies, if two way interaction is enabled for the attachment. range { 0, 1 } + std::vector<ClothAttachment> m_AttachedColliders; + + // state + bool m_IsTeared; + PPtr<Mesh> m_CachedMesh; + BehaviourListNode m_FixedUpdateNode; +}; + +} + +#endif // ENABLE_CLOTH diff --git a/Runtime/Dynamics/ClothRenderer.cpp b/Runtime/Dynamics/ClothRenderer.cpp new file mode 100644 index 0000000..0217a09 --- /dev/null +++ b/Runtime/Dynamics/ClothRenderer.cpp @@ -0,0 +1,273 @@ +#include "UnityPrefix.h" +#include "ClothRenderer.h" + +#if ENABLE_CLOTH + +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/Shaders/Shader.h" +#include "Runtime/Dynamics/Cloth.h" +#include "Runtime/Shaders/VBO.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/GfxDevice/ChannelAssigns.h" +#include "Runtime/BaseClasses/SupportedMessageOptimization.h" + +ClothRenderer::ClothRenderer (MemLabelId label, ObjectCreationMode mode) +: Super(kRendererCloth, label, mode) +, m_VBO(NULL) +{ + m_ChannelsInVBO = 0; + m_UnavailableInVBO = 0; + m_PauseWhenNotVisible = true; + m_IsPaused = false; + m_AABBDirty = true; + m_AABB.SetCenterAndExtent(Vector3f::zero, Vector3f::zero); +} + +ClothRenderer::~ClothRenderer () +{ + if (m_VBO) + GetGfxDevice().DeleteVBO(m_VBO); +} + +void ClothRenderer::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + + ReloadVBOToGfxDevice(); +} + +void ClothRenderer::Reset () +{ + Super::Reset (); + + m_PauseWhenNotVisible = true; +} + +void ClothRenderer::UpdateClothVBOImmediate (int requiredChannels, UInt32& unavailableChannels) +{ + Unity::Cloth& cloth = GetComponent(InteractiveCloth); + + int supportedChannels = VERTEX_FORMAT3(Vertex, Normal, Color); + if (cloth.m_UVs.size() == cloth.m_VerticesForRendering->size()) + supportedChannels |= VERTEX_FORMAT1(TexCoord0); + if (cloth.m_UV1s.size() == cloth.m_VerticesForRendering->size()) + supportedChannels |= VERTEX_FORMAT1(TexCoord1); + if (cloth.m_Tangents.size() == cloth.m_VerticesForRendering->size()) + supportedChannels |= VERTEX_FORMAT1(Tangent); + + // Silently create an all-white color array if shader wants colors, but mesh does not have them. + // On D3D, some runtime/driver combinations will crash if a vertex shader wants colors but does not + // have them (e.g. Vista drivers for Intel 965). In other cases it will default to white for fixed function + // pipe, and to undefined value for vertex shaders, which is not good either. + + if (cloth.m_Colors.size() != cloth.m_VerticesForRendering->size()) + cloth.m_Colors.resize_initialized( cloth.m_VerticesForRendering->size(), 0xFFFFFFFF ); + + int activeChannels = requiredChannels & supportedChannels; + unavailableChannels = requiredChannels & ~supportedChannels; + + // Set up vertex buffer channels and streams + VertexBufferData vertexBuffer; + UInt32 stride = 0; + for (int i = 0; i < kShaderChannelCount; i++) + { + ChannelInfo& info = vertexBuffer.channels[i]; + if (activeChannels & (1 << i)) + { + info.stream = 0; + info.offset = stride; + info.format = VBO::GetDefaultChannelFormat(i); + info.dimension = VBO::GetDefaultChannelDimension(i); + stride += VBO::GetDefaultChannelByteSize(i); + } + else + info.Reset(); + } + vertexBuffer.streams[0].channelMask = activeChannels; + vertexBuffer.streams[0].stride = stride; + + // Don't pass a buffer since we use map/unmap for writing + vertexBuffer.buffer = NULL; + int vertexCount = cloth.m_NumVerticesForRendering; + vertexBuffer.bufferSize = stride * vertexCount; + vertexBuffer.vertexCount = vertexCount; + m_VBO->SetVertexStreamMode(0, VBO::kStreamModeWritePersist); + m_VBO->UpdateVertexData(vertexBuffer); + + IndexBufferData indexBuffer; + indexBuffer.indices = &(*cloth.m_IndicesForRendering)[0]; + indexBuffer.count = cloth.m_NumIndicesForRendering; + indexBuffer.hasTopologies = (1<<kPrimitiveTriangles); + + VertexStreamData mappedStream; + if (!m_VBO->MapVertexStream(mappedStream, 0)) + return; + UInt8* buffer = mappedStream.buffer; + for (int v = 0; v < vertexCount; v++) + { + if (activeChannels & (1 << kShaderChannelVertex)) + { + *(Vector3f*)buffer = (*cloth.m_VerticesForRendering)[v]; + buffer += sizeof(Vector3f); + } + + if (activeChannels & (1 << kShaderChannelNormal)) + { + *(Vector3f*)buffer = (*cloth.m_NormalsForRendering)[v]; + buffer += sizeof(Vector3f); + } + + if (activeChannels & (1 << kShaderChannelColor)) + { + *(ColorRGBA32*)buffer = cloth.m_Colors[v]; + buffer += sizeof(ColorRGBA32); + } + + if (activeChannels & (1 << kShaderChannelTexCoord0)) + { + *(Vector2f*)buffer = cloth.m_UVs[v]; + buffer += sizeof(Vector2f); + } + + if (activeChannels & (1 << kShaderChannelTexCoord1)) + { + *(Vector2f*)buffer = cloth.m_UV1s[v]; + buffer += sizeof(Vector2f); + } + + if (activeChannels & (1 << kShaderChannelTangent)) + { + *(Vector4f*)buffer = cloth.m_Tangents[v]; + buffer += sizeof(Vector4f); + } + } + m_VBO->UnmapVertexStream(0); + + m_ChannelsInVBO = activeChannels; + m_VBO->UpdateIndexData (indexBuffer); +} + +void ClothRenderer::UpdateClothVerticesFromPhysics() +{ + Unity::Cloth& cloth = GetComponent(InteractiveCloth); + cloth.ProcessMeshForRenderer(); + cloth.m_NumVerticesFromPhysX = 0; + + UpdateAABB(); +} + +PROFILER_INFORMATION(gClothRenderProfile, "DeformableMeshRenderer.Render", kProfilerRender) + +void ClothRenderer::Render (int/* subsetIndex*/, const ChannelAssigns& channels) +{ + PROFILER_AUTO(gClothRenderProfile, this) + + UInt32 requiredChannels = channels.GetSourceMap(); + + Unity::Cloth& cloth = GetComponent(InteractiveCloth); + if (cloth.m_NumVertices > 0 || cloth.m_NumVerticesFromPhysX > 0) + { + if ((requiredChannels | m_ChannelsInVBO) != m_ChannelsInVBO || cloth.m_NumVerticesFromPhysX || m_VBO->IsVertexBufferLost()) + { + if (cloth.m_NumVerticesFromPhysX) + UpdateClothVerticesFromPhysics(); + + UpdateClothVBOImmediate(requiredChannels, m_UnavailableInVBO); + } + + if (m_CustomProperties) + GetGfxDevice().SetMaterialProperties (*m_CustomProperties); + m_VBO->DrawVBO (channels, 0, cloth.m_NumIndices, kPrimitiveTriangles, 0, cloth.m_NumVerticesForRendering); + } + GPU_TIMESTAMP(); +} + +void ClothRenderer::UpdateAABB() +{ + Unity::Cloth& cloth = GetComponent(InteractiveCloth); + dynamic_array<Vector3f> &vertices = *cloth.m_VerticesForRendering; + if (cloth.m_NumVertices != 0) + { + m_AABB.SetCenterAndExtent(vertices[0], Vector3f::zero); + for (int i=0; i<cloth.m_NumVertices; i++) + m_AABB.Encapsulate(vertices[i]); + } + else + m_AABB.SetCenterAndExtent(Vector3f::zero, Vector3f::zero); + + BoundsChanged(); + m_AABBDirty = false; +} + +void ClothRenderer::RendererBecameVisible () +{ + Super::RendererBecameVisible(); + Unity::Cloth& cloth = GetComponent(InteractiveCloth); + if (m_IsPaused) + { + m_IsPaused = false; + cloth.SetSuspended(false); + } +} + +void ClothRenderer::RendererBecameInvisible () +{ + Super::RendererBecameInvisible(); + Unity::Cloth& cloth = GetComponent(InteractiveCloth); + if (m_PauseWhenNotVisible) + { + m_IsPaused = true; + cloth.SetSuspended(true); + } +} + +void ClothRenderer::UpdateTransformInfo () +{ + { // transform + m_TransformInfo.worldMatrix.SetIdentity (); + m_TransformInfo.transformType = kNoScaleTransform; + m_TransformInfo.invScale = 1.0F; + } + if (m_AABBDirty) + UpdateClothVerticesFromPhysics (); + + m_TransformInfo.worldAABB = m_AABB; + + Transform& t = GetComponent(Transform); + TransformAABB( m_AABB, t.GetWorldToLocalMatrixNoScale(), m_TransformInfo.localAABB ); +} + + +void ClothRenderer::UnloadVBOFromGfxDevice() +{ + if (m_VBO) + { + GetGfxDevice().DeleteVBO (m_VBO); + } + m_VBO = NULL; +} + + +void ClothRenderer::ReloadVBOToGfxDevice() +{ + m_VBO = GetGfxDevice().CreateVBO(); + m_VBO->SetVertexStreamMode(0, VBO::kStreamModeDynamic); + m_VBO->SetIndicesDynamic(true); + m_ChannelsInVBO = 0; + m_UnavailableInVBO = 0; +} + + +template<class TransferFunction> +void ClothRenderer::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + TRANSFER (m_PauseWhenNotVisible); +} + +IMPLEMENT_CLASS (ClothRenderer) +IMPLEMENT_OBJECT_SERIALIZE (ClothRenderer) + +#endif // ENABLE_CLOTH diff --git a/Runtime/Dynamics/ClothRenderer.h b/Runtime/Dynamics/ClothRenderer.h new file mode 100644 index 0000000..06e4cd6 --- /dev/null +++ b/Runtime/Dynamics/ClothRenderer.h @@ -0,0 +1,54 @@ +#pragma once +#include "Configuration/UnityConfigure.h" + +#if ENABLE_CLOTH + +#include "Runtime/Filters/Renderer.h" + +class VBO; + +namespace Unity { class Cloth; } + + +class ClothRenderer : public Renderer +{ +public: + REGISTER_DERIVED_CLASS (ClothRenderer, Renderer) + DECLARE_OBJECT_SERIALIZE (ClothRenderer) + + ClothRenderer (MemLabelId label, ObjectCreationMode mode); + + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + virtual void Reset (); + virtual void Render (int/* subsetIndex*/, const ChannelAssigns& channels); + + virtual void UpdateTransformInfo(); + + virtual void RendererBecameVisible(); + virtual void RendererBecameInvisible(); + + bool GetPauseWhenNotVisible() const { return m_PauseWhenNotVisible; } + void SetPauseWhenNotVisible(bool pause) { SetDirty(); m_PauseWhenNotVisible = pause; } + + void UnloadVBOFromGfxDevice(); + void ReloadVBOToGfxDevice(); + +private: + + void UpdateClothVBOImmediate (int requiredChannels, UInt32& unavailableChannels); + void UpdateClothVerticesFromPhysics (); + void UpdateAABB (); + + VBO* m_VBO; + UInt32 m_ChannelsInVBO; + UInt32 m_UnavailableInVBO; + AABB m_AABB; + bool m_PauseWhenNotVisible; + bool m_IsPaused; + bool m_AABBDirty; + + friend class DeformableMesh; +}; + +#endif // ENABLE_CLOTH diff --git a/Runtime/Dynamics/Collider.cpp b/Runtime/Dynamics/Collider.cpp new file mode 100644 index 0000000..0a1a10a --- /dev/null +++ b/Runtime/Dynamics/Collider.cpp @@ -0,0 +1,598 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Collider.h" +#include "RigidBody.h" +#include "Runtime/Graphics/Transform.h" +#include "PhysicMaterial.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "PhysicsManager.h" +#include "Runtime/BaseClasses/SupportedMessageOptimization.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/ScriptingExportUtility.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Scripting/Scripting.h" + +/* Problems and limitations + +- When a rigid body is added or activated after the colliders are setup it won't attach them automatically + +- When a rigid body is destroyed it will recreate all its colliders as colliders without rigidbody. (Wasteful) + We should have activity state be treated hierarchically and deactivating/destroying children before parents. + +- Implement automatic instantiation when accessing a material + +- Colliders should be enlarged by min penetration epsilon. This way we prevent interpentration completely. + +- Get rigidbody only works when the rigidbody is active! + +*/ + +#if ENABLE_PROFILER +ProfilerInformation gStaticColliderModify ("Static Collider.Modify (Expensive delayed cost)", kProfilerPhysics, true); +ProfilerInformation gStaticColliderMove ("Static Collider.Move (Expensive delayed cost)", kProfilerPhysics, true); +ProfilerInformation gStaticColliderCreate ("Static Collider.Create (Expensive delayed cost)", kProfilerPhysics, true); +ProfilerInformation gDynamicColliderCreate ("Dynamic Collider.Create", kProfilerRender); +#endif + +Collider::Collider (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + m_Shape = NULL; + m_IsTrigger = false; + m_Enabled = true; +} + +Collider::~Collider () +{ + Cleanup (); +} + +void Collider::SetEnabled (bool enab) +{ + if ((bool)m_Enabled == enab) + return; + m_Enabled = enab; + Cleanup (); + CreateShapeIfNeeded (); + SetDirty (); +} + +void Collider::RecreateCollider (const Rigidbody* ignoreCollider) +{ + AssertIf (GetClassID() == 143);// Charactercontroller + + if (IsActive () && GetEnabled()) + Create (ignoreCollider); +} + +Rigidbody* Collider::GetRigidbody () +{ + if (m_Shape) + { + Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData; + return body; + } + else + return NULL; +} + +void Collider::Deactivate (DeactivateOperation operation) +{ + Super::Deactivate (operation); + Cleanup (); +} + +NxShape* Collider::CreateShapeIfNeeded() +{ + if (m_Shape != NULL) + return m_Shape; + if (IsActive () && GetEnabled()) + Create(NULL); + + return m_Shape; +} + +void Collider::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + + if (IsActive () && GetEnabled()) + { + if (m_Shape) + { + if (SupportsMaterial()) + SetMaterial (m_Material); + SetIsTrigger (m_IsTrigger); + } + CreateShapeIfNeeded(); + } + else + Cleanup (); +} + +bool Collider::HasActorRigidbody () +{ + return m_Shape && m_Shape->getActor ().userData; +} + +bool Collider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix) +{ + if (&transform == &anyParent) + { + matrix.SetIdentity (); + return true; + } + else + { + Vector3f childPosition = transform.GetPosition (); + Quaternionf childRotation = transform.GetRotation (); + + Matrix4x4f childMatrix, parentMatrix; + + childMatrix.SetTR (childPosition, childRotation); + parentMatrix = anyParent.GetWorldToLocalMatrixNoScale (); + + MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix); + ErrorFiniteParameterReturnFalse(matrix) + return true; + } +} + +bool Collider::GetRelativeToParentPositionAndRotationUtility (Transform& transform, Transform& anyParent, const Vector3f& localOffset, Matrix4x4f& matrix) +{ + Vector3f childPosition = transform.TransformPoint (localOffset); + Quaternionf childRotation = transform.GetRotation (); + + Matrix4x4f childMatrix, parentMatrix; + + childMatrix.SetTR (childPosition, childRotation); + parentMatrix = anyParent.GetWorldToLocalMatrixNoScale (); + + MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix); + ErrorFiniteParameterReturnFalse(matrix) + return true; +} + +void Collider::FetchPoseFromTransformUtility (const Vector3f& offset) +{ + AssertIf (m_Shape == NULL); + AssertIf (HasActorRigidbody ()); + Transform& transform = GetComponent (Transform); + + Vector3f pos = transform.TransformPoint (offset); + Quaternionf rot = transform.GetRotation (); + + AssertFiniteParameter(pos) + AssertFiniteParameter(rot) + + NxMat34 shapeMatrix ((const NxQuat&)rot, (const NxVec3&)pos); + m_Shape->setGlobalPose(shapeMatrix); +} + +void Collider::FetchPoseFromTransform () +{ + AssertIf (m_Shape == NULL); + AssertIf (HasActorRigidbody ()); + Transform& transform = GetComponent (Transform); + + Quaternionf rot; Vector3f pos; + transform.GetPositionAndRotation(pos, rot); + + AssertFiniteParameter(pos) + AssertFiniteParameter(rot) + + NxMat34 shapeMatrix ((const NxQuat&)rot, (const NxVec3&)pos); + m_Shape->setGlobalPose(shapeMatrix); +} + +void Collider::RigidbodyMassDistributionChanged () +{ + if (m_Shape) + { + Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData; + if (body) + body->UpdateMassDistribution(); + } +} + +void Collider::CreateWithoutIgnoreAttach () +{ + if ((IsActive () && GetEnabled()) || !IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + Create(NULL); +} + +NxCCDSkeleton* Collider::CreateCCDSkeleton() +{ + float kCCDScale = 0.8f; + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1)) + kCCDScale = 0.5f; + return CreateCCDSkeleton(kCCDScale); +} + +void Collider::UpdateCCDSkeleton() +{ + if (m_Shape) + { + Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData; + if (body && body->GetCollisionDetectionMode() != Rigidbody::kCCDModeOff) + { + NxCCDSkeleton *skel = m_Shape->getCCDSkeleton(); + + m_Shape->setCCDSkeleton(CreateCCDSkeleton()); + + if (skel) + GetDynamicsSDK().releaseCCDSkeleton (*skel); + } + } +} + + +void Collider::FinalizeCreate( NxShapeDesc& shapeDesc, bool setMaterial, const Rigidbody* dontAttachToRigidbody ) +{ + AssertIf (GetClassID() == 143);// Charactercontroller + + AssertIf (m_Shape != NULL); + if (m_IsTrigger) + { + shapeDesc.shapeFlags |= NX_TRIGGER_ENABLE; + if (!GetPhysicsManager().GetRaycastsHitTriggers()) + shapeDesc.shapeFlags |= NX_SF_DISABLE_RAYCASTING; + } + + Rigidbody* body = FindNewAttachedRigidbody (dontAttachToRigidbody); + + shapeDesc.userData = this; + // if !setMaterial is passed, the caller has the material set up (e.g. WheelCollider doesn't want it) + if( setMaterial ) + { + shapeDesc.materialIndex = GetMaterialIndex (); + } + shapeDesc.group = GetGameObject ().GetLayer (); + + if (!shapeDesc.isValid()) + { + ErrorStringObject ("This collider has some illegal parameters. Choose 'Reset' in the component popup menu to fix it.", this); + return; + } + + if (body) + { + PROFILER_AUTO(gDynamicColliderCreate, this) + + if (body->GetCollisionDetectionMode() != Rigidbody::kCCDModeOff) + { + if (body->GetCollisionDetectionMode() == Rigidbody::kCCDModeDynamic) + shapeDesc.shapeFlags |= NX_SF_DYNAMIC_DYNAMIC_CCD; + + // CCD skeletons should be smaller then the normal colliders so that collisions at normal speeds can + // be handled by those. + NxCCDSkeleton *skel = CreateCCDSkeleton(); + shapeDesc.ccdSkeleton = skel; + } + + body->Create (true); + Matrix4x4f matrix; + NxActor* actor = body->m_Actor; + if (actor == NULL) + { + ErrorStringObject ("Could not create actor. Maybe you are using too many colliders or rigidbodies in your scene?", this); + return; + } + + if (GetRelativeToParentPositionAndRotation (GetComponent (Transform), body->GetComponent (Transform), matrix)) + { + shapeDesc.localPose.setColumnMajor44 (matrix.GetPtr ()); + + m_Shape = actor->createShape (shapeDesc); + } + + if (!m_IsTrigger && GetClassID()!= 140) + actor->updateMassFromShapes (0.0F, body->GetMass ()); + } + else + { + PROFILER_AUTO(gStaticColliderCreate, this) + + NxActorDesc actorDesc; + actorDesc.userData = NULL; + actorDesc.shapes.push_back (&shapeDesc); + NxActor* actor = GetDynamicsScene ().createActor (actorDesc); + if (actor == NULL) + { + ErrorStringObject ("Could not create actor. Maybe you are using too many colliders or rigidbodies in your scene?", this); + return; + } + + m_Shape = actor->getShapes ()[0]; + FetchPoseFromTransform (); + + SupportedMessagesDidChange (GetGameObject ().GetSupportedMessages ()); + } +} + +void Collider::SupportedMessagesDidChange (int supported) +{ + // We only deal with colliders with no rigid body attached + if (m_Shape == NULL || m_Shape->getActor ().userData != NULL) + return; + + if (supported & kHasCollisionStay) + m_Shape->getActor ().setGroup (kContactTouchGroup); + else if (supported & (kHasCollisionStay | kHasCollisionEnterExit)) + m_Shape->getActor ().setGroup (kContactEnterExitGroup); + else + m_Shape->getActor ().setGroup (kContactNothingGroup); +} + +void Collider::SetupLayer () +{ + if (m_Shape) + m_Shape->setGroup (GetGameObject ().GetLayer ()); +} + +Rigidbody* Collider::FindNewAttachedRigidbody (const Rigidbody* ignoreAttachRigidbody) +{ + Rigidbody* body = QueryComponent (Rigidbody); + if (body && body->IsActive () && body != ignoreAttachRigidbody) + return body; + + Transform* parent = GetComponent (Transform).GetParent (); + while (parent) + { + GameObject* go = parent->GetGameObjectPtr (); + if (go) + body = go->QueryComponent (Rigidbody); + else + body = NULL; + if (body && body->IsActive () && body != ignoreAttachRigidbody) + return body; + + parent = parent->GetParent (); + } + return NULL; +} + +int Collider::GetMaterialIndex () +{ + PhysicMaterial* material = m_Material; + if (material) + return material->GetMaterialIndex (); + else + return 0; +} + +void Collider::SetIsTrigger (bool trigger) +{ + if (m_IsTrigger != trigger) + { + SetDirty (); + m_IsTrigger = trigger; + } + + if (m_Shape) + { + m_Shape->setFlag (NX_TRIGGER_ENABLE, trigger); + m_Shape->setFlag (NX_SF_DISABLE_RAYCASTING, trigger && !GetPhysicsManager().GetRaycastsHitTriggers()); + + RigidbodyMassDistributionChanged (); + } +} + +AABB Collider::GetBounds () +{ + if (m_Shape) + { + AABB aabb; + NxBounds3 bounds; + m_Shape->getWorldBounds(bounds); + bounds.getExtents ((NxVec3&)aabb.GetExtent ()); + bounds.getCenter ((NxVec3&)aabb.GetCenter ()); + return aabb; + } + else + { + return AABB (GetComponent (Transform).GetPosition (), Vector3f::zero); + } +} + +void Collider::Cleanup () +{ + if (m_Shape) + { + AssertIf (GetClassID() == 143); + + NxCCDSkeleton *skel = m_Shape->getCCDSkeleton(); + + if (m_Shape->getActor ().userData) + m_Shape->getActor ().releaseShape (*m_Shape); + else + GetDynamicsScene ().releaseActor (m_Shape->getActor ()); + + // Need to release skeleton after the actor or shape using it is release by physics, + // so we don't get an error about releasing a ccd mesh which is in use. + if (skel) + GetDynamicsSDK().releaseCCDSkeleton (*skel); + + m_Shape = NULL; + } +} + +void Collider::ReCreate() +{ + if( !m_Shape ) + return; + Cleanup(); + Create(NULL); +} + +PPtr<PhysicMaterial> Collider::GetMaterial () +{ + return m_Material; +} + +void Collider::SetMaterial (PPtr<PhysicMaterial> material) +{ + if (!SupportsMaterial()) + ErrorStringObject ("Setting the Material property is not supported for Colliders of type " + GetClassName() + ".", this); + + if (m_Material != material) + { + SetDirty (); + m_Material = material; + } + + if (m_Shape) + m_Shape->setMaterial (GetMaterialIndex ()); +} + +void Collider::TransformChanged (int changeMask) +{ + if (m_Shape) + { + if (changeMask & Transform::kParentingChanged) + { + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_4_a1)) + { + // Only recreate if the rigidbody attachement is actually changed by this. + // Otherwise we might unnecessarily cause trigger state to reset. + Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData; + Rigidbody* newBody = FindNewAttachedRigidbody (NULL); + if (newBody != body) + ReCreate(); + } + else if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1)) + ReCreate(); + } + } +} + +void Collider::ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance) +{ + outSqrDistance = std::numeric_limits<float>::infinity(); + + if (m_Shape) + { + NxBounds3 bounds; + m_Shape->getWorldBounds(bounds); + AABB aabb; + bounds.getCenter((NxVec3&)aabb.GetCenter()); + bounds.getExtents((NxVec3&)aabb.GetExtent()); + + CalculateClosestPoint(position, aabb, outPosition, outSqrDistance); + } + else + { + outPosition = GetComponent(Transform).GetPosition(); + outSqrDistance = SqrMagnitude(position - outPosition); + } +} + +bool Collider::Raycast (const Ray& ray, float distance, RaycastHit& outHit) +{ + AssertIf (!IsNormalized (ray.GetDirection ())); + + if (distance == std::numeric_limits<float>::infinity()) + distance = NX_MAX_F32; + + NxRaycastHit hit; + if (m_Shape && m_Shape->raycast ((NxRay&)ray, distance, 0xffffffff, hit, false)) + { + NxToRaycastHit(hit, outHit); + return true; + } + else + return false; +} + +#if UNITY_EDITOR +void Collider::RefreshPhysicsInEditMode() +{ + if ( !IsWorldPlaying() ) + { + GetPhysicsManager().RefreshWhenPaused(); + } +} +#endif + +template<class TransferFunction> +void Collider::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + + if (SupportsMaterial()) + TRANSFER_SIMPLE (m_Material); + + TRANSFER_SIMPLE (m_IsTrigger); + transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask | kEditorDisplaysCheckBoxMask); + transfer.Align(); +} + +void Collider::InitializeClass () +{ + REGISTER_MESSAGE_VOID (Collider, kLayerChanged, SetupLayer); + REGISTER_MESSAGE_VOID (Collider, kForceRecreateCollider, CreateWithoutIgnoreAttach); + REGISTER_MESSAGE (Collider, kTransformChanged, TransformChanged, int); +} + + +#if ENABLE_SCRIPTING +ScriptingObjectPtr ConvertContactToMono (Collision* input) +{ + Collision& contact = *reinterpret_cast<Collision*> (input); + MonoCollision monoContact; + if (contact.flipped) + { + monoContact.rigidbody = Scripting::ScriptingWrapperFor (contact.thisRigidbody); + monoContact.collider = Scripting::ScriptingWrapperFor (contact.thisCollider); + monoContact.relativeVelocity = contact.relativeVelocity; + } + else + { + monoContact.rigidbody = Scripting::ScriptingWrapperFor (contact.otherRigidbody); + monoContact.collider = Scripting::ScriptingWrapperFor (contact.otherCollider); + monoContact.relativeVelocity = -contact.relativeVelocity; + } + ScriptingArrayPtr contacts = CreateScriptingArray<MonoContactPoint>(GetMonoManager ().GetCommonClasses ().contactPoint,contact.contacts.size()); + monoContact.contacts = contacts; + int j = 0; + for (Collision::Contacts::iterator i=contact.contacts.begin ();i != contact.contacts.end ();i++) + { + #if UNITY_WINRT + MonoContactPoint contactPoint; + #else + MonoContactPoint& contactPoint = Scripting::GetScriptingArrayElement<MonoContactPoint> (contacts, j); + #endif + contactPoint.point = i->point; + if (contact.flipped) + { + contactPoint.thisCollider = Scripting::ScriptingWrapperFor (i->collider[1]); + contactPoint.otherCollider = Scripting::ScriptingWrapperFor (i->collider[0]); + contactPoint.normal = -i->normal; + } + else + { + contactPoint.thisCollider = Scripting::ScriptingWrapperFor (i->collider[0]); + contactPoint.otherCollider = Scripting::ScriptingWrapperFor (i->collider[1]); + contactPoint.normal = i->normal; + } + #if UNITY_WINRT + // A slower way to set a value in the array: + // * we create a scripting object; + // * then marshal data from contactPoint to that scripting object + // * and only then we're setting it in the array + // At the moment there's no other way, unless we remove all ScriptingObjectPtr from MonoContactPoint + Scripting::SetScriptingArrayElement(contacts, j, CreateScriptingObjectFromNativeStruct<MonoContactPoint>(GetMonoManager ().GetCommonClasses ().contactPoint, contactPoint)); + #endif + j++; + } + return CreateScriptingObjectFromNativeStruct<MonoCollision>(GetMonoManager ().GetCommonClasses ().collision, monoContact); +} +#endif //ENABLE_SCRIPTING +IMPLEMENT_CLASS_HAS_INIT (Collider) +IMPLEMENT_OBJECT_SERIALIZE (Collider) +INSTANTIATE_TEMPLATE_TRANSFER (Collider) + +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/Collider.h b/Runtime/Dynamics/Collider.h new file mode 100644 index 0000000..ff109e1 --- /dev/null +++ b/Runtime/Dynamics/Collider.h @@ -0,0 +1,168 @@ +#ifndef COLLIDER_H +#define COLLIDER_H + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Geometry/AABB.h" +#include "Runtime/Profiler/Profiler.h" + +class Transform; +class Matrix4x4f; +class Vector3f; + +class Rigidbody; +class NxShape; +class NxShapeDesc; +class NxCCDSkeleton; +class PhysicMaterial; +namespace Unity { class InteractiveCloth; } +struct RaycastHit; +class Ray; + +#if ENABLE_PROFILER +#define PROFILE_MODIFY_STATIC_COLLIDER if (m_Shape && m_Shape->getActor ().userData == NULL) { PROFILER_AUTO(gStaticColliderModify, this); } +extern ProfilerInformation gStaticColliderModify; +extern ProfilerInformation gStaticColliderMove; +extern ProfilerInformation gStaticColliderCreate; +extern ProfilerInformation gDynamicColliderCreate; +#else +#define PROFILE_MODIFY_STATIC_COLLIDER +#endif + +class Collider : public Unity::Component +{ + public: + REGISTER_DERIVED_ABSTRACT_CLASS (Collider, Component) + DECLARE_OBJECT_SERIALIZE (Collider) + + Collider (MemLabelId label, ObjectCreationMode mode); + // virtual ~Collider (); declared-in-macro + + virtual void Deactivate (DeactivateOperation operation); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + /// Enable or disable updates of this behaviour + virtual void SetEnabled (bool enab); + bool GetEnabled () const { return m_Enabled; } + + virtual bool SupportsMaterial () const { return true; } + PPtr<PhysicMaterial> GetMaterial (); + void SetMaterial (PPtr<PhysicMaterial> material); + + virtual void SetIsTrigger (bool trigger); + bool GetIsTrigger () { return m_IsTrigger; } + + Rigidbody* GetRigidbody (); + + static void InitializeClass (); + static void CleanupClass () {} + void SetupLayer (); + + virtual void SupportedMessagesDidChange (int supported); + + virtual AABB GetBounds (); + + void ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance); + + void SetupIgnoreLayer (); + + bool Raycast (const Ray& ray, float distance, RaycastHit& outHit); + + public: + + // SUBCLASSES OVERRIDE THIS + virtual void Create (const Rigidbody* ignoreAttachRigidbody) = 0; + virtual void ScaleChanged () = 0; + virtual void Cleanup (); + virtual void ReCreate(); + + void CreateWithoutIgnoreAttach (); + + NxShape* CreateShapeIfNeeded(); + + // Testing API + NxShape* GetShape() { return m_Shape; } + + protected: + + enum { kForceUpdateMass = 1 << 31 }; + virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix); + static bool GetRelativeToParentPositionAndRotationUtility (Transform& transform, Transform& anyParent, const Vector3f& localOffset, Matrix4x4f& matrix); + + bool HasActorRigidbody (); + int GetMaterialIndex (); + + void RecreateCollider (const Rigidbody* ignoreRigidbody); + + virtual void TransformChanged (int changeMask); + + virtual void FetchPoseFromTransform (); + void FetchPoseFromTransformUtility (const Vector3f& offset); + + void FinalizeCreate( NxShapeDesc& shape, bool dontSetMaterial, const Rigidbody* dontAttachToRigidbody ); + virtual NxCCDSkeleton* CreateCCDSkeleton(float scale) { return NULL; } + NxCCDSkeleton* CreateCCDSkeleton(); + void UpdateCCDSkeleton(); + + void RigidbodyMassDistributionChanged (); + + #if UNITY_EDITOR + void RefreshPhysicsInEditMode(); + #else + void RefreshPhysicsInEditMode() {} + #endif + + /// Finds the rigid body we want this collider to attach to. + /// If this returns null the collider will create a kinematic actor. + Rigidbody* FindNewAttachedRigidbody (const Rigidbody* ignoreAttachRigidbody); + + PPtr<PhysicMaterial> m_Material; + NxShape* m_Shape; + bool m_IsTrigger; + bool m_Enabled; + + friend class Rigidbody; + friend class Unity::InteractiveCloth; + friend class PhysicsManager; +}; + + + /* + PhysicMaterial& GetInstantiatedMaterial () + { + PhysicMaterial* material = m_Material; + if (material) + { + if (material->m_Owner == PPtr<Object> (this)) + return material; + + + } + else + { + material = new PhysicMaterial (); + material->Reset (); + material->m_Owner = this; + m_Material = material; + } + }*/ +struct MonoContactPoint +{ + Vector3f point; + Vector3f normal; + ScriptingObjectPtr thisCollider; + ScriptingObjectPtr otherCollider; +}; + +struct MonoCollision +{ + Vector3f relativeVelocity; + ScriptingObjectPtr rigidbody; + ScriptingObjectPtr collider; + ScriptingArrayPtr contacts; +}; + + +struct Collision; +ScriptingObjectPtr ConvertContactToMono (Collision* input); + +#endif diff --git a/Runtime/Dynamics/CollisionMeshData.cpp b/Runtime/Dynamics/CollisionMeshData.cpp new file mode 100644 index 0000000..d267495 --- /dev/null +++ b/Runtime/Dynamics/CollisionMeshData.cpp @@ -0,0 +1,121 @@ +#include "UnityPrefix.h" +#include "CollisionMeshData.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Math/Matrix4x4.h" +#include "Runtime/Profiler/Profiler.h" +//#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +//#include "Runtime/Dynamics/PhysicsManager.h" +#include "Runtime/Dynamics/nxmemorystream.h" +//#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h" +#include "Runtime/Interfaces/IPhysics.h" +//#include "Runtime/Dynamics/ExtractDataFromMesh.h" + +CollisionMeshData::CollisionMeshData () +{ + m_NxConvexMesh = NULL; + m_NxTriangleMesh = NULL; + m_SharedPhysicsMeshDirty = false; +} + +CollisionMeshData::~CollisionMeshData () +{ + Cleanup (); +} + +void CollisionMeshData::Cleanup () +{ + if (m_NxTriangleMesh) + { + GetIPhysics()->ReleaseNxTriangleMesh(*reinterpret_cast<NxTriangleMesh*> (m_NxTriangleMesh)); + m_NxTriangleMesh = NULL; + } + + if (m_NxConvexMesh) + { + GetIPhysics()->ReleaseNxConvexMesh(*reinterpret_cast<NxConvexMesh*> (m_NxConvexMesh)); + m_NxConvexMesh = NULL; + } +} + +void CollisionMeshData::VertexDataHasChanged () +{ + if (m_NxConvexMesh != NULL || m_NxTriangleMesh != NULL) + m_SharedPhysicsMeshDirty = true; +} + +void* CollisionMeshData::GetSharedNxMesh (Mesh& meshData) +{ +#if ENABLE_PHYSICS + if (m_NxTriangleMesh != NULL) + return m_NxTriangleMesh; + Matrix4x4f identity; identity.SetIdentity(); + m_NxTriangleMesh = GetIPhysics()->CreateNxMeshFromUnityMesh(&meshData, false, identity, kNoScaleTransform); +#endif + return m_NxTriangleMesh; +} + + +void* CollisionMeshData::GetSharedNxConvexMesh (Mesh& meshData) +{ +#if ENABLE_PHYSICS + if (m_NxConvexMesh != NULL) + return m_NxConvexMesh; + Matrix4x4f identity; identity.SetIdentity(); + m_NxConvexMesh = GetIPhysics()->CreateNxMeshFromUnityMesh(&meshData, true, identity, kNoScaleTransform); +#endif + return m_NxConvexMesh; +} + +void CollisionMeshData::AwakeFromLoadThreaded(Mesh& meshData) +{ +#if !USE_PREBAKED_COLLISIONMESH && ENABLE_PHYSICS + int meshUsageFlags = meshData.GetMeshUsageFlags(); + IPhysics* physics = GetIPhysics(); + + if (meshUsageFlags & kRequiresSharedTriangleCollisionMesh) + m_NxTriangleMesh = physics->CreateNxStreamFromUnityMesh(meshData, false); + + if (meshUsageFlags & kRequiresSharedConvexCollisionMesh) + m_NxConvexMesh = physics->CreateNxStreamFromUnityMesh(meshData, true); +#endif +} + +void CollisionMeshData::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + // Mark the shared physics mesh dirty when we have a mesh representation already and we are modifying the mesh (as opposed to just loading it from disk) + if ((m_NxConvexMesh != NULL || m_NxTriangleMesh != NULL) && (awakeMode & kDidLoadFromDisk) == 0 ) + m_SharedPhysicsMeshDirty = true; + + IPhysics* physics = GetIPhysics(); + +#if USE_PREBAKED_COLLISIONMESH + if (!m_BakedConvexCollisionMesh.empty()) + { + m_NxConvexMesh = physics->CreateNxMeshFromByteStream(TRUE,m_BakedConvexCollisionMesh); + m_BakedConvexCollisionMesh.clear(); + } + if (!m_BakedTriangleCollisionMesh.empty()) + { + m_NxTriangleMesh = physics->CreateNxMeshFromByteStream(FALSE,m_BakedTriangleCollisionMesh); + m_BakedTriangleCollisionMesh.clear(); + } +#else + + if (awakeMode & kDidLoadThreaded) + { + if (m_NxTriangleMesh) + { + MemoryStream* stream = reinterpret_cast<MemoryStream*>(m_NxTriangleMesh); + m_NxTriangleMesh = physics->CreateNxMeshFromNxStream (false, *stream); + physics->DeleteMemoryStream(stream); + } + + if (m_NxConvexMesh) + { + MemoryStream* stream = reinterpret_cast<MemoryStream*>(m_NxConvexMesh); + m_NxConvexMesh = physics->CreateNxMeshFromNxStream (true, *stream); + physics->DeleteMemoryStream(stream); + } + } +#endif +} diff --git a/Runtime/Dynamics/CollisionMeshData.h b/Runtime/Dynamics/CollisionMeshData.h new file mode 100644 index 0000000..95aa6d9 --- /dev/null +++ b/Runtime/Dynamics/CollisionMeshData.h @@ -0,0 +1,87 @@ +#ifndef COLLISION_MESH_DATA +#define COLLISION_MESH_DATA + +#include "Runtime/Utilities/dynamic_array.h" +#include "Runtime/BaseClasses/BaseObject.h" +#include "Runtime/Math/Matrix4x4.h" +#include "Editor/Interfaces/IPhysicsEditor.h" + +class Mesh; + +enum +{ + kMeshMustKeepVertexAndIndexData = 1 << 0, + kRequiresSharedConvexCollisionMesh = 1 << 1, // Mesh can be shared and should be precomputed on standalone platforms + kRequiresSharedTriangleCollisionMesh = 1 << 2, // Mesh can be shared and should be precomputed on standalone platforms + kRequiresScaledCollisionMesh = 1 << 3 // Mesh is used as mesh collider but is scaled +}; + +class EXPORT_COREMODULE CollisionMeshData +{ + void* m_NxConvexMesh; + void* m_NxTriangleMesh; + bool m_SharedPhysicsMeshDirty; + +public: + +#if USE_PREBAKED_COLLISIONMESH + dynamic_array<UInt8> m_BakedTriangleCollisionMesh; + dynamic_array<UInt8> m_BakedConvexCollisionMesh; +#endif + + CollisionMeshData (); + ~CollisionMeshData (); + + void Cleanup (); + + template<class TransferFunction> + void Transfer (TransferFunction& transfer, Mesh& mesh); + + void VertexDataHasChanged (); + + void AwakeFromLoad (AwakeFromLoadMode awake); + void AwakeFromLoadThreaded(Mesh& meshData); + + void* GetSharedNxMesh (Mesh& mesh); + void* GetSharedNxConvexMesh (Mesh& mesh); + + bool IsSharedPhysicsMeshDirty () { return m_SharedPhysicsMeshDirty; } + +}; + +EXPORT_COREMODULE void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType ); + +template<class TransferFunction> +inline void CollisionMeshData::Transfer (TransferFunction& transfer, Mesh& mesh) +{ +#if UNITY_EDITOR + // When building player we precalcuate mesh usage based on who uses the different MeshColliders in different scenes. + if (transfer.IsWritingGameReleaseData()) + { + int buildMeshUsageFlags = transfer.GetBuildUsage().meshUsageFlags; + + // Bake physX meshes + if (transfer.GetFlags() & kGenerateBakedPhysixMeshes) + { + IPhysicsEditor* physicsEditor = GetIPhysicsEditor(); + Assert(physicsEditor != NULL) ; + + dynamic_array<UInt8> bakedConvex; + if (buildMeshUsageFlags & kRequiresSharedConvexCollisionMesh) + physicsEditor->BakeMesh (&mesh, true, ShouldSerializeForBigEndian(transfer), bakedConvex); + transfer.Transfer (bakedConvex, "m_BakedConvexCollisionMesh", kHideInEditorMask); + + dynamic_array<UInt8> bakedConcave; + if (buildMeshUsageFlags & kRequiresSharedTriangleCollisionMesh) + physicsEditor->BakeMesh (&mesh, false, ShouldSerializeForBigEndian(transfer), bakedConcave); + transfer.Transfer (bakedConcave, "m_BakedTriangleCollisionMesh", kHideInEditorMask); + } + } +#endif +#if USE_PREBAKED_COLLISIONMESH + transfer.Transfer (m_BakedConvexCollisionMesh, "m_BakedConvexCollisionMesh", kHideInEditorMask); + transfer.Transfer (m_BakedTriangleCollisionMesh, "m_BakedTriangleCollisionMesh", kHideInEditorMask); +#endif +} + +#endif
\ No newline at end of file diff --git a/Runtime/Dynamics/ConfigurableJoint.cpp b/Runtime/Dynamics/ConfigurableJoint.cpp new file mode 100644 index 0000000..b036e24 --- /dev/null +++ b/Runtime/Dynamics/ConfigurableJoint.cpp @@ -0,0 +1,479 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "ConfigurableJoint.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h" +#include "PhysicsManager.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/Misc/BuildSettings.h" +#include "NxWrapperUtility.h" + +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" + +using namespace std; + +namespace Unity +{ + +#define GET_JOINT() static_cast<NxD6Joint*> (m_Joint) + + +/* +- We awake the hingejoint only once. (AwakeFromLoad) + At this point we setup the axes. They are never changed afterwards + -> The perfect solution remembers the old position/rotation of the rigid bodies. + Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state! +*/ + +void ConfigurableJoint::InitializeClass () +{ + #if UNITY_EDITOR + // Only introduced during 2.0 alpha + RegisterAllowNameConversion (ConfigurableJoint::GetClassStringStatic(), "m_ConfigureInWorldSpace", "m_ConfiguredInWorldSpace"); + #endif +} + + + +ConfigurableJoint::ConfigurableJoint (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +ConfigurableJoint::~ConfigurableJoint () +{ +} + +void ConfigurableJoint::CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const +{ + const Transform& transform = GetComponent (Transform); + + Vector3f localAxis = m_Axis; + if (SqrMagnitude (localAxis) < Vector3f::epsilon) + localAxis = Vector3f (1.0F, 0.0F, 0.0F); + Vector3f localNormal = m_SecondaryAxis; + + OrthoNormalize (&localAxis, &localNormal); + + globalAnchor = transform.TransformPoint (m_Anchor); + //Vector3f globalRigidbodyPos = transform.GetPosition (); + + if (m_ConfiguredInWorldSpace) + { + globalAxis = localAxis; + globalNormal = localNormal; + } + else + { + globalAxis = transform.TransformDirection (localAxis); + globalNormal = transform.TransformDirection (localNormal); + } +// Matrix3x3f m; +// m.SetOrthoNormalBasisInverse(globalAxis, globalNormal, Cross (globalAxis, globalNormal)); +// m.SetOrthoNormalBasisInverse(Vector3f::xAxis, globalNormal, Cross (globalAxis, globalNormal)); +// m.SetOrthoNormalBasisInverse(globalNormal, Cross (globalAxis, globalNormal), globalAxis); +// OrthoNormalize(m); +// Quaternionf q; +// MatrixToQuaternion(m, q); +// m_ConfigurationSpace = q * Inverse(transform.GetRotation ()); +} + +void ConfigurableJoint::Reset () +{ + Super::Reset(); + + m_XMotion = NX_D6JOINT_MOTION_FREE; + m_YMotion = NX_D6JOINT_MOTION_FREE; + m_ZMotion = NX_D6JOINT_MOTION_FREE; + + m_AngularXMotion = NX_D6JOINT_MOTION_FREE; + m_AngularYMotion = NX_D6JOINT_MOTION_FREE; + m_AngularZMotion = NX_D6JOINT_MOTION_FREE; + + InitSoftJointLimit(m_LinearLimit); + InitSoftJointLimit(m_LowAngularXLimit); + InitSoftJointLimit(m_HighAngularXLimit); + InitSoftJointLimit(m_AngularYLimit); + InitSoftJointLimit(m_AngularZLimit); + + InitJointDrive(m_XDrive); + InitJointDrive(m_YDrive); + InitJointDrive(m_ZDrive); + InitJointDrive(m_AngularXDrive); + InitJointDrive(m_AngularYZDrive); + InitJointDrive(m_SlerpDrive); + + m_ProjectionMode = 0; + m_ProjectionDistance = 0.1F; + m_ProjectionAngle = 5.0F; +// m_GearRatio = 1.0F; + m_RotationDriveMode = 0; +// m_UseGear = false; + m_TargetAngularVelocity = m_TargetVelocity = m_TargetPosition = Vector3f(0.0F, 0.0F, 0.0F); + m_TargetRotation = Quaternionf::identity(); + m_SecondaryAxis = Vector3f::yAxis; + m_ConfiguredInWorldSpace = false; + m_SwapBodies = false; +} + +void ConfigurableJoint::CheckConsistency () +{ + Super::CheckConsistency(); +} + +void ConfigurableJoint::SetXMotion (int motion) +{ + m_XMotion = motion; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetYMotion (int motion) +{ + m_YMotion = motion; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetAngularXMotion (int motion) +{ + m_AngularXMotion = motion; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetAngularYMotion (int motion) +{ + m_AngularYMotion = motion; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetAngularZMotion (int motion) +{ + m_AngularZMotion = motion; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetZMotion (int motion) +{ + m_ZMotion = motion; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetLinearLimit (const SoftJointLimit& limit) +{ + m_LinearLimit = limit; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetAngularZLimit (const SoftJointLimit& limit) +{ + m_AngularZLimit = limit; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetAngularYLimit (const SoftJointLimit& limit) +{ + m_AngularYLimit = limit; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetLowAngularXLimit (const SoftJointLimit& limit) +{ + m_LowAngularXLimit = limit; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetHighAngularXLimit (const SoftJointLimit& limit) +{ + m_HighAngularXLimit = limit; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetXDrive (const JointDrive& drive) +{ + m_XDrive = drive; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetYDrive (const JointDrive& drive) +{ + m_YDrive = drive; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetZDrive (const JointDrive& drive) +{ + m_ZDrive = drive; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetAngularXDrive (const JointDrive& drive) +{ + m_AngularXDrive = drive; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetAngularYZDrive (const JointDrive& drive) +{ + m_AngularYZDrive = drive; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetSlerpDrive (const JointDrive& drive) +{ + m_SlerpDrive = drive; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetRotationDriveMode (int mode) +{ + m_RotationDriveMode = mode; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetProjectionMode (int mode) +{ + m_ProjectionMode = mode; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetProjectionDistance (float dist) +{ + m_ProjectionDistance = dist; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetProjectionAngle (float angle) +{ + m_ProjectionAngle = angle; + ApplyKeepConfigurationSpace(); +} + +void ConfigurableJoint::SetConfiguredInWorldSpace (bool c) +{ + m_ConfiguredInWorldSpace = c; + ApplyRebuildConfigurationSpace(); +} + +void ConfigurableJoint::SetSwapBodies (bool c) +{ + m_SwapBodies = c; + ApplyRebuildConfigurationSpace(); +} + +void ConfigurableJoint::SetTargetPosition (const Vector3f& pos) +{ + m_TargetPosition = pos; + if (m_Joint) + GET_JOINT()->setDrivePosition(Vec3ToNx(pos)); +} + +void ConfigurableJoint::SetTargetRotation (const Quaternionf& rotation) +{ + m_TargetRotation = rotation; + if (m_Joint) + GET_JOINT()->setDriveOrientation((const NxQuat&)rotation); +} + +void ConfigurableJoint::SetTargetVelocity (const Vector3f& vel) +{ + m_TargetVelocity = vel; + if (m_Joint) + GET_JOINT()->setDriveLinearVelocity(Vec3ToNx(vel)); +} + +void ConfigurableJoint::SetTargetAngularVelocity (const Vector3f& angular) +{ + m_TargetAngularVelocity = angular; + if (m_Joint) + GET_JOINT()->setDriveAngularVelocity(Vec3ToNx(angular)); +} + +/* +- When the rigid body which is attached with the hinge joint is activated after the joint is loaded + It will not be connected with the joint! +*/ + +template<class TransferFunction> +void ConfigurableJoint::Transfer (TransferFunction& transfer) +{ + JointTransferPre (transfer); + TRANSFER (m_SecondaryAxis); + + TRANSFER(m_XMotion); + TRANSFER(m_YMotion); + TRANSFER(m_ZMotion); + TRANSFER(m_AngularXMotion); + TRANSFER(m_AngularYMotion); + TRANSFER(m_AngularZMotion); + + TRANSFER(m_LinearLimit); + TRANSFER(m_LowAngularXLimit); + TRANSFER(m_HighAngularXLimit); + TRANSFER(m_AngularYLimit); + TRANSFER(m_AngularZLimit); + + TRANSFER(m_TargetPosition); + TRANSFER(m_TargetVelocity); + + TRANSFER(m_XDrive); + TRANSFER(m_YDrive); + TRANSFER(m_ZDrive); + + TRANSFER(m_TargetRotation); + TRANSFER(m_TargetAngularVelocity); + + TRANSFER(m_RotationDriveMode); + + TRANSFER(m_AngularXDrive); + TRANSFER(m_AngularYZDrive); + TRANSFER(m_SlerpDrive); + + TRANSFER(m_ProjectionMode); + TRANSFER(m_ProjectionDistance); + TRANSFER(m_ProjectionAngle); + + TRANSFER(m_ConfiguredInWorldSpace); + TRANSFER(m_SwapBodies); + transfer.Align(); + + JointTransferPost (transfer); +} + +void ConfigurableJoint::ApplyKeepConfigurationSpace() +{ + SetDirty(); + if (m_Joint) + { + // Joint has NX_JS_UNBOUND state, when it's created, but physics aren't simulated yet. + // For ex., if you create ConfigurableJoint from a script, right after creation it will have such state. + if (m_Joint->getState () == NX_JS_SIMULATING || m_Joint->getState () == NX_JS_UNBOUND) + { + NxD6JointDesc desc; + GET_JOINT()->saveToDesc (desc); + SetupD6Desc(desc); + GET_JOINT()->loadFromDesc (desc); + } + } +} + +void ConfigurableJoint::ApplyRebuildConfigurationSpace() +{ + SetDirty(); + if (m_Joint) + { + GetDynamicsScene ().releaseJoint (*m_Joint); + m_Joint = NULL; + } + Create(); +} + + +void ConfigurableJoint::SetupD6Desc (NxD6JointDesc& desc) +{ + desc.xMotion = (NxD6JointMotion)m_XMotion; + desc.yMotion = (NxD6JointMotion)m_YMotion; + desc.zMotion = (NxD6JointMotion)m_ZMotion; + + desc.swing1Motion = (NxD6JointMotion)m_AngularYMotion; + desc.swing2Motion = (NxD6JointMotion)m_AngularZMotion; + desc.twistMotion = (NxD6JointMotion)m_AngularXMotion; + + ConvertSoftLimitLinear(m_LinearLimit, desc.linearLimit); + ConvertSoftLimit(m_AngularYLimit, desc.swing1Limit); + ConvertSoftLimit(m_AngularZLimit, desc.swing2Limit); + ConvertSoftLimit(m_LowAngularXLimit, desc.twistLimit.low); + ConvertSoftLimit(m_HighAngularXLimit, desc.twistLimit.high); + + if (desc.twistLimit.low.value > desc.twistLimit.high.value) + swap (desc.twistLimit.low, desc.twistLimit.high); + + ConvertDrive(m_XDrive, desc.xDrive); + ConvertDrive(m_YDrive, desc.yDrive); + ConvertDrive(m_ZDrive, desc.zDrive); + + ConvertDrive(m_AngularXDrive, desc.twistDrive); + ConvertDrive(m_AngularYZDrive, desc.swingDrive); + ConvertDrive(m_SlerpDrive, desc.slerpDrive); + + desc.projectionMode = (NxJointProjectionMode)m_ProjectionMode; + desc.projectionDistance = m_ProjectionDistance; + desc.projectionAngle = Deg2Rad(m_ProjectionAngle); +// desc.gearRatio = m_GearRatio; + + desc.flags = 0; + if (m_RotationDriveMode) + desc.flags |= NX_D6JOINT_SLERP_DRIVE; + +// if (m_UseGear) +// desc.flags |= NX_D6JOINT_GEAR_ENABLED; + + desc.driveAngularVelocity = Vec3ToNx(m_TargetAngularVelocity); + desc.driveOrientation = (NxQuat&)m_TargetRotation; + desc.driveLinearVelocity = Vec3ToNx(m_TargetVelocity); + desc.drivePosition = Vec3ToNx(m_TargetPosition); +} + +void ConfigurableJoint::Create () +{ + AssertIf (!IsActive ()); + + NxD6JointDesc desc; + + if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING) + GET_JOINT()->saveToDesc (desc); + + SetupD6Desc (desc); + FinalizeCreateD6 (desc); +} + +void ConfigurableJoint::FinalizeCreateD6 (NxD6JointDesc& desc) +{ + bool swapBodies = m_SwapBodies || (m_ConfiguredInWorldSpace && !IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_4_a1)); + FinalizeCreateImpl (desc, swapBodies); + if (swapBodies) + { + swap(desc.localNormal[0], desc.localNormal[1]); + swap(desc.localAxis[0], desc.localAxis[1]); + swap(desc.localAnchor[0], desc.localAnchor[1]); + } + + if (GET_JOINT ()) + { + GET_JOINT ()->loadFromDesc (desc); + + // When actors change, we can't use loadFromDesc on the same joint. + // Thus recreate the joint + if (m_Joint && m_Joint->getState () == NX_JS_BROKEN) + { + GetDynamicsScene ().releaseJoint (*m_Joint); + m_Joint = GetDynamicsScene ().createJoint (desc); + } + } + else + m_Joint = GetDynamicsScene ().createJoint (desc); +} + + +void ConfigurableJoint::SetSecondaryAxis (const Vector3f& axis) +{ + SetDirty (); + m_SecondaryAxis = axis; + if (IsActive () && GET_JOINT()) + { + NxD6JointDesc desc; + AssertIf (m_Joint->getState () == NX_JS_BROKEN); + GET_JOINT()->saveToDesc (desc); + + SetupAxes (desc, kChangeAxis); + GET_JOINT()->loadFromDesc (desc); + AssertIf (m_Joint->getState () == NX_JS_BROKEN); + } +} + +IMPLEMENT_AXIS_ANCHOR(ConfigurableJoint,NxD6JointDesc) + +} + +IMPLEMENT_CLASS_HAS_INIT (ConfigurableJoint) +IMPLEMENT_OBJECT_SERIALIZE (ConfigurableJoint) +#endif //ENABLE_PHSYICS diff --git a/Runtime/Dynamics/ConfigurableJoint.h b/Runtime/Dynamics/ConfigurableJoint.h new file mode 100644 index 0000000..9843f4d --- /dev/null +++ b/Runtime/Dynamics/ConfigurableJoint.h @@ -0,0 +1,180 @@ +#ifndef CONFIGURABLEJOINT_H +#define CONFIGURABLEJOINT_H + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Quaternion.h" +#include "JointDescriptions.h" +#include "Joint.h" +class Rigidbody; +class NxJointDesc; +class NxD6JointDesc; +class NxRevoluteJoint; + +namespace Unity +{ + +class ConfigurableJoint : public Joint +{ + public: + + REGISTER_DERIVED_CLASS (ConfigurableJoint, Joint) + DECLARE_OBJECT_SERIALIZE (ConfigurableJoint) + + ConfigurableJoint (MemLabelId label, ObjectCreationMode mode); + + virtual void Reset(); + + int GetXMotion() { return m_XMotion; } + void SetXMotion (int motion); + + int GetYMotion() { return m_YMotion; } + void SetYMotion (int motion); + + int GetZMotion() { return m_ZMotion; } + void SetZMotion (int motion); + + int GetAngularXMotion() { return m_AngularXMotion; } + void SetAngularXMotion (int motion); + + int GetAngularYMotion() { return m_AngularYMotion; } + void SetAngularYMotion (int motion); + + int GetAngularZMotion() { return m_AngularZMotion; } + void SetAngularZMotion (int motion); + + SoftJointLimit GetLinearLimit () { return m_LinearLimit; } + void SetLinearLimit (const SoftJointLimit& limit); + + + SoftJointLimit GetLowAngularXLimit () { return m_LowAngularXLimit; } + void SetLowAngularXLimit (const SoftJointLimit& limit); + + SoftJointLimit GetHighAngularXLimit () { return m_HighAngularXLimit; } + void SetHighAngularXLimit (const SoftJointLimit& limit); + + SoftJointLimit GetAngularYLimit () { return m_AngularYLimit; } + void SetAngularYLimit (const SoftJointLimit& limit); + + SoftJointLimit GetAngularZLimit () { return m_AngularZLimit; } + void SetAngularZLimit (const SoftJointLimit& limit); + + JointDrive GetXDrive () { return m_XDrive; } + void SetXDrive (const JointDrive& drive); + + JointDrive GetYDrive () { return m_YDrive; } + void SetYDrive (const JointDrive& drive); + + JointDrive GetZDrive () { return m_ZDrive; } + void SetZDrive (const JointDrive& drive); + + JointDrive GetAngularXDrive () { return m_AngularXDrive; } + void SetAngularXDrive (const JointDrive& drive); + + JointDrive GetAngularYZDrive () { return m_AngularYZDrive; } + void SetAngularYZDrive (const JointDrive& drive); + + JointDrive GetSlerpDrive () { return m_SlerpDrive; } + void SetSlerpDrive (const JointDrive& drive); + + int GetRotationDriveMode () { return m_RotationDriveMode; } + void SetRotationDriveMode (int drive); + +// void SetUseGear (bool gear); +// bool GetUseGear () { return m_UseGear; } + + int GetProjectionMode () { return m_ProjectionMode; } + void SetProjectionMode (int mode); + + float GetProjectionDistance () { return m_ProjectionDistance; } + void SetProjectionDistance (float dist); + + float GetProjectionAngle () { return m_ProjectionAngle; } + void SetProjectionAngle (float dist); + +// float GetGearRatio () { return m_GearRatio; } +// void SetGearRatio (float ratio); + + Vector3f GetTargetPosition () { return m_TargetPosition; } + void SetTargetPosition (const Vector3f& pos); + + Quaternionf GetTargetRotation () { return m_TargetRotation; } + void SetTargetRotation (const Quaternionf& rotation); + + Vector3f GetTargetVelocity () { return m_TargetVelocity; } + void SetTargetVelocity (const Vector3f& vel); + + Vector3f GetTargetAngularVelocity () { return m_TargetAngularVelocity; } + void SetTargetAngularVelocity (const Vector3f& angular); + + void SetConfiguredInWorldSpace (bool c); + bool GetConfiguredInWorldSpace () { return m_ConfiguredInWorldSpace; } + + void SetSwapBodies (bool c); + bool GetSwapBodies () { return m_SwapBodies; } + + ////// THIS IS NOT GOOD!!!!!!!!!!! + void CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const; + + void CheckConsistency (); + + virtual void ApplySetupAxesToDesc (int option); + + virtual void SetSecondaryAxis (const Vector3f& axis); + Vector3f GetSecondaryAxis () { return m_SecondaryAxis; } + + static void InitializeClass (); + static void CleanupClass() { } + + private: + void SetupD6Desc (NxD6JointDesc& desc); + void FinalizeCreateD6 (NxD6JointDesc& desc); + void ApplyKeepConfigurationSpace(); + void ApplyRebuildConfigurationSpace(); + + virtual void Create (); + void SetupDriveType (); + + int m_XMotion; ///< enum { Locked=0, Limited=1, Free=2 } + int m_YMotion; ///< enum { Locked=0, Limited=1, Free=2 } + int m_ZMotion; ///< enum { Locked=0, Limited=1, Free=2 } + + int m_AngularXMotion; ///< enum { Locked=0, Limited=1, Free=2 } + int m_AngularYMotion; ///< enum { Locked=0, Limited=1, Free=2 } + int m_AngularZMotion; ///< enum { Locked=0, Limited=1, Free=2 } + + SoftJointLimit m_LinearLimit; + SoftJointLimit m_LowAngularXLimit; + SoftJointLimit m_HighAngularXLimit; + SoftJointLimit m_AngularYLimit; + SoftJointLimit m_AngularZLimit; + + JointDrive m_XDrive; + JointDrive m_YDrive; + JointDrive m_ZDrive; + + JointDrive m_AngularYZDrive; + JointDrive m_AngularXDrive; + JointDrive m_SlerpDrive; + + int m_ProjectionMode;///< enum { None, Position and Rotation = 1, Position Only = 2 } + + float m_ProjectionDistance; + float m_ProjectionAngle; + + int m_RotationDriveMode; ///< enum { X & YZ = 0, Slerp = 1 } + bool m_ConfiguredInWorldSpace; + bool m_SwapBodies; + + Vector3f m_TargetPosition; + Quaternionf m_TargetRotation; + + Vector3f m_TargetVelocity; + Vector3f m_TargetAngularVelocity; + + ////// THIS IS NOT GOOD!!!!!!!!!!! + Vector3f m_SecondaryAxis; +}; + +} +#endif diff --git a/Runtime/Dynamics/ConstantForce.cpp b/Runtime/Dynamics/ConstantForce.cpp new file mode 100644 index 0000000..596b6b7 --- /dev/null +++ b/Runtime/Dynamics/ConstantForce.cpp @@ -0,0 +1,66 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "ConstantForce.h" +#include "RigidBody.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" + +using namespace Unity; + +IMPLEMENT_OBJECT_SERIALIZE (ConstantForce) +IMPLEMENT_CLASS (ConstantForce) + +ConstantForce::ConstantForce (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +, m_FixedUpdateNode (this) +{ +} + +ConstantForce::~ConstantForce () +{ +} + +template<class TransferFunction> +void ConstantForce::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + TRANSFER (m_Force); + TRANSFER (m_RelativeForce); + TRANSFER (m_Torque); + TRANSFER (m_RelativeTorque); +} + +void ConstantForce::Reset () +{ + Super::Reset (); + m_Force = Vector3f::zero; + m_RelativeForce = Vector3f::zero; + m_Torque = Vector3f::zero; + m_RelativeTorque = Vector3f::zero; +} + +void ConstantForce::FixedUpdate() { + Rigidbody* body = QueryComponent (Rigidbody); + if (!body) + { + // It should be impossible to reach this case, since Unity will not allow + // deleting components which are required by other components. But people + // have somehow managed (case 506613), so better check for it. + ErrorStringObject ("ConstantForce requires a Rigidbody component, but non is present.", this); + return; + } + body->AddForce (m_Force); + body->AddRelativeForce (m_RelativeForce); + body->AddTorque (m_Torque); + body->AddRelativeTorque (m_RelativeTorque); +} + +void ConstantForce::AddToManager () +{ + GetFixedBehaviourManager ().AddBehaviour (m_FixedUpdateNode, -1); +} + +void ConstantForce::RemoveFromManager () +{ + GetFixedBehaviourManager ().RemoveBehaviour (m_FixedUpdateNode); +} +#endif //ENABLE_PHSYICS
\ No newline at end of file diff --git a/Runtime/Dynamics/ConstantForce.h b/Runtime/Dynamics/ConstantForce.h new file mode 100644 index 0000000..8bcd90b --- /dev/null +++ b/Runtime/Dynamics/ConstantForce.h @@ -0,0 +1,30 @@ +#ifndef CONSTANTFORCE_H +#define CONSTANTFORCE_H + +#include "Runtime/GameCode/Behaviour.h" +#include "Runtime/Math/Vector3.h" + +class ConstantForce : public Behaviour +{ + public: + REGISTER_DERIVED_CLASS (ConstantForce, Behaviour) + DECLARE_OBJECT_SERIALIZE (ConstantForce) + + ConstantForce (MemLabelId label, ObjectCreationMode mode); + virtual void Reset (); + + virtual void FixedUpdate (); + virtual void AddToManager (); + virtual void RemoveFromManager (); + + Vector3f m_Force; ///< Force applied globally + Vector3f m_RelativeForce; ///< Force applied locally + Vector3f m_Torque; ///< Torque applied globally + Vector3f m_RelativeTorque; ///< Torque applied locally + + + BehaviourListNode m_FixedUpdateNode; +}; + + +#endif diff --git a/Runtime/Dynamics/DeformableMesh.cpp b/Runtime/Dynamics/DeformableMesh.cpp new file mode 100644 index 0000000..6fd3de0 --- /dev/null +++ b/Runtime/Dynamics/DeformableMesh.cpp @@ -0,0 +1,721 @@ +#include "UnityPrefix.h" +#include "DeformableMesh.h" + +#if ENABLE_CLOTH + +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "PhysicsManager.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h" +#include "nxmemorystream.h" +#include "Runtime/Math/Random/Random.h" +#include "Runtime/Dynamics/ExtractDataFromMesh.h" +#include "Runtime/Core/Callbacks/GlobalCallbacks.h" + +Rand gClothRand (1); + +namespace Unity { + +Cloth::Cloth (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) + , m_FixedUpdateNode(this) + , m_Indices(kMemVertexData) + , m_Vertices(kMemVertexData) + , m_Normals(kMemVertexData) + , m_Tangents(kMemVertexData) + , m_UVs(kMemVertexData) + , m_UV1s(kMemVertexData) + , m_Colors(kMemVertexData) + , m_TranslatedVertices(kMemVertexData) + , m_TranslatedNormals(kMemVertexData) + , m_TranslatedIndices(kMemVertexData) +{ + // configuration + m_BendingStiffness = 0.0f; + m_StretchingStiffness = 1.0f; + m_Damping = 0.0f; + m_Thickness = 0.2f; + m_UseGravity = true; + m_SelfCollision = false; + m_ExternalAcceleration = Vector3f::zero; + m_RandomAcceleration = Vector3f::zero; + + // state + m_Cloth = NULL; + m_ClothScene = NULL; + m_NumVertices = 0; + m_NumIndices = 0; + m_NumParentIndices = 0; + m_NumVerticesFromPhysX = 0; + m_NumIndicesFromPhysX = 0; + m_SuspendCount = 0; + m_IsSuspended = false; + m_NeedToWakeUp = false; + m_VerticesForRendering = &m_Vertices; + m_NormalsForRendering = &m_Normals; + m_IndicesForRendering = &m_Indices; + m_NumVerticesForRendering = 0; + m_NumIndicesForRendering = 0; +} + +Cloth::~Cloth () +{ + Cleanup(); +} + +void Cloth::Reset () +{ + Super::Reset(); + + // configuration + m_BendingStiffness = 0.0f; + m_StretchingStiffness = 1.0f; + m_Damping = 0.0f; + m_Thickness = 0.2f; + m_UseGravity = true; + m_SelfCollision = false; + m_ExternalAcceleration = Vector3f::zero; + m_RandomAcceleration = Vector3f::zero; +} + +void Cloth::Cleanup () +{ + if (m_Cloth) + { + NxClothMesh* mesh = m_Cloth->getClothMesh(); + m_ClothScene->releaseCloth(*m_Cloth); + GetDynamicsSDK().releaseClothMesh(*mesh); + m_Cloth = NULL; + } + m_NumVertices = 0; + m_NumVerticesFromPhysX = 0; + m_NumVerticesForRendering = 0; + m_NumIndices = 0; + m_NumIndicesFromPhysX = 0; + m_NumIndicesForRendering = 0; +} + +void Cloth::ProcessMeshForRenderer () +{ + if (m_NumVerticesFromPhysX > m_NumVertices) + { + int newVertices = m_NumVerticesFromPhysX - m_NumVertices; + int size = m_NumVertices; + if (m_VertexTranslationTable.size()) + { + size = m_VertexTranslationTable.size(); + m_VertexTranslationTable.resize_uninitialized(size + newVertices); + for (int i=0; i<newVertices; i++) + m_VertexTranslationTable[size + i] = m_NumVertices + i; + } + + if (!m_UVs.empty()) + { + for (int i=0; i<newVertices; i++) + m_UVs[size+i] = m_UVs[m_ParentIndices[m_NumVertices+i]]; + } + + if (!m_UV1s.empty()) + { + for (int i=0; i<newVertices; i++) + m_UV1s[size+i] = m_UV1s[m_ParentIndices[m_NumVertices+i]]; + } + + if (!m_Colors.empty()) + { + for (int i=0; i<newVertices; i++) + m_Colors[size+i] = m_Colors[m_ParentIndices[m_NumVertices+i]]; + } + + if (!m_Tangents.empty()) + { + for (int i=0; i<newVertices; i++) + m_Tangents[size+i] = m_Tangents[m_ParentIndices[m_NumVertices+i]]; + } + + m_NumVertices = m_NumVerticesFromPhysX; + m_NumVerticesForRendering += newVertices; + } + if (m_VertexTranslationTable.size()) + { + for (int i=0; i<m_VertexTranslationTable.size(); i++) + { + int index = m_VertexTranslationTable[i]; + m_TranslatedVertices[i] = m_Vertices[index]; + m_TranslatedNormals[i] = m_Normals[index]; + } + if (m_NumIndicesFromPhysX > m_NumIndices) + { + int size = m_NumIndicesForRendering; + int newIndices = m_NumIndicesFromPhysX > m_NumIndices; + for (int i=0; i<newIndices; i++) + { + int newIndex = m_Indices[m_NumIndices + i]; + if (newIndex < m_NumVertices) + m_TranslatedIndices[size + i] = newIndex; + else + m_TranslatedIndices[size + i] = m_VertexTranslationTable[newIndex]; + } + m_NumIndicesForRendering += newIndices; + m_NumIndices = m_NumIndicesFromPhysX; + } + } + else if (m_NumIndicesFromPhysX > m_NumIndices) + { + m_NumIndices = m_NumIndicesFromPhysX; + m_NumIndicesForRendering = m_NumIndicesFromPhysX; + } +} + +bool Cloth::SetupMeshData (bool transformToWorldSpace, bool externalVertexTranlation, int tearMemoryFactor) +{ + if (!ExtractDataFromMesh(*m_Mesh, m_Vertices, m_Indices, m_VertexTranslationTable)) + return false; + + m_NumVertices = m_Vertices.size(); + m_NumIndices = m_Indices.size(); + + bool vertexTranlationIsNeeded = externalVertexTranlation; + + StrideIterator<Vector2f> uvs = m_Mesh->GetUvBegin(0); + StrideIterator<Vector2f> uv1s = m_Mesh->GetUvBegin(1); + StrideIterator<ColorRGBA32> colors = m_Mesh->GetColorBegin(); + if (!vertexTranlationIsNeeded) + { + // find out if vertex mapping really is needed. If there are UV or color seams, it is. + // If there are just some duplicate vertices because the mesh is stripified, then we just use the physX indices instead. + for (int i=m_NumVertices; i<m_VertexTranslationTable.size();i++) + { + if (!uvs.IsNull () && uvs[m_VertexTranslationTable[i]] != uvs[i]) + { + vertexTranlationIsNeeded = true; + break; + } + if (!uv1s.IsNull () && uv1s[m_VertexTranslationTable[i]] != uv1s[i]) + { + vertexTranlationIsNeeded = true; + break; + } + if (!colors.IsNull () && colors[m_VertexTranslationTable[i]] != colors[i]) + { + vertexTranlationIsNeeded = true; + break; + } + // we don't check for normals and tangents. + // normals are regenerated by the cloth code anyways, so split normals are no use here. + } + } + + if (!vertexTranlationIsNeeded) + m_VertexTranslationTable.clear(); + + UInt32 maxVertices = std::max (std::max (m_NumVertices, m_NumVerticesFromPhysX), m_NumVertices * tearMemoryFactor); + UInt32 maxIndices = std::max (m_NumIndices, m_NumIndices * tearMemoryFactor); + + m_Vertices.resize_uninitialized(maxVertices); + m_Normals.resize_uninitialized(maxVertices); + m_Indices.resize_initialized(maxIndices, 0); + if (tearMemoryFactor>1) + m_ParentIndices.resize_initialized(maxVertices, 0); + else + m_ParentIndices.clear(); + + int maxVerticesForRendering; + if (vertexTranlationIsNeeded && !externalVertexTranlation) + { + Mesh::TemporaryIndexContainer triangles; + m_Mesh->GetTriangles(triangles); + m_NumIndicesForRendering = triangles.size(); + m_TranslatedIndices.resize_uninitialized(m_NumIndicesForRendering + m_NumIndices * (tearMemoryFactor-1)); + for (int i=0; i<m_NumIndicesForRendering; i++) + m_TranslatedIndices[i] = triangles[i]; + maxVerticesForRendering = m_Mesh->GetVertexCount() + m_NumVertices * (tearMemoryFactor-1); + m_TranslatedVertices.resize_uninitialized(maxVerticesForRendering); + m_TranslatedNormals.resize_uninitialized(maxVerticesForRendering); + m_NumVerticesForRendering = m_Mesh->GetVertexCount(); + m_VerticesForRendering = &m_TranslatedVertices; + m_NormalsForRendering = &m_TranslatedNormals; + m_IndicesForRendering = &m_TranslatedIndices; + } + else + { + maxVerticesForRendering = maxVertices; + m_TranslatedIndices.clear(); + m_TranslatedVertices.clear(); + m_TranslatedNormals.clear(); + m_VerticesForRendering = &m_Vertices; + m_NormalsForRendering = &m_Normals; + m_IndicesForRendering = &m_Indices; + m_NumVerticesForRendering = m_NumVertices; + m_NumIndicesForRendering = m_NumIndices; + } + + if (!m_Mesh->IsAvailable (kShaderChannelNormal)) + { + WarningString("Cloth simulation requires normals!"); + std::fill (m_Normals.begin (), m_Normals.end (), Vector3f(0,0,0)); + } + else if (vertexTranlationIsNeeded) + { + StrideIterator<Vector3f> n = m_Mesh->GetNormalBegin (); + for (int i=0;i<m_VertexTranslationTable.size();++i, ++n) + { + int index = m_VertexTranslationTable[i]; + m_Normals[index] = *n; + } + } + else + { + strided_copy (m_Mesh->GetNormalBegin (), m_Mesh->GetNormalBegin () + m_NumVertices, m_Normals.begin ()); + } + + if (!externalVertexTranlation) + { + if (m_Mesh->IsAvailable (kShaderChannelTexCoord0)) + { + m_UVs.resize_uninitialized(maxVerticesForRendering); + strided_copy (m_Mesh->GetUvBegin (0), m_Mesh->GetUvBegin (0) + m_NumVerticesForRendering, m_UVs.begin ()); + } + else + m_UVs.clear(); + + if (m_Mesh->IsAvailable (kShaderChannelTexCoord1)) + { + m_UV1s.resize_uninitialized(maxVerticesForRendering); + strided_copy (m_Mesh->GetUvBegin (1), m_Mesh->GetUvBegin (1) + m_NumVerticesForRendering, m_UV1s.begin ()); + } + else + m_UV1s.clear(); + + if (m_Mesh->IsAvailable(kShaderChannelColor)) + { + m_Colors.resize_uninitialized(maxVerticesForRendering); + strided_copy (m_Mesh->GetColorBegin (), m_Mesh->GetColorBegin () + m_NumVerticesForRendering, m_Colors.begin ()); + } + else + m_Colors.clear(); + + if (m_Mesh->IsAvailable(kShaderChannelTangent)) + { + m_Tangents.resize_uninitialized(maxVerticesForRendering); + strided_copy (m_Mesh->GetTangentBegin (), m_Mesh->GetTangentBegin () + m_NumVerticesForRendering, m_Tangents.begin ()); + } + else + m_Tangents.clear(); + } + + if (transformToWorldSpace) + { + Transform& transform = GetComponent (Transform); + TransformPoints3x4(transform.GetLocalToWorldMatrix(), &m_Vertices[0], &m_Vertices[0], m_NumVertices); + if (m_Mesh->IsAvailable (kShaderChannelNormal)) + TransformPoints3x3(transform.GetLocalToWorldMatrixNoScale(), &m_Normals[0], &m_Normals[0], m_NumVertices); + if (m_Mesh->IsAvailable (kShaderChannelTangent)) + { + Matrix4x4f m = transform.GetLocalToWorldMatrixNoScale(); + for ( int i=0; i<m_NumVertices; i++) + { + Vector3f tangent = Vector3f(m_Tangents[i].x, m_Tangents[i].y, m_Tangents[i].z); + Vector3f normalized = NormalizeSafe (m.MultiplyVector3 (tangent)); + m_Tangents[i] = Vector4f(normalized.x, normalized.y ,normalized.z, m_Tangents[i].w); + } + } + + m_NumVerticesFromPhysX = m_NumVertices; + } + + return true; +} + +void Cloth::SetupMeshBuffers (NxMeshData &meshData) +{ + meshData.verticesPosBegin = m_Vertices.begin(); + meshData.verticesNormalBegin = m_Normals.empty() ? NULL : m_Normals.begin(); + meshData.verticesPosByteStride = sizeof (Vector3f); + meshData.verticesNormalByteStride = sizeof (Vector3f); + meshData.maxVertices = m_Vertices.size(); + meshData.numVerticesPtr = (NxU32*)&m_NumVerticesFromPhysX; + + meshData.indicesBegin = &m_Indices[0]; + meshData.indicesByteStride = sizeof (UInt16); + meshData.maxIndices = m_Indices.size(); + meshData.numIndicesPtr = (NxU32*)&m_NumIndicesFromPhysX; + meshData.flags = NX_MDF_16_BIT_INDICES; + + if (m_ParentIndices.size()) + { + meshData.parentIndicesBegin = &m_ParentIndices[0]; + meshData.parentIndicesByteStride = sizeof (UInt16); + meshData.maxParentIndices = m_ParentIndices.size(); + meshData.numParentIndicesPtr = (NxU32*)&m_NumParentIndices; + } +} + +NxClothMesh *Cloth::CookClothMesh (bool tearable) +{ + NxClothMeshDesc clothMeshDesc; + + clothMeshDesc.numVertices = m_NumVertices; + clothMeshDesc.numTriangles = m_NumIndices / 3; + clothMeshDesc.pointStrideBytes = sizeof (Vector3f); + clothMeshDesc.triangleStrideBytes = sizeof (m_Indices[0]) * 3; + clothMeshDesc.vertexMassStrideBytes = 0; + clothMeshDesc.vertexFlagStrideBytes = 0; + clothMeshDesc.points = &m_Vertices[0]; + clothMeshDesc.triangles = &m_Indices[0]; + clothMeshDesc.vertexMasses = NULL; + clothMeshDesc.vertexFlags = NULL; + clothMeshDesc.flags = NX_MF_16_BIT_INDICES; + + if (tearable) + clothMeshDesc.flags |= NX_CLOTH_MESH_TEARABLE; + + MemoryStream memoryStream(NULL, 0); + if (!NxCookClothMesh(clothMeshDesc, memoryStream)) + { + ErrorString ("Failed cooking cloth"); + return NULL; + } + + return GetDynamicsSDK ().createClothMesh(memoryStream); +} + +void Cloth::SetupClothDesc (NxClothDesc &clothDesc, bool tearable) +{ + SetupMeshBuffers (clothDesc.meshData); + clothDesc.collisionGroup = GetGameObject ().GetLayer (); + clothDesc.clothMesh = CookClothMesh(tearable); + clothDesc.bendingStiffness = m_BendingStiffness; + clothDesc.stretchingStiffness = m_StretchingStiffness; + clothDesc.dampingCoefficient = m_Damping; + clothDesc.thickness = m_Thickness; + clothDesc.selfCollisionThickness = m_Thickness; + clothDesc.userData = this; + if (m_BendingStiffness > 0) + clothDesc.flags |= NX_CLF_BENDING; + else + clothDesc.flags &= ~NX_CLF_BENDING; + if (m_Damping > 0) + clothDesc.flags |= NX_CLF_DAMPING; + else + clothDesc.flags &= ~NX_CLF_DAMPING; + if (m_UseGravity) + clothDesc.flags |= NX_CLF_GRAVITY; + else + clothDesc.flags &= ~NX_CLF_GRAVITY; + if (m_SelfCollision) + clothDesc.flags |= NX_CLF_SELFCOLLISION; + else + clothDesc.flags &= ~NX_CLF_SELFCOLLISION; +} + +void Cloth::Deactivate (DeactivateOperation operation) +{ + Super::Deactivate (operation); + Cleanup (); +} + +void Cloth::FixedUpdate() +{ + if (m_Cloth) + { + Vector3f accel = m_ExternalAcceleration+RandomPointInsideCube (gClothRand, m_RandomAcceleration); + m_Cloth->setExternalAcceleration((const NxVec3&)accel); + if (m_NeedToWakeUp && !m_IsSuspended) + m_Cloth->wakeUp(); + } + m_NeedToWakeUp = false; +} + +void Cloth::AddToManager () +{ + GetFixedBehaviourManager().AddBehaviour (m_FixedUpdateNode, -1); +} + +void Cloth::RemoveFromManager () +{ + GetFixedBehaviourManager().RemoveBehaviour (m_FixedUpdateNode); +} + +void Cloth::PauseSimulation () +{ + if (!m_Cloth->isSleeping()) + { + m_IsSuspended = true; + m_Cloth->putToSleep(); + } +} + +void Cloth::ResumeSimulation () +{ + if (m_IsSuspended) + { + // only wake up the cloth when it becomes visible when it has been set to sleep by the renderer, + // not when it has been set to sleep by PhysX. + m_IsSuspended = false; + m_Cloth->wakeUp(); + } +} + +void Cloth::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if (m_Cloth) + { + // Apply changed values + SetThickness (m_Thickness); + SetBendingStiffness (m_BendingStiffness); + SetStretchingStiffness (m_StretchingStiffness); + SetDamping (m_Damping); + SetExternalAcceleration (m_ExternalAcceleration); + SetRandomAcceleration (m_RandomAcceleration); + SetUseGravity (m_UseGravity); + SetSelfCollision (m_SelfCollision); + } + + Super::AwakeFromLoad (awakeMode); + if (IsActive ()) + { + if (!m_Cloth) + Create (); + else + m_NeedToWakeUp = true; + } + else + Cleanup (); + + m_SuspendCount = 0; + + if(!GetEnabled()) + SetSuspended(true); +} + +void Cloth::SetSuspended (bool suspended) +{ + if (suspended) + m_SuspendCount++; + else + m_SuspendCount--; + + if (m_Cloth) + { + if (m_SuspendCount) + PauseSimulation(); + else + ResumeSimulation(); + } +} + +void Cloth::SetEnabled (bool enabled) +{ + if (enabled != GetEnabled()) + { + Super::SetEnabled(enabled); + SetSuspended(!enabled); + } +} + +#define ENFORCE_MINEQ(x) {if (value < x) { value = x; ErrorString("value must be greater than or equal to " #x);}} +#define ENFORCE_MIN(x) {if (value <= x) { value = x; ErrorString("value must be greater than " #x);}} +#define ENFORCE_MAXEQ(x) {if (value > x) { value = x; ErrorString("value must be smaller than or equal to " #x);}} +#define ENFORCE_MAX(x) {if (value >= x) { value = x; ErrorString("value must be smaller than " #x);}} + +void Cloth::SetBendingStiffness (float value) +{ + ENFORCE_MINEQ(0); + ENFORCE_MAXEQ(1); + if (value != m_BendingStiffness) + { + m_NeedToWakeUp = true; + m_BendingStiffness = value; + } + if (m_Cloth) + { + if (value > 0) + { + m_Cloth->setBendingStiffness(value); + m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_BENDING); + } + else + m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_BENDING)); + } + SetDirty(); +} + +void Cloth::SetStretchingStiffness (float value) +{ + ENFORCE_MIN(0); + ENFORCE_MAXEQ(1); + if (value != m_StretchingStiffness) + { + m_NeedToWakeUp = true; + m_StretchingStiffness = value; + } + if (m_Cloth) + m_Cloth->setStretchingStiffness(value); + SetDirty(); +} + +void Cloth::SetDamping (float value) +{ + ENFORCE_MINEQ(0); + ENFORCE_MAXEQ(1); + if (value != m_Damping) + { + m_NeedToWakeUp = true; + m_Damping = value; + } + if (m_Cloth) + { + if (value > 0) + { + m_Cloth->setDampingCoefficient(value); + m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_DAMPING); + } + else + m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_DAMPING)); + } + SetDirty(); +} + +void Cloth::SetThickness (float value) +{ + ENFORCE_MIN(0); + + if (value != m_Thickness) + { + m_NeedToWakeUp = true; + m_Thickness = value; + } + if (m_Cloth) + { + m_Cloth->setThickness(value); + m_Cloth->setSelfCollisionThickness(value); + } + SetDirty(); +} + + +void Cloth::SetUseGravity (bool value) +{ + if (value != m_UseGravity) + { + m_NeedToWakeUp = true; + m_UseGravity = value; + } + if (m_Cloth) + { + if (value) + m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_GRAVITY); + else + m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_GRAVITY)); + } + SetDirty(); +} + +void Cloth::SetSelfCollision (bool value) +{ + if (value != m_SelfCollision) + { + m_NeedToWakeUp = true; + m_SelfCollision = value; + } + if (m_Cloth) + { + if (value) + m_Cloth->setFlags(m_Cloth->getFlags() | NX_CLF_SELFCOLLISION); + else + m_Cloth->setFlags(m_Cloth->getFlags() & ~(NX_CLF_SELFCOLLISION)); + } + SetDirty(); +} + +void Cloth::SetExternalAcceleration (const Vector3f &value) +{ + if (value != m_ExternalAcceleration) + { + m_NeedToWakeUp = true; + m_ExternalAcceleration = value; + } + SetDirty(); +} + +void Cloth::SetRandomAcceleration (const Vector3f &value) +{ + if (value != m_RandomAcceleration) + { + m_NeedToWakeUp = true; + m_RandomAcceleration = value; + } + SetDirty(); +} + +template<class TransferFunction> +void Cloth::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + TRANSFER (m_BendingStiffness); + TRANSFER (m_StretchingStiffness); + TRANSFER (m_Damping); + TRANSFER (m_Thickness); + TRANSFER (m_UseGravity); + TRANSFER (m_SelfCollision); + transfer.Align(); + TRANSFER (m_ExternalAcceleration); + TRANSFER (m_RandomAcceleration); +} + +static void ResetRandSeed () +{ + gClothRand.SetSeed (1); +} + +void Cloth::InitializeClass () +{ +#if UNITY_EDITOR + REGISTER_MESSAGE (Cloth, kTransformChanged, TransformChanged, int); + REGISTER_MESSAGE_VOID (Cloth, kBecameVisible, BecameVisible); + REGISTER_MESSAGE_VOID (Cloth, kBecameInvisible, BecameInvisible); +#endif + + GlobalCallbacks::Get().resetRandomAfterLevelLoad.Register(ResetRandSeed); +} + +void Cloth::CleanupClass () +{ + GlobalCallbacks::Get().resetRandomAfterLevelLoad.Unregister(ResetRandSeed); +} + +#if UNITY_EDITOR +// When the mesh is updated by Phyics, it acts independently of it's Transform, +// simulation is in world coordinates. In the editor, we use this code to allow +// placement, though. +void Cloth::TransformChanged( int changeMask ) +{ + if (!IsWorldPlaying() && IsActive()) + { + Create(); + } +} + +void Cloth::BecameVisible() +{ + if (GetSuspended()) + SetSuspended (false); +} + +void Cloth::BecameInvisible() +{ + SetSuspended (true); +} + +#endif +} + +IMPLEMENT_CLASS_HAS_INIT (Cloth) +IMPLEMENT_OBJECT_SERIALIZE (Cloth) +INSTANTIATE_TEMPLATE_TRANSFER (Cloth) + +#endif // ENABLE_CLOTH diff --git a/Runtime/Dynamics/DeformableMesh.h b/Runtime/Dynamics/DeformableMesh.h new file mode 100644 index 0000000..ba5c78e --- /dev/null +++ b/Runtime/Dynamics/DeformableMesh.h @@ -0,0 +1,146 @@ +#ifndef DEFORMABLEMESH_H +#define DEFORMABLEMESH_H + +#include "Configuration/UnityConfigure.h" +#if ENABLE_CLOTH || DOXYGEN + +#include "Runtime/GameCode/Behaviour.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Vector4.h" +#include "Runtime/Math/Color.h" +#include "Runtime/Utilities/dynamic_array.h" + +class Mesh; +class ClothRenderer; +class NxMeshData; +class NxScene; + +class NxClothMesh; +class NxCloth; +class NxClothDesc; + +namespace Unity { + +class Cloth : public Behaviour +{ +public: + REGISTER_DERIVED_ABSTRACT_CLASS (Cloth, Behaviour) + DECLARE_OBJECT_SERIALIZE (Cloth) + + Cloth (MemLabelId label, ObjectCreationMode mode); + + bool GetSuspended() { return m_SuspendCount != 0; } + void SetSuspended(bool suspended); + virtual void SetEnabled (bool enab); + virtual void Reset (); + + + virtual void FixedUpdate (); + virtual void Deactivate (DeactivateOperation operation); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + virtual void AddToManager (); + virtual void RemoveFromManager (); + virtual void ProcessMeshForRenderer (); + + static void InitializeClass(); + static void CleanupClass (); + +#if UNITY_EDITOR + void TransformChanged( int changeMask ); + void BecameVisible(); + void BecameInvisible(); +#endif + + float GetBendingStiffness () const { return m_BendingStiffness; } + void SetBendingStiffness (float value); + + float GetStretchingStiffness () const { return m_StretchingStiffness; } + void SetStretchingStiffness (float value); + + float GetDamping () const { return m_Damping; } + void SetDamping (float value); + + bool GetUseGravity () const { return m_UseGravity; } + void SetUseGravity (bool value); + + Vector3f GetExternalAcceleration () const { return m_ExternalAcceleration; } + void SetExternalAcceleration (const Vector3f &value); + + Vector3f GetRandomAcceleration () const { return m_RandomAcceleration; } + void SetRandomAcceleration (const Vector3f &value); + + bool GetSelfCollision () { return m_SelfCollision; } + void SetSelfCollision (bool value); + + float GetThickness () const { return m_Thickness; } + void SetThickness (float value); + + dynamic_array<Vector3f> &GetVertices () { return m_Vertices; } + dynamic_array<Vector3f> &GetNormals () { return m_Normals; } +protected: + + virtual void PauseSimulation (); + virtual void ResumeSimulation (); + + virtual void Create () = 0; + virtual void Cleanup (); + bool SetupMeshData (bool transformToWorldSpace, bool externalVertexTranlation, int tearMemoryFactor); + void SetupMeshBuffers (NxMeshData &meshData); + + void SetupClothDesc (NxClothDesc &clothDesc, bool tearable); + NxClothMesh *CookClothMesh (bool tearable); + + // configuration + PPtr<Mesh> m_Mesh; + float m_BendingStiffness; ///<Bending stiffness. 0 = disabled. range { 0, 1 } + float m_StretchingStiffness; ///<Stretching stiffness. range { 0.001, 1 } + float m_Damping; ///<Motion damping coefficient. 0 = disabled. range { 0, 1 } + float m_Thickness; ///<Thickness of the Cloth surface. range { 0.001, 10000 } + bool m_UseGravity; ///<Should gravitational acceleration be applied to the cloth? + bool m_SelfCollision; ///<Can the cloth collide with itself? + Vector3f m_ExternalAcceleration; ///<External acceleration applied to the cloth. + Vector3f m_RandomAcceleration; ///<Random acceleration applied to the cloth. + + + // state + NxCloth* m_Cloth; + + NxScene* m_ClothScene; + + int m_SuspendCount; + + bool m_IsSuspended; + bool m_NeedToWakeUp; + + UInt32 m_NumVertices; + UInt32 m_NumVerticesFromPhysX; + UInt32 m_NumVerticesForRendering; + UInt32 m_NumIndices; + UInt32 m_NumIndicesFromPhysX; + UInt32 m_NumIndicesForRendering; + dynamic_array<Vector3f> m_Vertices; + dynamic_array<Vector3f> m_TranslatedVertices; + dynamic_array<Vector3f> *m_VerticesForRendering; + dynamic_array<Vector3f> m_Normals; + dynamic_array<Vector3f> m_TranslatedNormals; + dynamic_array<Vector3f> *m_NormalsForRendering; + dynamic_array<UInt16> m_Indices; + dynamic_array<UInt16> m_TranslatedIndices; + dynamic_array<UInt16> *m_IndicesForRendering; + dynamic_array<Vector4f> m_Tangents; + dynamic_array<Vector2f> m_UVs; + dynamic_array<Vector2f> m_UV1s; + dynamic_array<ColorRGBA32> m_Colors; + UInt32 m_NumParentIndices; + dynamic_array<UInt16> m_ParentIndices; + dynamic_array<UInt16> m_VertexTranslationTable; + + BehaviourListNode m_FixedUpdateNode; + + friend class ::ClothRenderer; +}; +} + +#endif // ENABLE_CLOTH || DOXYGEN +#endif // DEFORMABLEMESH_H diff --git a/Runtime/Dynamics/ExtractDataFromMesh.cpp b/Runtime/Dynamics/ExtractDataFromMesh.cpp new file mode 100644 index 0000000..a2cd9f4 --- /dev/null +++ b/Runtime/Dynamics/ExtractDataFromMesh.cpp @@ -0,0 +1,56 @@ +#include "UnityPrefix.h" +#include "ExtractDataFromMesh.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Misc/MeshWelding.h" +#include "Runtime/Graphics/TriStripper.h" + +///@TODO: Use dynamic_array temp allocator +// Extracts unique welded vertices & triangle array indexing into the welded vertices. +bool ExtractDataFromMesh (Mesh& mesh, dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap) +{ + int vertexCount = mesh.GetVertexCount(); + if (vertexCount == 0) + return false; + + if (!mesh.HasVertexData()) + { + ErrorStringObject("CollisionMeshData couldn't be created because the mesh has been marked as non-accessible", &mesh); + return false; + } + + vertices.resize_uninitialized(vertexCount); + mesh.ExtractVertexArray(&vertices[0]); + + triangles.clear(); + for (unsigned submesh=0; submesh<mesh.GetSubMeshCount(); submesh++) + { + if (submesh >= mesh.GetSubMeshCount()) + { + ErrorString("Failed getting triangles. Submesh index is out of bounds."); + return false; + } + + UInt16* indices = mesh.GetSubMeshBuffer16(submesh); + SubMesh& sm = mesh.GetSubMeshFast(submesh); + if (sm.topology == kPrimitiveTriangleStripDeprecated) + { + const UInt32 startIndex = triangles.size(); + int triCount = CountTrianglesInStrip (indices, sm.indexCount); + triangles.resize_uninitialized((triCount * 3) + startIndex); + Destripify(indices, sm.indexCount, triangles.begin() + startIndex, triCount); + } + else if (sm.topology == kPrimitiveTriangles) + { + triangles.insert(triangles.end(), indices, indices + sm.indexCount); + } + else + { + ErrorString("Failed to extract collision data: non-triangle mesh."); + return false; + } + } + + WeldVertexArray(vertices, triangles, remap); + return true; +}
\ No newline at end of file diff --git a/Runtime/Dynamics/ExtractDataFromMesh.h b/Runtime/Dynamics/ExtractDataFromMesh.h new file mode 100644 index 0000000..7a68823 --- /dev/null +++ b/Runtime/Dynamics/ExtractDataFromMesh.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Runtime/Utilities/dynamic_array.h" +#include "Runtime/Math/Vector3.h" + +class Mesh; + +bool ExtractDataFromMesh (Mesh& mesh, dynamic_array<Vector3f>& vertices, dynamic_array<UInt16>& triangles, dynamic_array<UInt16>& remap); diff --git a/Runtime/Dynamics/FixedJoint.cpp b/Runtime/Dynamics/FixedJoint.cpp new file mode 100644 index 0000000..d712724 --- /dev/null +++ b/Runtime/Dynamics/FixedJoint.cpp @@ -0,0 +1,61 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "FixedJoint.h" +#include "PhysicsManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" + +using namespace std; + +namespace Unity +{ + +#define GET_JOINT() static_cast<NxD6Joint*> (m_Joint) + +/* +- We awake the hingejoint only once. (AwakeFromLoad) + At this point we setup the axes. They are never changed afterwards + -> The perfect solution remembers the old position/rotation of the rigid bodies. + Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state! +*/ + +FixedJoint::FixedJoint (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +FixedJoint::~FixedJoint () +{ +} + +void FixedJoint::Create () +{ + AssertIf (!IsActive ()); + + NxD6JointDesc desc; + desc.zMotion = desc.yMotion = desc.xMotion = NX_D6JOINT_MOTION_LOCKED; + desc.swing1Motion = desc.swing2Motion = desc.twistMotion = NX_D6JOINT_MOTION_LOCKED; + + if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING) + GET_JOINT()->saveToDesc (desc); + + FINALIZE_CREATE (desc, NxFixedJointDesc); +} + +IMPLEMENT_AXIS_ANCHOR(FixedJoint,NxD6JointDesc) + +template<class TransferFunction> +void FixedJoint::Transfer (TransferFunction& transfer) +{ + Super::Super::Transfer (transfer); + TRANSFER_SIMPLE (m_ConnectedBody); + JointTransferPost (transfer); +} + +} + +IMPLEMENT_CLASS (FixedJoint) +IMPLEMENT_OBJECT_SERIALIZE (FixedJoint) + +#undef GET_JOINT +#endif //ENABLE_PHYSICS
\ No newline at end of file diff --git a/Runtime/Dynamics/FixedJoint.h b/Runtime/Dynamics/FixedJoint.h new file mode 100644 index 0000000..8bb9567 --- /dev/null +++ b/Runtime/Dynamics/FixedJoint.h @@ -0,0 +1,32 @@ +#ifndef FIXEDJOINT_H +#define FIXEDJOINT_H + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Math/Vector3.h" +#include "JointDescriptions.h" +#include "Joint.h" +class Rigidbody; +class NxJointDesc; +class NxRevoluteJoint; + +namespace Unity +{ + +class FixedJoint : public Joint +{ + public: + + REGISTER_DERIVED_CLASS (FixedJoint, Joint) + DECLARE_OBJECT_SERIALIZE (FixedJoint) + + FixedJoint (MemLabelId label, ObjectCreationMode mode); + + private: + + virtual void ApplySetupAxesToDesc (int option); + virtual void Create (); +}; + +} + +#endif diff --git a/Runtime/Dynamics/HingeJoint.cpp b/Runtime/Dynamics/HingeJoint.cpp new file mode 100644 index 0000000..9494960 --- /dev/null +++ b/Runtime/Dynamics/HingeJoint.cpp @@ -0,0 +1,280 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "Runtime/Utilities/StaticAssert.h" +#include "HingeJoint.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "PhysicsManager.h" + +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" + +using namespace std; + +namespace Unity +{ + +#define GET_JOINT() static_cast<NxRevoluteJoint*> (m_Joint) + +/* +- We awake the hingejoint only once. (AwakeFromLoad) + At this point we setup the axes. They are never changed afterwards + -> The perfect solution remembers the old position/rotation of the rigid bodies. + Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state! +*/ + +HingeJoint::HingeJoint (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + InitJointLimits (m_Limits); + InitJointSpring (m_Spring); + InitJointMotor (m_Motor); + + m_UseLimits = false; + m_UseMotor = false; + m_UseSpring = false; + + CompileTimeAssert(sizeof(JointMotor) == sizeof(NxMotorDesc), "Unity JointMotor type has different size from physx one"); + CompileTimeAssert(sizeof(JointSpring) == sizeof(NxSpringDesc), "Unity JointSpring type has different size from physx one"); + CompileTimeAssert(sizeof(JointLimits) == sizeof(NxJointLimitPairDesc), "Unity JointLimits type has different size from physx one"); +} + +HingeJoint::~HingeJoint () +{ +} + +inline NxSpringDesc ToNovodex (const JointSpring& spring) +{ + JointSpring desc = spring; + desc.targetPosition = Deg2Rad (desc.targetPosition); + return (const NxSpringDesc&)desc; +} + +inline NxMotorDesc ToNovodex (const JointMotor& motor) +{ + JointMotor desc = motor; + desc.targetVelocity = Deg2Rad (desc.targetVelocity); + return (const NxMotorDesc&)desc; +} + +#define kHingeJointLimitError "Joint limits for hinge joint out of bounds. Limits need to be between -360 and 360 degrees." +inline NxJointLimitPairDesc ToNovodex (const JointLimits& limits) +{ + NxJointLimitPairDesc l = (const NxJointLimitPairDesc&)limits; + if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1)) + { + // PhysX specs limit hinge joints to [-180,180] degress. But it seems 360 works fine + // and is very useful in practice. Higher numbers produce broken results (case 492847) + if (l.low.value < -360) + { + l.low.value = -360; + ErrorString (kHingeJointLimitError); + } + if (l.low.value > 360) + { + l.low.value = 360; + ErrorString (kHingeJointLimitError); + } + if (l.high.value < -360) + { + l.high.value = -360; + ErrorString (kHingeJointLimitError); + } + if (l.high.value > 360) + { + l.high.value = 360; + ErrorString (kHingeJointLimitError); + } + } + l.low.value = Deg2Rad (l.low.value); + l.high.value = Deg2Rad (l.high.value); + + /// DO SOME SPECIAL HANDLING WHEN LOW IS LARGER THAN HIGH!!!!! + /// MAKE IT MORE INTUITIVE + + if (l.low.value > l.high.value) + swap (l.low, l.high); + + return l; +} + +void HingeJoint::SetUseMotor (bool enable) +{ + SetDirty (); + m_UseMotor = enable; + if (m_Joint) + { + UInt32 flags = GET_JOINT()->getFlags (); + if (enable) + flags |= NX_RJF_MOTOR_ENABLED; + else + flags &= ~NX_RJF_MOTOR_ENABLED; + GET_JOINT()->setFlags (flags); + } +} + +bool HingeJoint::GetUseMotor () const +{ + return m_UseMotor; +} + +void HingeJoint::SetUseLimits (bool enable) +{ + SetDirty (); + m_UseLimits = enable; + if (m_Joint) + { + UInt32 flags = GET_JOINT()->getFlags (); + if (enable) + flags |= NX_RJF_LIMIT_ENABLED; + else + flags &= ~NX_RJF_LIMIT_ENABLED; + GET_JOINT()->setFlags (flags); + } +} + +bool HingeJoint::GetUseLimits () const +{ + return m_UseLimits; +} + +void HingeJoint::SetUseSpring (bool enable) +{ + SetDirty (); + m_UseSpring = enable; + if (m_Joint) + { + UInt32 flags = GET_JOINT()->getFlags (); + if (enable) + flags |= NX_RJF_SPRING_ENABLED; + else + flags &= ~NX_RJF_SPRING_ENABLED; + GET_JOINT()->setFlags (flags); + } +} + +bool HingeJoint::GetUseSpring () const +{ + return m_UseSpring; +} + +JointMotor HingeJoint::GetMotor () const +{ + return m_Motor; +} + +void HingeJoint::SetMotor (const JointMotor& motor) +{ + SetDirty (); + m_Motor = motor; + if (m_Joint) + { + GET_JOINT()->setMotor (ToNovodex (motor)); + } +} + +JointSpring HingeJoint::GetSpring () const +{ + return m_Spring; +} + +void HingeJoint::SetSpring (const JointSpring& spring) +{ + SetDirty (); + m_Spring = spring; + if (m_Joint) + GET_JOINT()->setSpring (ToNovodex (spring)); +} + +JointLimits HingeJoint::GetLimits () const +{ + return m_Limits; +} + +void HingeJoint::SetLimits (const JointLimits& limits) +{ + SetDirty (); + m_Limits = limits; + if (m_Joint) + { + GET_JOINT()->setLimits (ToNovodex (limits)); + + // Novodex seems to have a bug that it will not update HingeJoints when only the limits are changed. + // Also call setMotor to force it to update. + GET_JOINT()->setMotor (ToNovodex (m_Motor)); + } +} + +float HingeJoint::GetVelocity () const +{ + if (m_Joint) + return Rad2Deg (GET_JOINT()->getVelocity ()); + else + return 0.0F; +} + +float HingeJoint::GetAngle () const +{ + if (m_Joint) + return Rad2Deg (GET_JOINT()->getAngle ()); + else + return 0.0F; +} + +/* +- When the rigid body which is attached with the hinge joint is activated after the joint is loaded + It will not be connected with the joint! +*/ + +template<class TransferFunction> +void HingeJoint::Transfer (TransferFunction& transfer) +{ + JointTransferPre (transfer); + + TRANSFER (m_UseSpring); + transfer.Align(); + TRANSFER (m_Spring); + + TRANSFER (m_UseMotor); + transfer.Align(); + TRANSFER (m_Motor); + + TRANSFER (m_UseLimits); + transfer.Align(); + TRANSFER (m_Limits); + + JointTransferPost (transfer); +} + +void HingeJoint::Create () +{ + AssertIf (!IsActive ()); + + NxRevoluteJointDesc desc; + + if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING) + GET_JOINT()->saveToDesc (desc); + + desc.motor = ToNovodex (m_Motor); + desc.limit = ToNovodex (m_Limits); + desc.spring = ToNovodex (m_Spring); + + desc.flags = 0; + if (m_UseMotor) + desc.flags |= NX_RJF_MOTOR_ENABLED; + if (m_UseLimits) + desc.flags |= NX_RJF_LIMIT_ENABLED; + if (m_UseSpring) + desc.flags |= NX_RJF_SPRING_ENABLED; + + FINALIZE_CREATE (desc, NxRevoluteJoint); +} + +IMPLEMENT_AXIS_ANCHOR(HingeJoint,NxRevoluteJointDesc) + +} + +IMPLEMENT_CLASS (HingeJoint) +IMPLEMENT_OBJECT_SERIALIZE (HingeJoint) + +#undef GET_JOINT +#endif //ENABLE_PHSYICS diff --git a/Runtime/Dynamics/HingeJoint.h b/Runtime/Dynamics/HingeJoint.h new file mode 100644 index 0000000..b1051e3 --- /dev/null +++ b/Runtime/Dynamics/HingeJoint.h @@ -0,0 +1,69 @@ +#ifndef HINGEJOINT_H +#define HINGEJOINT_H + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Math/Vector3.h" +#include "JointDescriptions.h" +#include "Joint.h" +class Rigidbody; +class NxJointDesc; +class NxRevoluteJoint; + +namespace Unity +{ + +class HingeJoint : public Joint +{ + public: + + REGISTER_DERIVED_CLASS (HingeJoint, Joint) + DECLARE_OBJECT_SERIALIZE (HingeJoint) + + HingeJoint (MemLabelId label, ObjectCreationMode mode); + + JointMotor GetMotor () const; + void SetMotor (const JointMotor& motor); + + JointLimits GetLimits () const; + void SetLimits (const JointLimits& limits); + + JointSpring GetSpring () const; + void SetSpring (const JointSpring& spring); + + void SetUseMotor (bool enable); + bool GetUseMotor () const; + + void SetUseLimits (bool enable); + bool GetUseLimits () const; + + void SetUseSpring (bool enable); + bool GetUseSpring () const ; + + // The hinge angle's rate of change (angular velocity). + float GetVelocity () const; + + // The hinge's angle + float GetAngle () const; + + virtual void ApplySetupAxesToDesc (int option); + + private: + + virtual void Create (); + + void SetSpringNoEnable (const JointSpring& spring); + void SetMotorNoEnable (const JointMotor& motor); + void SetLimitsNoEnable (const JointLimits& limits); + + JointLimits m_Limits; + JointSpring m_Spring; + JointMotor m_Motor; + + bool m_UseLimits; + bool m_UseMotor; + bool m_UseSpring; +}; + +} + +#endif diff --git a/Runtime/Dynamics/Joint.cpp b/Runtime/Dynamics/Joint.cpp new file mode 100644 index 0000000..eaa470e --- /dev/null +++ b/Runtime/Dynamics/Joint.cpp @@ -0,0 +1,325 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "Joint.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Filters/AABBUtility.h" +#include "Runtime/Graphics/Transform.h" +#include "PhysicsManager.h" +#include "RigidBody.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "NxWrapperUtility.h" + +namespace Unity +{ + +Joint::Joint (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + m_DidSetupAxes = false; + m_Joint = NULL; + m_Anchor = Vector3f::zero; + m_AutoConfigureConnectedAnchor = true; + m_ConnectedAnchor = Vector3f::zero; + m_Axis = Vector3f::xAxis; + + m_BreakForce = std::numeric_limits<float>::infinity (); + m_BreakTorque = std::numeric_limits<float>::infinity (); +} + + +Joint::~Joint () +{ + Cleanup (); +} + + +void Joint::Reset () +{ + // Anchor is at the edge of bounding volume .y ! + Super::Reset (); + AABB aabb; + if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb)) + m_Anchor = Vector3f (0, aabb.GetCenter ().y + aabb.GetExtent ().y, 0); + else + m_Anchor = Vector3f::zero; + m_ConnectedAnchor = Vector3f::zero; + m_AutoConfigureConnectedAnchor = true; + m_Axis = Vector3f::xAxis; +} + +void Joint::CheckConsistency () +{ + Super::CheckConsistency (); + + Rigidbody* body = QueryComponent (Rigidbody); + + Rigidbody* otherBody = m_ConnectedBody; + + if (otherBody == body) + { + m_ConnectedBody = NULL; + } +} + +inline Vector3f GlobalToLocalUnscaledSpace (Transform& transform, Vector3f& p) +{ + Vector3f localAnchor = p - transform.GetPosition (); + localAnchor = transform.InverseTransformDirection (localAnchor); + return localAnchor; +} + + +/// OPTIMIZE ALL THOSE TRANSFORM ACCESSES. +/// Use a matrix instead. +void Joint::SetupAxes (NxJointDesc& desc, int options) +{ + Vector3f globalAnchor, globalAxis, globalNormal; + CalculateGlobalHingeSpace (globalAnchor, globalAxis, globalNormal); + Vector3f globalConnectedAnchor = CalculateGlobalConnectedAnchor (m_AutoConfigureConnectedAnchor); + + Transform& transform = GetComponent (Transform); + Transform* otherTransform = NULL; + Rigidbody* body; + body = m_ConnectedBody; + if (body) + otherTransform = body->QueryComponent (Transform); + + // Setup anchor + if (options & kChangeAnchor) + { + desc.localAnchor[0] = Vec3ToNx(GlobalToLocalUnscaledSpace (transform, globalAnchor)); + } + if (options & kChangeAxis) + { + desc.localNormal[0] = Vec3ToNx(transform.InverseTransformDirection (globalNormal)); + desc.localAxis[0] = Vec3ToNx(transform.InverseTransformDirection (globalAxis)); + } + + if (otherTransform) + { + if (options & kChangeAnchor) + { + desc.localAnchor[1] = Vec3ToNx(GlobalToLocalUnscaledSpace (*otherTransform, globalConnectedAnchor)); + } + if (options & kChangeAxis) + { + desc.localAxis[1] = Vec3ToNx(otherTransform->InverseTransformDirection (globalAxis)); + desc.localNormal[1] = Vec3ToNx(otherTransform->InverseTransformDirection (globalNormal)); + } + } + else + { + if (options & kChangeAnchor) + { + desc.localAnchor[1] = Vec3ToNx(globalConnectedAnchor); + } + if (options & kChangeAxis) + { + desc.localAxis[1] = Vec3ToNx(globalAxis); + desc.localNormal[1] = Vec3ToNx(globalNormal); + } + } +} + +Vector3f Joint::CalculateGlobalConnectedAnchor (bool autoConfigureConnectedFrame) +{ + Vector3f globalConnectedAnchor; + Transform* otherTransform = NULL; + Rigidbody* body = m_ConnectedBody; + if (body) + otherTransform = body->QueryComponent (Transform); + + if (autoConfigureConnectedFrame) + { + Vector3f globalAnchor = GetComponent (Transform).TransformPoint (m_Anchor); + if (otherTransform) + m_ConnectedAnchor = otherTransform->InverseTransformPoint (globalAnchor); + else + m_ConnectedAnchor = globalAnchor; + } + + if (otherTransform) + globalConnectedAnchor = otherTransform->TransformPoint (m_ConnectedAnchor); + else + globalConnectedAnchor = m_ConnectedAnchor; + + return globalConnectedAnchor; +} + +void Joint::CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const +{ + const Transform& transform = GetComponent (Transform); + + Vector3f localAxis = m_Axis; + if (SqrMagnitude (localAxis) < Vector3f::epsilon) + localAxis = Vector3f (1.0F, 0.0F, 0.0F); + + globalAnchor = transform.TransformPoint (m_Anchor); + + Vector3f localDirection = - m_Anchor; + Vector3f localNormal = Cross (localDirection, localAxis); + OrthoNormalize (&localAxis, &localNormal); + + globalAxis = transform.TransformDirection (localAxis); + globalNormal = transform.TransformDirection (localNormal); +} + +float ToNovodexInfinity (float f) +{ + if (f == std::numeric_limits<float>::infinity ()) + return NX_MAX_REAL; + else + return f; +} + +void Joint::SetBreakForce (float force) +{ + SetDirty (); + m_BreakForce = force; + if (m_Joint) + m_Joint->setBreakable (ToNovodexInfinity (m_BreakForce), GetBreakTorque ()); +} + +void Joint::SetBreakTorque (float torque) +{ + SetDirty (); + m_BreakTorque = torque; + if (m_Joint) + m_Joint->setBreakable (GetBreakForce (), ToNovodexInfinity (m_BreakTorque)); +} + +float Joint::GetBreakForce () const +{ + return m_BreakForce; +} + +float Joint::GetBreakTorque () const +{ + return m_BreakTorque; +} + +void Joint::FinalizeCreateImpl (NxJointDesc& desc, bool swapActors) +{ + desc.maxForce = ToNovodexInfinity (m_BreakForce); + desc.maxTorque = ToNovodexInfinity (m_BreakTorque); + desc.userData = this; + + Rigidbody& body = GetComponent (Rigidbody); + body.Create (true); + body.FetchPoseFromTransform (); + + AssertIf (!body.m_ActiveScene); + bool didActorsChange = false; + + int actor0 = swapActors ? 1 : 0; + int actor1 = swapActors ? 0 : 1; + + if (desc.actor[actor0] != body.m_Actor) + { + desc.actor[actor0] = body.m_Actor; + didActorsChange = true; + } + + Rigidbody* otherBody = m_ConnectedBody; + if (otherBody != NULL && otherBody->IsActive ()) + { + otherBody->Create (true); + otherBody->FetchPoseFromTransform (); + + AssertIf (!otherBody->m_ActiveScene); + if (desc.actor[actor1] != otherBody->m_Actor) + { + desc.actor[actor1] = otherBody->m_Actor; + didActorsChange = true; + } + } + else + { + if (desc.actor[actor1] != NULL) + { + desc.actor[actor1] = NULL; + didActorsChange = true; + } + } + + // If we don't recreate the joint novodex will mark the joint broken and then we get an assert. + // because we can't save to the desc anymore! + if (didActorsChange) + Cleanup (); + + if (!m_DidSetupAxes || m_Joint == NULL) + { + SetupAxes (desc); + m_DidSetupAxes = true; + } +} + +void Joint::Cleanup () +{ + if (m_Joint) + { + GetDynamicsScene ().releaseJoint (*m_Joint); + m_Joint = NULL; + } +} + +void Joint::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + if (IsActive ()) + Create (); + else + Cleanup (); +} + +void Joint::Deactivate (DeactivateOperation operation) +{ + Cleanup (); + Super::Deactivate (operation); +} + +void Joint::SetConnectedBody (PPtr<Rigidbody> body) +{ + if (m_ConnectedBody != body) + { + SetDirty (); + m_ConnectedBody = body; + } + + if (IsActive ()) + Create (); +} + +void Joint::SetAxis (const Vector3f& axis) +{ + SetDirty (); + m_Axis = axis; + ApplySetupAxesToDesc(kChangeAxis); +} + +void Joint::SetAnchor (const Vector3f& anchor) +{ + SetDirty (); + m_Anchor = anchor; + ApplySetupAxesToDesc(kChangeAnchor); +} + +void Joint::SetConnectedAnchor (const Vector3f& anchor) +{ + SetDirty (); + m_ConnectedAnchor = anchor; + ApplySetupAxesToDesc(kChangeAnchor); +} + +void Joint::SetAutoConfigureConnectedAnchor (bool anchor) +{ + SetDirty (); + m_AutoConfigureConnectedAnchor = anchor; + ApplySetupAxesToDesc(kChangeAxis|kChangeAnchor); +} + +} + +IMPLEMENT_CLASS (Joint) + +#endif //ENABLE_PHYSICS
\ No newline at end of file diff --git a/Runtime/Dynamics/Joint.h b/Runtime/Dynamics/Joint.h new file mode 100644 index 0000000..e632116 --- /dev/null +++ b/Runtime/Dynamics/Joint.h @@ -0,0 +1,148 @@ +#ifndef JOINT_H +#define JOINT_H + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Math/Vector3.h" +#include "RigidBody.h" +#include "JointDescriptions.h" +class Rigidbody; +class NxJointDesc; +class NxJoint; + +namespace Unity +{ + +class Joint : public Component +{ + public: + + REGISTER_DERIVED_ABSTRACT_CLASS (Joint, Component) + + Joint (MemLabelId label, ObjectCreationMode mode); + // virtual ~Joint (); declared-by-macro + + Vector3f GetAxis () const { return m_Axis; } + virtual void SetAxis (const Vector3f& axis); + + Vector3f GetAnchor () const { return m_Anchor; } + virtual void SetAnchor (const Vector3f& axis); + + Vector3f GetConnectedAnchor () const { return m_ConnectedAnchor; } + virtual void SetConnectedAnchor (const Vector3f& axis); + + bool GetAutoConfigureConnectedAnchor () const { return m_AutoConfigureConnectedAnchor; } + virtual void SetAutoConfigureConnectedAnchor (bool anchor); + + void SetBreakForce (float force); + float GetBreakForce () const; + void SetBreakTorque (float torque); + float GetBreakTorque () const; + + void SetConnectedBody (PPtr<Rigidbody> body); + PPtr<Rigidbody> GetConnectedBody () const { return m_ConnectedBody; } + + virtual void CalculateGlobalHingeSpace (Vector3f& globalAnchor, Vector3f& globalAxis, Vector3f& globalNormal) const; + Vector3f CalculateGlobalConnectedAnchor (bool autoConfigureConnectedFrame); + + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + virtual void Deactivate (DeactivateOperation operation); + virtual void Reset (); + + virtual void CheckConsistency(); + + void NullJoint () { m_Joint = NULL; } + + protected: + + /// SUB CLASSES OVERRIDE THIS + virtual void Create () = 0; + virtual void ApplySetupAxesToDesc (int option) = 0; + + template<class TransferFunction> + void JointTransferPre (TransferFunction& transfer) + { + Super::Transfer (transfer); + + TRANSFER_SIMPLE (m_ConnectedBody); + TRANSFER_SIMPLE (m_Anchor); + TRANSFER_SIMPLE (m_Axis); + TRANSFER_SIMPLE (m_AutoConfigureConnectedAnchor); + transfer.Align(); + TRANSFER_SIMPLE (m_ConnectedAnchor); + + } + + template<class TransferFunction> + void JointTransferPreNoAxis (TransferFunction& transfer) + { + Super::Transfer (transfer); + + TRANSFER_SIMPLE (m_ConnectedBody); + TRANSFER_SIMPLE (m_Anchor); + TRANSFER_SIMPLE (m_AutoConfigureConnectedAnchor); + transfer.Align(); + TRANSFER_SIMPLE (m_ConnectedAnchor); + } + + template<class TransferFunction> + void JointTransferPost (TransferFunction& transfer) + { + TRANSFER (m_BreakForce); + TRANSFER (m_BreakTorque); + } + + void FinalizeCreateImpl (NxJointDesc& desc, bool swapActors = false); + + NxJoint* m_Joint; + bool m_AutoConfigureConnectedAnchor; + Vector3f m_Anchor; + Vector3f m_ConnectedAnchor; + Vector3f m_Axis; + + enum { kChangeAxis = 1 << 0, kChangeAnchor = 1 << 1 }; + void SetupAxes (NxJointDesc& desc, int options = kChangeAnchor|kChangeAxis); + + private: + + void Cleanup (); + + bool m_DidSetupAxes; + + float m_BreakForce;///< Maximum force the joint can withstand before breaking. Infinity means unbreakable. range { 0.001, infinity } + float m_BreakTorque; ///< Maximum torque the joint can withstand before breaking. Infinity means unbreakable. range { 0.001, infinity } + + protected: + PPtr<Rigidbody> m_ConnectedBody; + + friend class ::Rigidbody; +}; + +/// We need to to know the type of the desc so unfortunately this function must be inside the subclasses. +/// For reuse of code we do it with macros +#define IMPLEMENT_AXIS_ANCHOR(klass,nxdesc)\ +void klass::ApplySetupAxesToDesc (int option)\ +{\ + if (IsActive () && m_Joint)\ + {\ + nxdesc desc;\ + AssertIf (m_Joint->getState () == NX_JS_BROKEN);\ + GET_JOINT()->saveToDesc (desc);\ + SetupAxes (desc, option);\ + GET_JOINT()->loadFromDesc (desc);\ + AssertIf (m_Joint->getState () == NX_JS_BROKEN);\ + }\ +}\ + +#define FINALIZE_CREATE(desc, type)\ + FinalizeCreateImpl (desc);\ + if (GET_JOINT ())\ + GET_JOINT ()->loadFromDesc (desc);\ + else\ + m_Joint = GetDynamicsScene ().createJoint (desc);\ + AssertIf (m_Joint && m_Joint->getState () == NX_JS_BROKEN);\ + Assert (!GetGameObject().IsDestroying ()); + +} + +#endif diff --git a/Runtime/Dynamics/JointDescriptions.h b/Runtime/Dynamics/JointDescriptions.h new file mode 100644 index 0000000..9236509 --- /dev/null +++ b/Runtime/Dynamics/JointDescriptions.h @@ -0,0 +1,215 @@ +#ifndef JOINTDESCRIPTIONS_H +#define JOINTDESCRIPTIONS_H + +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" + +struct JointMotor +{ + /* + The relative velocity the motor is trying to achieve. The motor will only be able + to reach this velocity if the maxForce is sufficiently large. If the joint is + spinning faster than this velocity, the motor will actually try to brake. If you set this + to infinity then the motor will keep speeding up, unless there is some sort of resistance + on the attached bodies. The sign of this variable determines the rotation direction, + with positive values going the same way as positive joint angles. + Default is infinity. + */ + + float targetVelocity;///< target velocity of motor + + /* + The maximum force (torque in this case) the motor can exert. Zero disables the motor. + Default is 0, should be >= 0. Setting this to a very large value if velTarget is also + very large may not be a good idea. + */ + float force; ///< maximum motor force / torque range { 0, infinity } + + /* + If true, motor will not brake when it spins faster than velTarget + default: false. + */ + int freeSpin;/// + + DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointMotor) +}; + +template<class TransferFunction> +void JointMotor::Transfer (TransferFunction& transfer) +{ + TRANSFER_SIMPLE (targetVelocity); + TRANSFER (force); + transfer.Transfer (freeSpin, "freeSpin", kEditorDisplaysCheckBoxMask); +} + +struct JointDrive +{ + int mode; ///< The mode of what this dirves enum { Disabled = 0, position = 1, velocity = 2, position and velocity = 3 } + float positionSpring;///< The spring used to reach the target range { 0, infinity } + float positionDamper;///< The damping used to reach the target range { 0, infinity } + float maximumForce;///< The maximum force the drive can exert to reach the target velocity. range {0, infinity} + + DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointDrive) +}; + +template<class TransferFunction> +void JointDrive::Transfer (TransferFunction& transfer) +{ + transfer.SetVersion (2); + + TRANSFER_SIMPLE (mode); + TRANSFER_SIMPLE (positionSpring); + TRANSFER_SIMPLE (positionDamper); + TRANSFER_SIMPLE (maximumForce); + if (transfer.IsOldVersion(1) && transfer.IsReading()) + { + // This case used to be ignored in old versions of PhysX, but no longer is. + // If it is zero, set it to a working value instead. + if (mode == NX_D6JOINT_DRIVE_POSITION) + maximumForce = NX_MAX_F32; + } +} +struct SoftJointLimit +{ + float limit; + float bounciness;///< When the joint hits the limit. This will determine how bouncy it will be. { 0, 1 } + float spring;///< If greater than zero, the limit is soft. The spring will pull the joint back. { 0, infinity } + float damper;///< If spring is greater than zero, the limit is soft. This is the damping of spring. { 0, infinity } + + DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (SoftJointLimit) +}; + +template<class TransferFunction> +void SoftJointLimit::Transfer (TransferFunction& transfer) +{ + TRANSFER_SIMPLE (limit); + TRANSFER_SIMPLE (bounciness); + TRANSFER_SIMPLE (spring); + TRANSFER_SIMPLE (damper); +} + + + + +// Describes a joint spring. The spring is implicitly integrated, so even high spring and damper +// coefficients should be robust. +struct JointSpring +{ + float spring; //!< spring coefficient range { 0, infinity } + float damper; //!< damper coefficient range { 0, infinity } + float targetPosition; //!< target value (angle/position) of spring where the spring force is zero. + + bool operator != (const JointSpring& s)const + { + return spring != s.spring + || damper != s.damper + || targetPosition != s.targetPosition + ; + } + + DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointSpring) +}; + +template<class TransferFunction> +void JointSpring::Transfer (TransferFunction& transfer) +{ + TRANSFER_SIMPLE (spring); + TRANSFER_SIMPLE (damper); + TRANSFER_SIMPLE (targetPosition); +} + +struct JointLimits +{ + float min; + float minBounce;///< range {0, 1} + float minHardness;// unsupported + + float max; + float maxBounce;///< range {0, 1} + float maxHardness;// unsupported + JointLimits () { minHardness = maxHardness = 0.0F; } + + DECLARE_SERIALIZE_NO_PPTR (JointLimits) +}; + +template<class TransferFunction> +void JointLimits::Transfer (TransferFunction& transfer) +{ + TRANSFER_SIMPLE (min); + TRANSFER_SIMPLE (max); + TRANSFER_SIMPLE (minBounce); + TRANSFER_SIMPLE (maxBounce); +} + +inline void InitJointLimits (JointLimits& limits) +{ + limits.min = 0.0F; + limits.max = 0.0F; + limits.minBounce = 0.0F; + limits.maxBounce = 0.0F; + limits.minHardness = 0.0F; + limits.maxHardness = 0.0F; +} + +inline void InitJointSpring (JointSpring& spring) +{ + spring.spring = 0.0F; + spring.damper = 0.0F; + spring.targetPosition = 0.0F; +} + +inline void InitJointMotor (JointMotor& motor) +{ + motor.targetVelocity = 0.0F; + motor.force = 0.0F; + motor.freeSpin = 0; +} + +inline void InitJointDrive (JointDrive& motor) +{ + motor.mode = 0; + motor.positionSpring = 0.0F; + motor.positionDamper = 0.0F; + motor.maximumForce = NX_MAX_F32; +} + +inline void InitSoftJointLimit (SoftJointLimit& motor) +{ + motor.limit = 0.0F; + motor.bounciness = 0.0F; + motor.spring = 0.0F; + motor.damper = 0.0F; +} + +inline void ConvertDrive (JointDrive& drive, NxJointDriveDesc& out) +{ + out.spring = drive.positionSpring; + out.damping = drive.positionDamper; + out.forceLimit = drive.maximumForce; + out.driveType = drive.mode; +} + +inline void ConvertDrive (JointDrive& drive, NxJointDriveDesc& out, int mask) +{ + out.spring = drive.positionSpring; + out.damping = drive.positionDamper; + out.forceLimit = drive.maximumForce; + out.driveType = mask; +} + +inline void ConvertSoftLimit (SoftJointLimit& limit, NxJointLimitSoftDesc& out) +{ + out.value = Deg2Rad (limit.limit); + out.restitution = limit.bounciness; + out.spring = limit.spring; + out.damping = limit.damper; +} + +inline void ConvertSoftLimitLinear (SoftJointLimit& limit, NxJointLimitSoftDesc& out) +{ + out.value = limit.limit; + out.restitution = limit.bounciness; + out.spring = limit.spring; + out.damping = limit.damper; +} + +#endif diff --git a/Runtime/Dynamics/Joints.h b/Runtime/Dynamics/Joints.h new file mode 100644 index 0000000..12a2e79 --- /dev/null +++ b/Runtime/Dynamics/Joints.h @@ -0,0 +1,6 @@ +#ifndef JOINTS_H +#define JOINTS_H + +#include "HingeJoint.h" + +#endif diff --git a/Runtime/Dynamics/MeshCollider.cpp b/Runtime/Dynamics/MeshCollider.cpp new file mode 100644 index 0000000..124d532 --- /dev/null +++ b/Runtime/Dynamics/MeshCollider.cpp @@ -0,0 +1,326 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "MeshCollider.h" +#include "Runtime/Graphics/Transform.h" +#include "RigidBody.h" +#include "PhysicsManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Filters/AABBUtility.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Filters/Mesh/LodMeshFilter.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "nxmemorystream.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h" +#include "Runtime/Misc/BuildSettings.h" + +#define GET_CONVEX_SHAPE() ((class NxConvexShape*)m_Shape) +#define GET_MESH_SHAPE() ((class NxTriangleMeshShape*)m_Shape) + +#if UNITY_EDITOR +const NxConvexMesh* MeshCollider::GetConvexMesh() const +{ + if( !m_Shape ) + return NULL; + if( !m_Shape->isConvexMesh() ) + return NULL; + const NxConvexMesh& mesh = GET_CONVEX_SHAPE ()->getConvexMesh (); + return &mesh; +} +const NxTriangleMesh* MeshCollider::GetTriangleMesh() const +{ + if( !m_Shape ) + return NULL; + if( m_Shape->isConvexMesh() ) + return NULL; + const NxTriangleMesh& mesh = GET_MESH_SHAPE ()->getTriangleMesh (); + return &mesh; +} +#endif + +MeshCollider::MeshCollider (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +, m_MeshNode(this) +{ + m_Shared = false; +} + +MeshCollider::~MeshCollider () +{ +} + +void MeshCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if (m_Shape) + { + // Apply changed values + if (m_Convex != (m_Shape->isConvexMesh() != NULL)) + SetConvex(m_Convex); + + if (m_Shape) + { + bool smooth; + if (m_Shape->isConvexMesh()) + { + NxConvexShapeDesc desc; + GET_CONVEX_SHAPE ()->saveToDesc(desc); + smooth = desc.meshFlags & NX_MESH_SMOOTH_SPHERE_COLLISIONS; + } + else + { + NxTriangleMeshShapeDesc desc; + GET_MESH_SHAPE ()->saveToDesc(desc); + smooth = desc.meshFlags & NX_MESH_SMOOTH_SPHERE_COLLISIONS; + } + if (smooth != m_SmoothSphereCollisions) + SetSmoothSphereCollisions(m_SmoothSphereCollisions); + SetSharedMesh(m_Mesh); + } + } + + Super::AwakeFromLoad (awakeMode); +} + +void MeshCollider::Cleanup () +{ + m_MeshNode.RemoveFromList(); + if (m_Shape) + { + if (m_Shape->isConvexMesh()) + { + NxConvexMesh& mesh = GET_CONVEX_SHAPE ()->getConvexMesh (); + Super::Cleanup (); + if (!m_Shared) + GetDynamicsSDK ().releaseConvexMesh (mesh); + } + else + { + NxTriangleMesh& mesh = GET_MESH_SHAPE ()->getTriangleMesh (); + Super::Cleanup (); + if (!m_Shared) + GetDynamicsSDK ().releaseTriangleMesh (mesh); + } + } +} + +void MeshCollider::DidDeleteMesh () +{ + Cleanup (); +} + +void MeshCollider::CreateShape( void* nxmesh, const Rigidbody* ignoreRigidbody ) +{ + if ( !nxmesh ) + return; + + if (m_Convex) + { + NxConvexShapeDesc shapeDesc; + shapeDesc.meshData = (NxConvexMesh*)nxmesh; + + if (m_SmoothSphereCollisions) + shapeDesc.meshFlags |= NX_MESH_SMOOTH_SPHERE_COLLISIONS; + + FinalizeCreate (shapeDesc, true, ignoreRigidbody); + } + else + { + NxTriangleMeshShapeDesc shapeDesc; + + if (m_SmoothSphereCollisions) + shapeDesc.meshFlags |= NX_MESH_SMOOTH_SPHERE_COLLISIONS; + + shapeDesc.meshData = (NxTriangleMesh*)nxmesh; + + FinalizeCreate (shapeDesc, true, ignoreRigidbody); + } +} + +//@TODO: Updating physics mesh when reimporting no longer works! + +void MeshCollider::Create (const Rigidbody* ignoreRigidbody) +{ + if (m_Shape) + Cleanup (); + + Mesh* mesh = m_Mesh; + m_CachedMesh = mesh; + if (mesh == NULL) + return; + // do not create anything if have no vertices or less-than-one triangle + if (mesh->GetVertexCount() == 0 || mesh->GetPrimitiveCount() == 0) + return; + + /* + NxCookingParams params; + params.hintCollisionSpeed = true; + NxSetCookingParams (params); + */ + + Matrix4x4f scalematrix; + + TransformType type = GetComponent (Transform).CalculateTransformMatrixScaleDelta (scalematrix); + + void* nxmesh = NULL; + if (IsNoScaleTransform(type) && !mesh->IsSharedPhysicsMeshDirty()) + { + m_Shared = true; + + if (m_Convex) + { + nxmesh = mesh->GetSharedNxConvexMesh (); + } + else + { + nxmesh = mesh->GetSharedNxMesh (); + } + } + // For scaled meshes, create instance + else + { + m_Shared = false; + nxmesh = CreateNxMeshFromUnityMesh(mesh, m_Convex, scalematrix, type); + } + + if (nxmesh == NULL) + return; + + mesh->AddObjectUser( m_MeshNode ); + + CreateShape( nxmesh, ignoreRigidbody ); +} + +void MeshCollider::ReCreate() +{ + if( !m_Shape ) + return; + // Re-creating the full mesh collider is expensive, so we only re-create the shape and not the mesh. + if ( m_Shape->isConvexMesh() ) + { + NxConvexMesh& mesh = GET_CONVEX_SHAPE ()->getConvexMesh (); + Super::Cleanup(); + CreateShape( &mesh, NULL ); + } + else + { + NxTriangleMesh& mesh = GET_MESH_SHAPE ()->getTriangleMesh (); + Super::Cleanup(); + CreateShape( &mesh, NULL ); + } +} + +void MeshCollider::ScaleChanged () +{ + Create (NULL); +} + +void MeshCollider::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) + RigidbodyMassDistributionChanged (); + } + + if (changeMask & Transform::kScaleChanged) + ScaleChanged (); + + RefreshPhysicsInEditMode(); + } + else if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1) && IsActive() && GetEnabled()) + Create (NULL); +} + +void MeshCollider::InitializeClass () +{ + REGISTER_MESSAGE_VOID(MeshCollider, kDidDeleteMesh, DidDeleteMesh); +} + +void MeshCollider::SetSharedMesh (const PPtr<Mesh> m) +{ + if (m_CachedMesh != m) + { + SetDirty (); + m_Mesh = m; + if (IsActive()) + Create(NULL); + } +} + +PPtr<Mesh> MeshCollider::GetSharedMesh () +{ + return m_Mesh; +} + +void MeshCollider::Reset () +{ + Super::Reset (); + if (GetGameObjectPtr ()) + { + MeshFilter* filter = QueryComponent (MeshFilter); + if (filter && m_Mesh.GetInstanceID () == 0) + { + PPtr<Mesh> newMesh = filter->GetSharedMesh (); + if (newMesh != m_Mesh) + { + m_Mesh = newMesh; + if (IsActive()) + Create(NULL); + } + } + } + m_SmoothSphereCollisions = false; + m_Convex = false; +} + +template<class TransferFunction> +void MeshCollider::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.SetVersion (2); + + TRANSFER (m_SmoothSphereCollisions); + transfer.Transfer (m_Convex, "m_Convex"); + transfer.Align(); + TRANSFER (m_Mesh); +} + +void MeshCollider::SetConvex (bool convex) +{ + m_Convex = convex; + if (m_Shape) + Create(NULL); + SetDirty(); + RefreshPhysicsInEditMode(); +} + +void MeshCollider::SetSmoothSphereCollisions (bool smooth) +{ + m_SmoothSphereCollisions = smooth; + if (m_Shape) + Create(NULL); + SetDirty(); + RefreshPhysicsInEditMode(); +} + +IMPLEMENT_CLASS_HAS_INIT (MeshCollider) +IMPLEMENT_OBJECT_SERIALIZE (MeshCollider) +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/MeshCollider.h b/Runtime/Dynamics/MeshCollider.h new file mode 100644 index 0000000..1773561 --- /dev/null +++ b/Runtime/Dynamics/MeshCollider.h @@ -0,0 +1,63 @@ +#ifndef MESHCOLLIDER_H +#define MESHCOLLIDER_H + +#include "Collider.h" +#include "Runtime/Math/Vector3.h" +class Mesh; + +#if UNITY_EDITOR +class NxConvexMesh; +class NxTriangleMesh; +#endif + +class MeshCollider : public Collider +{ +public: + REGISTER_DERIVED_CLASS (MeshCollider, Collider) + DECLARE_OBJECT_SERIALIZE (MeshCollider) + + MeshCollider (MemLabelId label, ObjectCreationMode mode); + + void SetSharedMesh (const PPtr<Mesh> m); + PPtr<Mesh> GetSharedMesh (); + + void SetConvex (bool convex); + bool GetConvex () const { return m_Convex; } + + void SetSmoothSphereCollisions (bool convex); + bool GetSmoothSphereCollisions () const { return m_SmoothSphereCollisions; } + + virtual void Reset (); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + virtual void TransformChanged (int changeMask); + static void InitializeClass (); + static void CleanupClass () {} + void DidDeleteMesh (); + + #if UNITY_EDITOR + const NxConvexMesh* GetConvexMesh() const; + const NxTriangleMesh* GetTriangleMesh() const; + #endif + +private: + void CreateShape( void* nxmesh, const Rigidbody* ignoreRigidbody ); + +protected: + + virtual void Create (const Rigidbody* ignoreRigidbody); + virtual void Cleanup (); + virtual void ReCreate(); + void ScaleChanged (); + + //virtual NxCCDSkeleton* CreateCCDSkeleton(float scale); + + bool m_SmoothSphereCollisions; + bool m_Convex; + bool m_Shared; + PPtr<Mesh> m_Mesh; + PPtr<Mesh> m_CachedMesh; + ListNode<Object> m_MeshNode; +}; + +#endif diff --git a/Runtime/Dynamics/NxMeshCreation.cpp b/Runtime/Dynamics/NxMeshCreation.cpp new file mode 100644 index 0000000..7ecff3e --- /dev/null +++ b/Runtime/Dynamics/NxMeshCreation.cpp @@ -0,0 +1,115 @@ +#include "UnityPrefix.h" + +#if ENABLE_PHYSICS +#include "Runtime/Math/Matrix4x4.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Dynamics/ExtractDataFromMesh.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "Runtime/Dynamics/PhysicsManager.h" +#include "Runtime/Dynamics/nxmemorystream.h" +#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h" +#include "Runtime/Interfaces/IPhysics.h" +#include "Runtime/Profiler/Profiler.h" + +PROFILER_INFORMATION(gBakeCollisionMesh, "Mesh.Bake PhysX CollisionData", kProfilerPhysics) +PROFILER_INFORMATION(gBakeCollisionScaledMesh, "Mesh.Bake Scaled Mesh PhysX CollisionData", kProfilerPhysics) + +bool CreateNxStreamFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType, MemoryStream& stream ) +{ + dynamic_array<Vector3f> vertices; + dynamic_array<Vector3f> normals; + dynamic_array<UInt16> triangles; + dynamic_array<UInt16> remap; + if (!ExtractDataFromMesh(*mesh, vertices, triangles, remap)) + return false; + + int vertexCount = vertices.size(); + int inStride = sizeof(Vector3f); + if (!IsNoScaleTransform(transformType)) + TransformPoints3x3 (scalematrix, &vertices[0], sizeof(Vector3f), &vertices[0], sizeof(Vector3f), vertexCount); + + // PhysX crashes when using only 1 triangle + // So just duplicate the triangle... + if (triangles.size() == 3) + { + triangles.push_back(triangles[0]); + triangles.push_back(triangles[1]); + triangles.push_back(triangles[2]); + } + + if (convex) + { + NxConvexMeshDesc desc; + desc.flags = NX_CF_COMPUTE_CONVEX; + + desc.numVertices = vertexCount; + desc.points = &vertices[0]; + desc.pointStrideBytes = inStride; + + return NxCookConvexMesh (desc, stream); + } + else + { + NxTriangleMeshDesc desc; + + desc.numVertices = vertexCount; + desc.points = &vertices[0]; + desc.pointStrideBytes = inStride; + + desc.numTriangles = triangles.size () / 3; + desc.triangles = &triangles[0]; + desc.triangleStrideBytes = sizeof (triangles[0]) * 3; + desc.flags = NX_MF_16_BIT_INDICES; + + if (transformType & kOddNegativeScaleTransform) + desc.flags |= NX_MF_FLIPNORMALS; + + return NxCookTriangleMesh (desc, stream); + } +} + +MemoryStream* CreateNxStreamFromUnityMesh(Mesh& meshData, bool convex) +{ + PROFILER_AUTO_THREAD_SAFE(gBakeCollisionMesh, &meshData) + Matrix4x4f identity; identity.SetIdentity(); + MemoryStream* stream = new MemoryStream (NULL, 0); + CreateNxStreamFromUnityMesh(&meshData, convex, identity, kNoScaleTransform, *stream); + return stream; +} + +void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType ) +{ +#if ENABLE_PROFILER + if (IsNoScaleTransform(transformType)) + { + PROFILER_BEGIN(gBakeCollisionMesh, mesh) + } + else + { + PROFILER_BEGIN(gBakeCollisionScaledMesh, mesh) + } +#endif + + MemoryStream stream (NULL, 0); + + if (!CreateNxStreamFromUnityMesh(mesh, convex, scalematrix, transformType, stream)) + { + PROFILER_END + return NULL; + } + + if (convex) + { + NxConvexMesh* nxmesh = GetDynamicsSDK().createConvexMesh (stream); + PROFILER_END + return nxmesh; + } + else + { + NxTriangleMesh* nxmesh = GetDynamicsSDK().createTriangleMesh (stream); + PROFILER_END + return nxmesh; + } +} + +#endif
\ No newline at end of file diff --git a/Runtime/Dynamics/NxMeshCreation.h b/Runtime/Dynamics/NxMeshCreation.h new file mode 100644 index 0000000..1c9fbcb --- /dev/null +++ b/Runtime/Dynamics/NxMeshCreation.h @@ -0,0 +1,11 @@ +#pragma once + +class Mesh; +class Matrix4x4f; +enum TransformType; +class MemoryStream; + +bool CreateNxStreamFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType, MemoryStream& stream ); +MemoryStream* CreateNxStreamFromUnityMesh(Mesh& meshData, bool convex); +//static void* CreateNxMeshFromByteStream(bool convex,dynamic_array<UInt8>& mesh) +void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType ); diff --git a/Runtime/Dynamics/NxWrapperUtility.h b/Runtime/Dynamics/NxWrapperUtility.h new file mode 100644 index 0000000..a9c159e --- /dev/null +++ b/Runtime/Dynamics/NxWrapperUtility.h @@ -0,0 +1,14 @@ +#ifndef NXWRAPPERUTILITY_H +#define NXWRAPPERUTILITY_H + +#include "External/PhysX/builds/SDKs/Foundation/include/NxVec3.h" +#include "External/PhysX/builds/SDKs/Foundation/include/NxVersionNumber.h" +#include "External/PhysX/builds/SDKs/NxCharacter/include/NxExtended.h" + +inline NxExtendedVec3 Vec3ToNxExtended (const Vector3f& v) { return NxExtendedVec3(v.x, v.y, v.z); } +inline Vector3f NxExtendedToVec3 (const NxExtendedVec3& v) { return Vector3f(v.x, v.y, v.z); } + +inline NxVec3 Vec3ToNx (const Vector3f& v) { return NxVec3(v.x, v.y, v.z); } +inline Vector3f Vec3FromNx (const NxVec3& v) { return Vector3f(v.x, v.y, v.z); } + +#endif diff --git a/Runtime/Dynamics/PhysXRaycast.cpp b/Runtime/Dynamics/PhysXRaycast.cpp new file mode 100644 index 0000000..8e16d02 --- /dev/null +++ b/Runtime/Dynamics/PhysXRaycast.cpp @@ -0,0 +1,195 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "PhysXRaycast.h" + +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Dynamics/Collider.h" +#include "Runtime/Dynamics/PhysicsManager.h" +#include "Runtime/Dynamics/RigidBody.h" +#include "Runtime/Geometry/Intersection.h" +#include "Runtime/Geometry/AABB.h" +#include "Runtime/Geometry/Plane.h" +#include <limits> + +// a small value to expand the shape AABBs with in order to make sure there are not boundary issues when broadphase culling the raycasts +static const float kAABBEpsilon = 0.00001f; +static const float kEpsilon = 0.000001f; + +void PhysXRaycast::InitializeClass () +{ + SetRaycastInterface (new PhysXRaycast ()); +} + +void PhysXRaycast::CleanupClass () +{ + PhysXRaycast* raycast = reinterpret_cast<PhysXRaycast*> (GetRaycastInterface ()); + delete raycast; + SetRaycastInterface (NULL); +} + +static inline int GetAttachedRigidbodyOrColliderInstanceID (Collider& collider) +{ + Rigidbody* body = collider.GetRigidbody (); + if (body) + return body->GetInstanceID(); + else + return collider.GetInstanceID(); +} + +bool PhysXRaycast::Raycast (const Ray& ray, float distance, int mask, HitInfo& output) +{ + RaycastHit hit; + bool result = GetPhysicsManager().Raycast(ray,distance,hit,mask); + if (!result) + return false; + + output.intersection = hit.point; + output.normal = hit.normal; + output.colliderInstanceID = hit.collider->GetInstanceID(); + output.rigidBodyOrColliderInstanceID = GetAttachedRigidbodyOrColliderInstanceID (*hit.collider); + + return true; +} + + +// Cast a ray against a list of shapes shapes +bool PhysXRaycast::Intersect(const Ray& ray, float maxDist, NxShape** shapes, AABB* shapeBounds, size_t shapeCount, HitInfo& hit) const +{ + Vector3f halfDir = ray.GetDirection() * maxDist * 0.5; + AABB rayBounds(ray.GetOrigin() + halfDir, Abs(halfDir)); + rayBounds.Expand(kAABBEpsilon); + NxRay physicsRay ((const NxVec3&)ray.GetOrigin(), (const NxVec3&)ray.GetDirection()); + + NxRaycastHit nxhit; + NxU32 flags = NX_RAYCAST_IMPACT | NX_RAYCAST_NORMAL; + + Vector3f collisionPos; + Vector3f collisionNormal; + float collisionSqrDist = std::numeric_limits<float>::infinity (); + + for (size_t s = 0; s < shapeCount; ++s) + { + if (!IntersectAABBAABBInclusive(rayBounds, shapeBounds[s])) + continue; + + NxShape* shape = shapes[s]; + if (!shape) continue; + +#if ENABLE_MULTITHREADED_CODE + // We shouldn't need to do this. But it turns out that sometimes raycast will return a duff intersection normal when + // called from multiple threads intersection the same object at the same time, this causes graphical functional test + // 267 to fail for instance. Jesper has contacted NVidia about this. + SimpleLock::AutoLock lock(m_Lock); +#endif + + if (shape->raycast (physicsRay, maxDist, flags, nxhit, false)) + { + Vector3f intersectionPoint = (const Vector3f&)nxhit.worldImpact; + float sqrDistance = SqrMagnitude(ray.GetOrigin() - intersectionPoint); + + if (sqrDistance < collisionSqrDist) + { + Collider* collider = (Collider*)shape->userData; + if (!collider || collider->GetIsTrigger ()) + continue; + hit.colliderInstanceID = collider->GetInstanceID (); + hit.rigidBodyOrColliderInstanceID = GetAttachedRigidbodyOrColliderInstanceID (*collider); + collisionSqrDist = sqrDistance; + hit.intersection = (const Vector3f&)nxhit.worldImpact; + hit.normal = (const Vector3f&)nxhit.worldNormal; + } + } + } + + return collisionSqrDist < std::numeric_limits<float>::infinity (); +} + +// Should probably be a param +enum { kMaxParticleCollisionShapes = 256 }; + +// Determines which shapes overlap the particle system and caches the bounding boxes of those shapes +size_t PhysXRaycast::BroadPhaseCulling(dynamic_array<NxShape*>& nxShapes, dynamic_array<AABB>& aabbs, const MinMaxAABB& particleSystemAABB, const int filter, bool staticOnly) const +{ + size_t shapeCount = GetShapes(particleSystemAABB, kMaxParticleCollisionShapes, nxShapes.data(), filter, staticOnly); + if (shapeCount == 0) + return 0; + + // Cache bounds for all shapes + for (int i = 0; i < shapeCount; ++i) + { + GetAABB( aabbs[i], *nxShapes[i] ); + } + return shapeCount; +} + +size_t PhysXRaycast::BatchIntersect( const dynamic_array<BatchedRaycast>& raycasts, dynamic_array<BatchedRaycastResult>& results, const UInt32 filter, bool staticOnly ) const +{ + // Get new aabb for particle system + MinMaxAABB aabb = ComputeBatchAABB(raycasts); + aabb.Expand(kAABBEpsilon); + + // Get all shapes within bounds (aabb) + dynamic_array<NxShape*> nxShapes(kMaxParticleCollisionShapes, kMemTempAlloc); + dynamic_array<AABB> aabbs(kMaxParticleCollisionShapes, kMemTempAlloc); + + size_t shapeCount = BroadPhaseCulling( nxShapes, aabbs, aabb, filter, staticOnly ); + + if (shapeCount == 0) + return 0; + + // trace rays against the shapes + size_t numIntersections = 0; + for (size_t r = 0; r < raycasts.size(); r++) + { + const BatchedRaycast pointsRay = raycasts[r]; + + // attempt to cull based on ray extent + if ( !IntersectAny( pointsRay, aabbs.data(), shapeCount ) ) continue; + + // build actual ray - this is relatively expensive due to Magnitude that has a sqrt and a dot product + // TODO: possibly do 4 rays at a time in SIMD + const Vector3f displacement = pointsRay.to-pointsRay.from; + float distance = Magnitude(displacement); + if (distance <= kEpsilon) + continue; + const Vector3f direction = displacement / distance; + const Ray ray(pointsRay.from, direction); + + // intersect the shapes with the ray + HitInfo hit; + const bool isHit = Intersect(ray, distance, nxShapes.data(), aabbs.data(), shapeCount, hit); + if ( isHit ) + { + results[numIntersections] = BatchedRaycastResult(r,hit); + numIntersections++; + } + } + + return numIntersections; +} + +// Perform PhysX broad phase culling +size_t PhysXRaycast::GetShapes( const AABB& bounds, int maxResult, NxShape** outShapes, UInt32 groupMask, bool staticOnly ) const +{ + Vector3f min = bounds.GetMin(); + Vector3f max = bounds.GetMax(); + + NxBounds3 physicsBounds; + physicsBounds.set((const NxVec3&)min, (const NxVec3&)max); + + return GetDynamicsScene().overlapAABBShapes (physicsBounds, ( staticOnly ? NX_STATIC_SHAPES : NX_ALL_SHAPES ), maxResult, outShapes, NULL, groupMask, NULL, false); +} + +void PhysXRaycast::GetAABB( AABB& bounds, const NxShape& shape ) const +{ + NxBounds3 physicsBounds; + shape.getWorldBounds(physicsBounds); + NxVec3 center, extents; + physicsBounds.getCenter(center); + physicsBounds.getExtents(extents); + bounds = AABB((const Vector3f&)center, (const Vector3f&)extents); + bounds.Expand(kAABBEpsilon); +} + +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/PhysXRaycast.h b/Runtime/Dynamics/PhysXRaycast.h new file mode 100644 index 0000000..298f051 --- /dev/null +++ b/Runtime/Dynamics/PhysXRaycast.h @@ -0,0 +1,42 @@ +#ifndef PHYSXRAYCAST_H +#define PHYSXRAYCAST_H + +#include "UnityPrefix.h" +#if ENABLE_MULTITHREADED_CODE +#include "Runtime/Threads/SimpleLock.h" +#endif +#include "Runtime/Interfaces/IRaycast.h" +#include "Runtime/Geometry/AABB.h" + +class Plane; +class NxShape; + +/// PhysX thread safety: +/// Unfortunately scene query + concurrent writes to the SDK are not thread safe. However, concurrent reads such as scene queries from multiple threads should be safe. +/// But simulate calls count as writes, so is not OK at the same time. +class PhysXRaycast : public IRaycast +{ +public: + static void InitializeClass (); + static void CleanupClass (); + + // virtual interface implementation + virtual size_t BatchIntersect( const dynamic_array<BatchedRaycast>& raycasts, dynamic_array<BatchedRaycastResult>& results, const UInt32 filter, bool staticOnly ) const; + virtual bool Raycast (const Ray& ray, float distance, int mask, HitInfo& hit); + + PhysXRaycast() {} + ~PhysXRaycast() {} + +private: + // helper functions for working with the NxShapes + bool Intersect( const Ray& ray, float maxDist, NxShape** shapes, AABB* shapeBounds, size_t shapeCount, HitInfo& hit ) const; + size_t GetShapes( const AABB& bounds, int maxResult, NxShape** outShapes, UInt32 groupMask = 0xffffffff, bool staticOnly = false ) const; + void GetAABB( AABB& bounds, const NxShape& shape ) const; + size_t BroadPhaseCulling(dynamic_array<NxShape*>& nxShapes, dynamic_array<AABB>& aabbs, const MinMaxAABB& particleSystemAABB, const int filter, bool staticOnly) const; + +#if ENABLE_MULTITHREADED_CODE + mutable SimpleLock m_Lock; +#endif +}; + +#endif diff --git a/Runtime/Dynamics/PhysicMaterial.cpp b/Runtime/Dynamics/PhysicMaterial.cpp new file mode 100644 index 0000000..6077d46 --- /dev/null +++ b/Runtime/Dynamics/PhysicMaterial.cpp @@ -0,0 +1,340 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "PhysicMaterial.h" +#include "PhysicsManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h" +#include "Runtime/Misc/ResourceManager.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/Misc/BuildSettings.h" + +enum MonoBounceCombineMode { + kMonoAverage = 0, + kMonoMultiply = 1, + kMonoMinimum = 2, + kMonoMaximum = 3, +}; + +static int MonoCombineModeToNxCombineMode(int monoMode) +{ + if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1)) { + return monoMode; + } + + switch (monoMode) { + case kMonoAverage: return NX_CM_AVERAGE; + case kMonoMultiply: return NX_CM_MULTIPLY; + case kMonoMinimum: return NX_CM_MIN; + case kMonoMaximum: return NX_CM_MAX; + } + + return 0; +} + +static int NxCombineModeToMonoCombineMode(int nxMode) +{ + if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1)) { + return nxMode; + } + + switch (nxMode) { + case NX_CM_AVERAGE: return kMonoAverage; + case NX_CM_MULTIPLY: return kMonoMultiply; + case NX_CM_MIN: return kMonoMinimum; + case NX_CM_MAX: return kMonoMaximum; + } + + return 0; +} + +PhysicMaterial::PhysicMaterial (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + m_Material = NULL; + m_MaterialIndex = -1; +} + +PhysicMaterial::~PhysicMaterial () +{ + MainThreadCleanup (); +} + +bool PhysicMaterial::MainThreadCleanup () +{ + if (m_Material) + { + GetDynamicsScene ().releaseMaterial (*m_Material); + m_Material = NULL; + } + + return true; +} + + +void PhysicMaterial::InitializeClass () +{ + RegisterAllowNameConversion(PhysicMaterial::GetClassStringStatic(), "bouncyness", "bounciness"); +} +void PhysicMaterial::Reset () +{ + Super::Reset(); + m_FrictionCombine = 0; + m_BounceCombine = 0; + m_DynamicFriction = 0.4f; + m_StaticFriction = 0.4f; + m_Bounciness = 0; + m_DynamicFriction2 = 0; + m_StaticFriction2 = 0; + m_FrictionDirection2 = Vector3f::zero; +} + +bool PhysicMaterial::IsDefaultMaterial () +{ + return GetPhysicsManager ().m_CachedDefaultMaterial == this; +} + +template<class TransferFunction> +void PhysicMaterial::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + + transfer.Transfer (m_DynamicFriction, "dynamicFriction", kSimpleEditorMask); + transfer.Transfer (m_StaticFriction, "staticFriction", kSimpleEditorMask); + transfer.Transfer (m_Bounciness, "bounciness", kSimpleEditorMask); + + transfer.Transfer (m_FrictionCombine, "frictionCombine"); + transfer.Transfer (m_BounceCombine, "bounceCombine"); + + transfer.Transfer (m_FrictionDirection2, "frictionDirection2"); + transfer.Transfer (m_DynamicFriction2, "dynamicFriction2"); + transfer.Transfer (m_StaticFriction2, "staticFriction2"); +} + +void PhysicMaterial::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + + NxMaterialDesc representation; + representation.dynamicFriction = m_DynamicFriction; + representation.staticFriction = m_StaticFriction; + representation.restitution = clamp<float>(m_Bounciness, 0.0f, 1.0f); + representation.frictionCombineMode = (NxCombineMode)m_FrictionCombine; + representation.restitutionCombineMode = (NxCombineMode)m_BounceCombine; + representation.dynamicFrictionV = m_DynamicFriction2; + representation.staticFrictionV = m_StaticFriction2; + + if (m_Material == NULL) + { + m_Material = GetDynamicsScene ().createMaterial (representation); + m_MaterialIndex = m_Material->getMaterialIndex (); + } + else + m_Material->loadFromDesc (representation); + + UpdateFrictionDirection2 (); + + if (IsDefaultMaterial ()) + CopyMaterialToDefault (); +} + +float PhysicMaterial::GetDynamicFriction () const +{ + return m_DynamicFriction; +} + +void PhysicMaterial::SetDynamicFriction (float friction) +{ + if (friction < 0) + { + ErrorString( Format("Physics material %s cannot have dynamicFriction = %f", GetName(), friction) ); + friction = 0.0f; + } + + m_DynamicFriction = friction; + m_Material->setDynamicFriction (friction); + SetDirty (); +} + +float PhysicMaterial::GetStaticFriction () const +{ + return m_StaticFriction; +} + +void PhysicMaterial::SetStaticFriction (float friction) +{ + if (friction < 0) + { + ErrorString( Format("Physics material %s cannot have staticFriction = %f", GetName(), friction) ); + friction = 0.0f; + } + + m_StaticFriction = friction; + m_Material->setStaticFriction (friction); + SetDirty (); +} + +float PhysicMaterial::GetBounciness () const +{ + return m_Bounciness; +} + +Vector3f PhysicMaterial::GetFrictionDirection2 () const +{ + return m_FrictionDirection2; +} + +float PhysicMaterial::GetStaticFriction2 () const +{ + return m_StaticFriction2; +} + +float PhysicMaterial::GetDynamicFriction2 () const +{ + return m_DynamicFriction2; +} + +int PhysicMaterial::GetFrictionCombine () +{ + return NxCombineModeToMonoCombineMode(m_FrictionCombine); +} + +int PhysicMaterial::GetBounceCombine () +{ + return NxCombineModeToMonoCombineMode(m_BounceCombine); +} + +void PhysicMaterial::ChangedMaterial () +{ + if (IsDefaultMaterial ()) + CopyMaterialToDefault (); + SetDirty (); +} + +void PhysicMaterial::SetBounciness (float bounce) +{ + if (bounce < 0 || bounce > 1) + { + ErrorString( Format("Physics material %s cannot have bounciness = %f", GetName(), bounce) ); + bounce = clamp(bounce, 0.0f, 1.0f); + } + + m_Bounciness = bounce; + m_Material->setRestitution (bounce); + ChangedMaterial (); +} + +void PhysicMaterial::UpdateFrictionDirection2 () +{ + Vector3f dir = m_FrictionDirection2; + float magnitude = Magnitude (dir); + if (magnitude < 0.1F) + { + m_Material->setDirOfAnisotropy (NxVec3 (0,0,0)); + m_Material->setFlags (m_Material->getFlags () & (~NX_MF_ANISOTROPIC)); + } + else + { + dir /= magnitude; + m_Material->setDirOfAnisotropy ((NxVec3&)dir); + m_Material->setFlags (m_Material->getFlags () | NX_MF_ANISOTROPIC); + } + +} + +void PhysicMaterial::SetFrictionDirection2 (Vector3f dir) +{ + m_FrictionDirection2 = dir; + UpdateFrictionDirection2 (); + ChangedMaterial (); +} + +void PhysicMaterial::SetDynamicFriction2 (float fric) +{ + if (fric < 0) + { + ErrorString( Format("Physics material %s cannot have dynamicFriction2 = %f", GetName(), fric) ); + fric = 0.0f; + } + + m_DynamicFriction2 = fric; + m_Material->setDynamicFrictionV (fric); + ChangedMaterial (); +} + +void PhysicMaterial::SetStaticFriction2 (float fric) +{ + if (fric < 0) + { + ErrorString( Format("Physics material %s cannot have staticFriction2 = %f", GetName(), fric) ); + fric = 0.0f; + } + + m_StaticFriction2 = fric; + m_Material->setStaticFrictionV (fric); + ChangedMaterial (); +} + +void PhysicMaterial::SetFrictionCombine (int mode) +{ + m_FrictionCombine = MonoCombineModeToNxCombineMode(mode); + m_Material->setFrictionCombineMode ((NxCombineMode)m_FrictionCombine); + ChangedMaterial (); +} + +void PhysicMaterial::SetBounceCombine (int mode) +{ + m_BounceCombine = MonoCombineModeToNxCombineMode(mode); + m_Material->setRestitutionCombineMode ((NxCombineMode)m_BounceCombine); + ChangedMaterial (); +} + +void PhysicMaterial::CopyMaterialToDefault () const +{ + NxMaterialDesc desc; + m_Material->saveToDesc (desc); + GetDynamicsScene ().getMaterialFromIndex (0)->loadFromDesc (desc); +} + +PhysicMaterial& PhysicMaterial::GetInstantiatedMaterial (PhysicMaterial* material, Object& owner) +{ + if (material) + { + if (material->m_Owner == PPtr<Object> (&owner)) + return const_cast<PhysicMaterial&> (*material); + else + { + PhysicMaterial* instance = NEW_OBJECT(PhysicMaterial); + instance->Reset (); + + instance->SetNameCpp (Append (material->GetName (), " (Instance)")); + instance->m_FrictionCombine = material->m_FrictionCombine; + instance->m_BounceCombine = material->m_BounceCombine; + instance->m_DynamicFriction = material->m_DynamicFriction; + instance->m_StaticFriction = material->m_StaticFriction; + instance->m_Bounciness = material->m_Bounciness; + instance->m_DynamicFriction2 = material->m_DynamicFriction2; + instance->m_StaticFriction2 = material->m_StaticFriction2; + instance->m_FrictionDirection2 = material->m_FrictionDirection2; + instance->m_Owner = &owner; + + instance->AwakeFromLoad (kDefaultAwakeFromLoad); + return *instance; + } + } + else + { + PhysicMaterial* instance = NEW_OBJECT (PhysicMaterial); + instance->Reset (); + + instance->SetName ("Default (Instance)"); + instance->m_Owner = &owner; + + instance->AwakeFromLoad (kDefaultAwakeFromLoad); + return *instance; + } +} + +IMPLEMENT_CLASS_HAS_INIT (PhysicMaterial) +IMPLEMENT_OBJECT_SERIALIZE (PhysicMaterial) +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/PhysicMaterial.h b/Runtime/Dynamics/PhysicMaterial.h new file mode 100644 index 0000000..b31cded --- /dev/null +++ b/Runtime/Dynamics/PhysicMaterial.h @@ -0,0 +1,99 @@ +#ifndef DYNAMICSMATERIAL_H +#define DYNAMICSMATERIAL_H + +#if ENABLE_PHYSICS +#include "Runtime/BaseClasses/NamedObject.h" +#include "Runtime/Math/Vector3.h" +#include "JointDescriptions.h" + +class NxMaterial; + +class PhysicMaterial : public NamedObject +{ + public: + + REGISTER_DERIVED_CLASS (PhysicMaterial, NamedObject) + DECLARE_OBJECT_SERIALIZE (PhysicMaterial) + + PhysicMaterial (MemLabelId label, ObjectCreationMode mode); + // ~PhysicMaterial (); declared-by-macro + + virtual bool MainThreadCleanup (); + + static void InitializeClass (); + static void CleanupClass () {} + + float GetDynamicFriction () const; + void SetDynamicFriction (float friction); + + float GetStaticFriction () const; + void SetStaticFriction (float friction); + + void SetBounciness (float bounce); + float GetBounciness () const; + + void SetFrictionDirection2 (Vector3f dir); + Vector3f GetFrictionDirection2 () const; + + float GetStaticFriction2 () const; + void SetStaticFriction2 (float fric); + + float GetDynamicFriction2 () const; + void SetDynamicFriction2 (float fric); + + void SetSpring (const JointSpring& spring); + JointSpring GetSpring () const; + + void SetUseSpring (bool use); + bool GetUseSpring () const; + + int GetMaterialIndex () const { Assert(m_MaterialIndex > 0); return m_MaterialIndex; } + + virtual void Reset (); + + static PhysicMaterial& GetInstantiatedMaterial (PhysicMaterial* material, Object& owner); + + int GetFrictionCombine (); + void SetFrictionCombine (int mode); + + int GetBounceCombine (); + void SetBounceCombine (int mode); + + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + private: + + void UpdateFrictionDirection2 (); + Vector3f m_FrictionDirection2;///< Friction + int m_FrictionCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 } + int m_BounceCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 } + float m_DynamicFriction;///< range { 0, 1 } + float m_StaticFriction;///< range { 0, infinity } + float m_Bounciness;///< range { 0, 1 } + float m_DynamicFriction2;///< range { 0, 1 } + float m_StaticFriction2;///< range { 0, infinity } + +#if DOXYGEN + Vector3f frictionDirection2;///< Friction + int frictionCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 } + int bounceCombine;///< enum { Average = 0, Minimum = 1, Multiply = 2, Maximum = 3 } + float dynamicFriction;///< range { 0, 1 } + float staticFriction;///< range { 0, infinity } + float bounciness;///< range { 0, 1 } + float dynamicFriction2;///< range { 0, 1 } + float staticFriction2;///< range { 0, infinity } +#endif + + void CopyMaterialToDefault () const; + bool IsDefaultMaterial (); + void ChangedMaterial (); + + int m_MaterialIndex; + NxMaterial* m_Material; + PPtr<Object> m_Owner; + + friend class PhysicsManager; +}; + +#endif //ENABLE_PHYSICS +#endif diff --git a/Runtime/Dynamics/PhysicsManager.cpp b/Runtime/Dynamics/PhysicsManager.cpp new file mode 100644 index 0000000..1ba62fe --- /dev/null +++ b/Runtime/Dynamics/PhysicsManager.cpp @@ -0,0 +1,1508 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "PhysicsManager.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Input/TimeManager.h" +#include "RigidBody.h" +#include "Collider.h" +#include "Joint.h" +#include "PhysicMaterial.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/BaseClasses/MessageHandler.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxIntersectionBoxBox.h" +#include "Runtime/BaseClasses/Tags.h" +#include "Runtime/Utilities/Utility.h" +#include "NxWrapperUtility.h" +#include "Runtime/Misc/Allocator.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/BaseClasses/Tags.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Misc/SystemInfo.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h" +#include "Cloth.h" +#include "SkinnedCloth.h" +#include "CharacterController.h" +#include "PhysXRaycast.h" +#include "Runtime/Profiler/ProfilerStats.h" +#include "Runtime/Allocator/MemoryMacros.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/ScriptingManager.h" +#include "Runtime/Scripting/ScriptingExportUtility.h" +#include "Runtime/BaseClasses/SupportedMessageOptimization.h" +#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h" +#include "Runtime/Core/Callbacks/GlobalCallbacks.h" +#include "PhysicsModule.h" + +#if UNITY_PS3 +#include "PS3/NxCellConfiguration.h" + +extern uint8_t g_aPhysXPriorities[8]; +extern CellSpurs2* g_pSpursInstance; + +#endif + +inline void VerifyObjectPtr(Object* obj) +{ + #if !UNITY_RELEASE + if (obj == NULL) + return; + // OutputDebugString is windows/360 only! + //if (Object::IDToPointer(obj->GetInstanceID()) != obj) + // OutputDebugString(Format("%x\n", obj).c_str()); + Assert(Object::IDToPointer(obj->GetInstanceID()) == obj); + #endif +} + +using namespace std; +using namespace Unity; + +PROFILER_INFORMATION(gRaycastProfile, "Physics.Raycast", kProfilerPhysics) +PROFILER_INFORMATION(gRaycastAllProfile, "Physics.RaycastAll", kProfilerPhysics) +PROFILER_INFORMATION(gCapsuleTestProfile, "Physics.CheckCapsule", kProfilerPhysics) +PROFILER_INFORMATION(gSphereOverlapProfile, "Physics.OverlapSphere", kProfilerPhysics) +PROFILER_INFORMATION(gSphereTestProfile, "Physics.TestSphere", kProfilerPhysics) +PROFILER_INFORMATION(gCapsuleCastProfile, "Physics.CapsuleCast", kProfilerPhysics) +PROFILER_INFORMATION(gCapsuleCastAllProfile, "Physics.CapsuleCastAll", kProfilerPhysics) + + +#define RETURNIFNOPHYSX( v ) if(!gPhysicsSDK) return v; +#define RETURNIFNOPHYSX_VOID() if(!gPhysicsSDK) return; + + +static Vector3f CalculateRelativeVelocity (NxActor& a, NxActor& b); + +#define DEBUG_VISUALIZE_PHYSX 0 + +enum +{ +#if UNITY_WII + kNovodexMemoryAlignment = 8 +#else + kNovodexMemoryAlignment = kDefaultMemoryAlignment +#endif +}; + +class NovodexAllocator : public NxUserAllocator +{ + public: + + void* malloc(size_t size, NxMemoryType type) + { + return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment); + } + + void* malloc(size_t size) + { + return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment); + } + + void* mallocDEBUG(size_t size, const char* fileName, int line, const char* className, NxMemoryType type) + { + return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment); + return 0; + } + void* mallocDEBUG(size_t size, const char* fileName, int line) + { + return UNITY_MALLOC_ALIGNED(kMemPhysics, size, kNovodexMemoryAlignment); + return 0; + } + + void* realloc(void* memory, size_t size) + { + return UNITY_REALLOC_ALIGNED(kMemPhysics, memory, size, kNovodexMemoryAlignment); + } + + void free(void* memory) + { + UNITY_FREE(kMemPhysics, memory); + } + +/* check is a bad name to choose. The MacOSX libraries already Carbon declare that function name. + void check() + { + } +*/ +}; + + +struct ErrorStream : public NxUserOutputStream +{ + virtual void reportError(NxErrorCode code, const char * message, const char *file, int line) + { + if (code == NXE_DB_WARNING) + return; + + DebugStringToFile (message, 0, file, line, kError); + } + /** + Reports an assertion violation. The user should return + */ + virtual NxAssertResponse reportAssertViolation(const char * message, const char *file, int line) + { + DebugStringToFile (message, 0, file, line, kAssert); + return NX_AR_CONTINUE; + } + /** + Simply prints some debug text + */ + virtual void print(const char * message) + { + LogString (message); + } +}; + +static void CreateDynamicsScene (); + +/* +struct CollisionData +{ + +}; +*/ + +class RaycastCollector : public NxUserRaycastReport +{ + public: + PhysicsManager::RaycastHits* hits; + + virtual bool onHit(const NxRaycastHit& hit) + { + RaycastHit outHit; + NxToRaycastHit(hit, outHit); + hits->push_back(outHit); + return true; + } +}; + + +class TriggerMessage : public NxUserTriggerReport +{ + public: + PhysicsManager::RecordedTriggers* record; + + virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status) + { + Collider* trigger = (Collider*)triggerShape.userData; + Collider* collider = (Collider*)otherShape.userData; + record->push_back (PhysicsManager::RecordedTrigger ()); + record->back ().trigger = trigger; + record->back ().collider = collider; + record->back ().status = status; + } +}; + +static Vector3f CalculateRelativeVelocity (NxActor& a, NxActor& b) +{ + NxVec3 vel0, vel1; + if (a.isDynamic ()) + vel0 = a.getLinearVelocity (); + else + vel0.zero (); + + if (b.isDynamic ()) + vel1 = b.getLinearVelocity (); + else + vel1.zero (); + vel0 -= vel1; + + return (const Vector3f&)vel0; +} + +class ContactMessage : public NxUserContactReport +{ + public: + PhysicsManager::RecordedContacts* record; + + virtual void onContactNotify (NxContactPair& pair, NxU32 status) + { + + Rigidbody* thisRigidbody = (Rigidbody*)pair.actors[0]->userData; + Rigidbody* otherRigidbody = (Rigidbody*)pair.actors[1]->userData; + + Collider* thisCollider = NULL; + if (pair.actors[0]->getNbShapes()) + thisCollider = (Collider*)pair.actors[0]->getShapes ()[0]->userData; + + Collider* otherCollider = NULL; + if (pair.actors[1]->getNbShapes()) + otherCollider = (Collider*)pair.actors[1]->getShapes ()[0]->userData; + + record->push_back (Collision ()); + record->back ().thisRigidbody = thisRigidbody; + record->back ().otherRigidbody = otherRigidbody; + record->back ().thisCollider = thisCollider; + record->back ().otherCollider = otherCollider; + record->back ().status = status; + record->back ().impactForceSum = (const Vector3f&)pair.sumNormalForce; + record->back ().frictionForceSum = (const Vector3f&)pair.sumFrictionForce; + record->back ().relativeVelocity = CalculateRelativeVelocity (*pair.actors[0], *pair.actors[1]); + + VerifyObjectPtr(record->back ().thisRigidbody); + VerifyObjectPtr(record->back ().otherRigidbody); + VerifyObjectPtr(record->back ().thisCollider); + VerifyObjectPtr(record->back ().otherCollider); + + std::list<ContactPoint>& contacts = record->back ().contacts; + NxContactStreamIterator i (pair.stream); + + //user can call getNumPairs() here + while(i.goNextPair()) + { + Collider* c0 = i.isDeletedShape(0) ? NULL : (Collider*)i.getShape (0)->userData; + Collider* c1 = i.isDeletedShape(1) ? NULL : (Collider*)i.getShape (1)->userData; + //user can also call getShape() and getNumPatches() here + while(i.goNextPatch()) + { + Vector3f normal = (const Vector3f&)i.getPatchNormal (); + //user can also call getPatchNormal() and getNumPoints() here + while(i.goNextPoint()) + { + ContactPoint c; + c.collider[0] = c0; + c.collider[1] = c1; + c.point = (const Vector3f&)i.getPoint (); + c.normal = normal; + contacts.push_back (c); + } + } + } + } +}; + +class BrokenJointMessage : public NxUserNotify +{ + public: + PhysicsManager::RecordedJointBreaks* records; + + virtual bool onJointBreak (NxReal breakingForce, NxJoint & brokenJoint) + { + Joint* joint = (Joint*)brokenJoint.userData; + joint->NullJoint(); + PhysicsManager::RecordedJointBreak jointbreak; + jointbreak.impulse = breakingForce; + jointbreak.joint = joint; + records->push_back(jointbreak); + return true; + } + + virtual void onWake(NxActor** actors, NxU32 count) {} + virtual void onSleep(NxActor** actors, NxU32 count) { } +}; + +static ErrorStream gErrorStream; +static NovodexAllocator gAllocator; +static NxPhysicsSDK* gPhysicsSDK = NULL; +static NxScene* gPhysicsScene = NULL; +static std::vector<NxScene*> gClothingScenes; +static NxScene* gInactivePhysicsScene = NULL; +static NxActor* gNULLActor = NULL; + +void PhysicsManager::InitializeClass () +{ + RegisterAllowNameConversion (PhysicsManager::GetClassStringStatic(), "m_BounceTreshold", "m_BounceThreshold"); + + PhysXRaycast::InitializeClass(); + InitializePhysicsModule(); + + gPhysicsSDK = NxCreatePhysicsSDK (NX_PHYSICS_SDK_VERSION, &gAllocator, &gErrorStream); + if (!gPhysicsSDK) + FatalErrorString ("Couldn't load physics"); + if (!NxInitCooking (0, &gErrorStream)) + FatalErrorString ("Couldn't load physics "); + +#if UNITY_PS3 + NxCellSpursControl::initWithSpurs(g_pSpursInstance,5, g_aPhysXPriorities); +#endif + +#if DEBUG_VISUALIZE_PHYSX + gPhysicsSDK->setParameter (NX_VISUALIZATION_SCALE, 1F); + gPhysicsSDK->setParameter (NX_VISUALIZE_COLLISION_SHAPES, 1F); +// gPhysicsSDK->setParameter (NX_VISUALIZE_COLLISION_COMPOUNDS, 1F); +#endif + + gPhysicsSDK->setParameter(NX_IMPROVED_SPRING_SOLVER, 0); + + CreateDynamicsScene (); + CharacterController::CreateControllerManager (); + + REGISTER_PLAYERLOOP_CALL (PhysicsFixedUpdate, GetPhysicsManager().FixedUpdate()); + REGISTER_PLAYERLOOP_CALL (PhysicsUpdate, GetPhysicsManager().Update ()); + REGISTER_PLAYERLOOP_CALL (PhysicsRefreshWhenPaused, GetPhysicsManager().RefreshWhenPaused ()); + REGISTER_PLAYERLOOP_CALL (PhysicsSkinnedClothUpdate, GetPhysicsManager().SkinnedClothUpdate ()); + REGISTER_PLAYERLOOP_CALL (PhysicsResetInterpolatedTransformPosition, GetPhysicsManager().ResetInterpolatedTransformPosition ()); + + REGISTER_GLOBAL_CALLBACK (didUnloadScene, GetPhysicsManager().RecreateScene()); +} + + +// If we use multiple scenes per CPU, we may get better parallelization, when scene load becomes +// unbalances because cloth objects get disabled or destroyed. +// Apparently, PhysX doesn't handle more then 64 scenes - and we use two for rigidbody physics. + +#if (UNITY_XENON || UNITY_PS3) +# define NUMBER_OF_CLOTHING_SCENES_PER_CPU 1 +# define MAX_CLOTHING_SCENES 6 +#else +# define NUMBER_OF_CLOTHING_SCENES_PER_CPU 4 +# define MAX_CLOTHING_SCENES 32 +#endif + +static void CreateDynamicsScene () +{ + AssertIf (gPhysicsScene != NULL); + NxSceneDesc sceneDesc; + // Multi threading causes issues when simulating 1000 objects at the same location. + // We are not taking advantage of it anyway. So... + // Test is in exception unit test on rudolph + sceneDesc.flags &= ~NX_SF_SIMULATE_SEPARATE_THREAD; + + if (gInactivePhysicsScene == NULL) + gInactivePhysicsScene = gPhysicsSDK->createScene(sceneDesc); + + + gPhysicsScene = gPhysicsSDK->createScene(sceneDesc); + gPhysicsScene->setGravity (NxVec3 (0.0F, -9.81F, 0.0F)); + gPhysicsScene->setTiming (1.0F, 8, NX_TIMESTEP_VARIABLE); +//broken? +#if !UNITY_WINRT + if (systeminfo::GetProcessorCount() > 1) + { + int numClothingScenes = std::min(NUMBER_OF_CLOTHING_SCENES_PER_CPU * systeminfo::GetProcessorCount(), MAX_CLOTHING_SCENES); + gClothingScenes.resize (numClothingScenes); + } + else +#endif + gClothingScenes.resize (1); + for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++) + *i = NULL; + + // Setup actor group masks! + // We put every actor into a group depending on what contact notifications it wants. + // This way we don't send too many contact notify messages! + int masks[3] = { + 0, + NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_END_TOUCH, + NX_NOTIFY_ON_TOUCH | NX_NOTIFY_ON_START_TOUCH | NX_NOTIFY_ON_END_TOUCH + }; + + for (int i=0;i<3;i++) + { + for (int j=0;j<3;j++) + GetDynamicsScene ().setActorGroupPairFlags (i, j, masks[i] | masks[j]); + } + + GetDynamicsScene ().setGroupCollisionFlag (kIgnoreCollisionLayer, kIgnoreCollisionLayer, 0); + + // Create hole material + NxMaterialDesc holematerial; + NxMaterial* material = GetDynamicsScene ().createMaterial (holematerial); + AssertIf(material->getMaterialIndex() != 1); + + // Create wheel collider material - used by wheel collider + NxMaterialDesc wheelMaterial; + wheelMaterial.flags |= NX_MF_DISABLE_FRICTION; + + material = GetDynamicsScene ().createMaterial (wheelMaterial); + AssertIf(material->getMaterialIndex() != 2); +} + +void PhysicsManager::CleanupReports() +{ + // is there any point doing these allocations? + + TriggerMessage* trigger = (TriggerMessage*)gPhysicsScene->getUserTriggerReport(); + gPhysicsScene->setUserTriggerReport(NULL); + delete trigger; + + ContactMessage* contact = (ContactMessage*)gPhysicsScene->getUserContactReport(); + gPhysicsScene->setUserContactReport(NULL); + delete contact; + + BrokenJointMessage* notify = (BrokenJointMessage*)gPhysicsScene->getUserNotify(); + gPhysicsScene->setUserNotify(NULL); + delete notify; +} + +void PhysicsManager::CreateReports() +{ + CleanupReports(); + + TriggerMessage* triggerMessage = new TriggerMessage (); + triggerMessage->record = &m_RecordedTriggers; + gPhysicsScene->setUserTriggerReport (triggerMessage); + + ContactMessage* contactMessage = new ContactMessage (); + contactMessage->record = &m_RecordedContacts; + gPhysicsScene->setUserContactReport (contactMessage); + + BrokenJointMessage* brokenJoint = new BrokenJointMessage (); + brokenJoint->records = &m_RecordedJointBreaks; + gPhysicsScene->setUserNotify (brokenJoint); +} + +void PhysicsManager::CleanupClass () +{ + PhysXRaycast::CleanupClass(); + CleanupPhysicsModule(); + + if( !gPhysicsSDK ) + return; // happens when user quits the screen selector + + // Needs to be cleaned up before the physics scene, or we get a crash, if there + // are still controllers around (as is the case in the editor, which does not clean up all objects). +#if ENABLE_PHYSICS + CharacterController::CleanupControllerManager (); +#endif + + vector<PhysicMaterial*> materialsTemp; + vector<NxMaterialDesc> materialDescsTemp; + ReleaseMaterials(materialsTemp, materialDescsTemp); + + CleanupReports(); + + gPhysicsSDK->releaseScene (*gPhysicsScene); + gPhysicsSDK->releaseScene (*gInactivePhysicsScene); + + for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++) + { + if (*i != NULL) + { + gPhysicsSDK->releaseScene (**i); + *i = NULL; + } + } + std::vector<NxScene*> emptyScenes; + gClothingScenes.swap(emptyScenes); + + // We are not using NxReleasePhysicsSDK, because it doesn't release the sdk (a bug in our implementation of USE_STATIC_LIBS) + // it's fine to call the release on gPhysicsSDK directly, except that it doesn't set g_nxPhysicsSDK in PhysX to NULL + // NxReleasePhysicsSDK(gPhysicsSDK); + + NxFoundationSDK* foundationSDK = NxGetFoundationSDK (); + gPhysicsSDK->release (); + gPhysicsSDK = NULL; + + foundationSDK->release(); + +#if UNITY_PS3 + if(NxCellSpursControl::isSpursInitialized()) + CELLCALL(NxCellSpursControl::terminate()); +#endif +} + + +PhysicsManager::PhysicsManager (MemLabelId label, ObjectCreationMode mode) + : Super(label, mode) +{ + m_RaycastsHitTriggers = true; + + m_Gravity = Vector3f (0, -9.81F, 0.0F); + m_LayerCollisionMatrix.resize (kNumLayers, 0xffffffff); + m_DefaultIterationCount = 6; + m_RigidbodyTransformMessageEnabled = true; + m_SmoothedClothDeltaTime = 0.0f; + + RETURNIFNOPHYSX_VOID(); + + GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, 0.01F); + GetDynamicsSDK ().setParameter (NX_CONTINUOUS_CD, true); + GetDynamicsSDK ().setParameter (NX_CCD_EPSILON, 0.01F); + + SetupDefaultMaterial (); + CreateReports(); + Object::FindAllDerivedClasses (ClassID (Collider), &m_DisableTransformMessage); +} + +void PhysicsManager::Reset () +{ + RETURNIFNOPHYSX_VOID(); + + Super::Reset(); + + m_Gravity = Vector3f (0, -9.81F, 0.0F); + m_LayerCollisionMatrix.resize (kNumLayers, 0xffffffff); + m_DefaultIterationCount = 6; + GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, 0.01F); + + +} + +PhysicsManager::~PhysicsManager () +{ + CleanupReports(); + // delete objects after setting them to null on novodex - just in case +} + +inline Unity::Component* AttachedRigidbodyOrCollider (Collider& collider) +{ + Rigidbody* body = collider.GetRigidbody (); + if (body) + return body; + else + return &collider; +} + + +inline Unity::Component* GetRigidbodyOrCollider (Collision& col) +{ + return col.thisRigidbody != NULL ? (Unity::Component*)col.thisRigidbody : (Unity::Component*)col.thisCollider; +} + +inline Unity::Component* GetOtherRigidbodyOrCollider (Collision& col) +{ + return col.otherRigidbody != NULL ? (Unity::Component*)col.otherRigidbody : (Unity::Component*)col.otherCollider; +} + +void PhysicsManager::SetTransformMessageEnabled(bool enable) +{ + // Disable rigid body / collider transform changed message handler + // This simply avoids setting the position/rotation in the rigidbody while we are fetching the novodex state. + for (int i=0;i<m_DisableTransformMessage.size ();i++) + { + GameObject::GetMessageHandler ().SetMessageEnabled (m_DisableTransformMessage[i], kTransformChanged.messageID, enable); + } + + m_RigidbodyTransformMessageEnabled = enable; +} + +void PhysicsManager::ReleaseMaterials(vector<PhysicMaterial*>& materials, vector<NxMaterialDesc>& materialDescs) +{ + materials.clear(); + Object::FindObjectsOfType(&materials); + + materialDescs.resize(materials.size()); + + for (int i=0;i<materials.size();i++) + { + materials[i]->m_Material->saveToDesc(materialDescs[i]); + GetDynamicsScene ().releaseMaterial (*materials[i]->m_Material); + materials[i]->m_Material = NULL; + } +} + +void PhysicsManager::RecreateScene () +{ + RETURNIFNOPHYSX_VOID(); + + if (GetDynamicsScene().getNbActors() == 0 && GetDynamicsScene().getNbStaticShapes() == 0 && GetDynamicsScene().getNbJoints() == 0) + { + vector<PhysicMaterial*> materials; + vector<NxMaterialDesc> materialDescs; + ReleaseMaterials(materials, materialDescs); + + CleanupReports(); + for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!=gClothingScenes.end(); i++) + { + if (*i != NULL) + { + gPhysicsSDK->releaseScene (**i); + *i = NULL; + } + } + gPhysicsSDK->releaseScene (*gPhysicsScene); + gPhysicsScene = NULL; + CreateDynamicsScene (); + CreateReports(); + + for (int i=0;i<materials.size();i++) + { + NxMaterial* material = GetDynamicsScene ().createMaterial (materialDescs[i]); + + if (material) + { + materials[i]->m_MaterialIndex = material->getMaterialIndex (); + } + else + { + ErrorString(std::string("Invalid physics material ") + materials[i]->GetName()); + materials[i]->m_MaterialIndex = 0; + } + + materials[i]->m_Material = material; + + materials[i]->AwakeFromLoad (kDefaultAwakeFromLoad); + } + + GetPhysicsManager().AwakeFromLoad(kDefaultAwakeFromLoad); + } +} + +PROFILER_INFORMATION(gPhysicsProfile, "Physics.Simulate", kProfilerPhysics) +PROFILER_INFORMATION(gPhysicsClothProfile, "Physics.UpdateSkinnedCloth", kProfilerPhysics) +PROFILER_INFORMATION(gPhysicsInterpolationProfile, "Physics.Interpolation", kProfilerPhysics) + +void PhysicsManager::UpdateSkinnedClothes () +{ +#if ENABLE_CLOTH + if (m_SmoothedClothDeltaTime == 0.0) + m_SmoothedClothDeltaTime = GetTimeManager().GetDeltaTime(); + else + m_SmoothedClothDeltaTime = Lerp (m_SmoothedClothDeltaTime, GetTimeManager().GetDeltaTime(), 0.01F); + + if(!gClothingScenes.size()) + return; + + PROFILER_AUTO(gPhysicsClothProfile, NULL) + + Assert(m_ActiveSkinnedMeshes.empty()); + SkinnedMeshRenderer::UpdateAllSkinnedMeshes(SkinnedMeshRenderer::kUpdateCloth, &m_ActiveSkinnedMeshes); + +#if ENABLE_MULTITHREADED_SKINNING + JobScheduler& js = GetJobScheduler(); + m_ClothJobGroup = js.BeginGroup (gClothingScenes.size()); +#endif + + for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!= gClothingScenes.end(); i++) + { + if (*i != NULL) + { + if ((*i)->getNbCloths() == 0) + { + gPhysicsSDK->releaseScene (**i); + (*i) = NULL; + } + else + { + #if ENABLE_MULTITHREADED_SKINNING + js.SubmitJob (m_ClothJobGroup, SimulateClothingScene, *i, NULL); + #else + SimulateClothingScene(*i); + #endif + } + } + } +#endif +} + +void PhysicsManager::FinishUpdatingSkinnedClothes () +{ +#if ENABLE_CLOTH +#if ENABLE_MULTITHREADED_SKINNING + PROFILER_AUTO(gPhysicsClothProfile, NULL) + + JobScheduler& js = GetJobScheduler(); + js.WaitForGroup (m_ClothJobGroup); +#endif + + SkinnedMeshRenderer::UploadSkinnedClothes(m_ActiveSkinnedMeshes); + m_ActiveSkinnedMeshes.clear(); +#endif +} + +void PhysicsManager::FixedUpdate () +{ + PROFILER_AUTO(gPhysicsProfile, NULL) + + RETURNIFNOPHYSX_VOID(); + + AssertIf (!m_RecordedTriggers.empty ()); + + // Store interpolated position + for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++) + { + Rigidbody* body = i->body; + i->disabled = 0; + if (body->GetInterpolation() == kInterpolate) + { + i->position = body->GetPosition(); + i->rotation = body->GetRotation(); + } + } + + // Force inactive scene to deallocate memory for all released actors + // Requires PhysX code to be patched accordingly + // NOTE: we don't need to call it on active physics scene since such deallocation will happen inside simulate() method + gInactivePhysicsScene->flushCaches(); + + gPhysicsScene->simulate(GetTimeManager ().GetFixedDeltaTime ()); + + gPhysicsScene->flushStream(); + gPhysicsScene->fetchResults(NX_RIGID_BODY_FINISHED, true); + + // Disable rigid body / collider / controller transform changed message handler + // This simply avoids setting the position/rotation in the rigidbody while we are fetching the novodex state. + SetTransformMessageEnabled (false); + + // Update position / rotation of all rigid bodies + MessageData velocityMessageData; + for (int level=0;level<kMaxSortedActorsDepth;level++) + { + RigidbodyList& bodies = m_SortedActors[level]; + for (RigidbodyList::iterator i=bodies.begin();i != bodies.end();i++) + { + Rigidbody& body = **i; + NxActor* actor = body.m_Actor; + + if (actor->isSleeping ()) + continue; + + if (body.m_DisableReadUpdateTransform == 0) + { + GameObject& go = body.GetGameObject(); + Transform& transform = go.GetComponent (Transform); + NxVec3 pos = actor->getGlobalPosition (); + NxQuat rot = actor->getGlobalOrientationQuat (); + + /// @TODO: DONT NORMALIZE. NOVODEX VALUES ARE PRETTY CLOSE TO UNIT. + /// INSTEAD MAKE OUR NORMALIZE ASSERT CHECKS MORE RELAXED! LOTS OF WORK! + transform.SetPositionAndRotationSafe ((const Vector3f&)pos, (const Quaternionf&)rot); + + + // Synchronize velocity with other components eg. NavMesh + if (go.GetSupportedMessages() & kSupportsVelocityChanged) + { + Vector3f velocity = Vec3FromNx(actor->getLinearVelocity ()); + velocityMessageData.SetData(&velocity, ClassID(Vector3f)); + + go.SendMessageAny(kDidVelocityChange, velocityMessageData); + } + } + } + } + + // Enable rigid body transform changed message handler + SetTransformMessageEnabled (true); + + ProcessRecordedReports(); +} + +void PhysicsManager::ResetInterpolatedTransformPosition () +{ + PROFILER_AUTO(gPhysicsInterpolationProfile, NULL) + + for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++) + { + Rigidbody* body = i->body; + if (body->IsSleeping()) + continue; + + Transform& transform = body->GetComponent(Transform); + + Vector3f pos = body->GetPosition(); + Quaternionf rot = body->GetRotation(); + transform.SetPositionAndRotationSafeWithoutNotification (pos, rot); + } +} + +#if ENABLE_PROFILER +void PhysicsManager::GetPerformanceStats(PhysicsStats& physicsStats) +{ + NxSceneStats stats; + GetDynamicsScene().getStats(stats); + + physicsStats.activeRigidbodies = stats.numDynamicActorsInAwakeGroups; + physicsStats.sleepingRigidbodies = stats.numDynamicActors - stats.numDynamicActorsInAwakeGroups; + + physicsStats.numberOfShapePairs = stats.numPairs; + + physicsStats.numberOfStaticColliders = stats.numStaticShapes; + physicsStats.numberOfDynamicColliders = stats.numDynamicShapes; +} +#endif + +void PhysicsManager::SkinnedClothUpdate () +{ + UpdateSkinnedClothes(); + FinishUpdatingSkinnedClothes (); +} + +void *PhysicsManager::SimulateClothingScene (void *voidScene) +{ +#if ENABLE_CLOTH + NxScene *scene = (NxScene*)voidScene; + + // For correct simulation, cloth should be updated in Fixed Update. + // However, dynamic update tends to behave better for performance - as cloth is a purely visible effect, we care more about + // performance, then about accuracy. + // The problem is that physX seems to apply external forces stronger when using a longer delta time for the simulation, + // resulting in cloth hanging down further at slower frame rates, because gravity is stronger. This can be fixed by adjusting + // the gravity and external forces accordingly. + + // Also, we don't use the actual dynamic delta time, but a highly smoothed version of it. + // Otherwise, the changes in frame rate cause the cloth to jitter. + // This will make the cloth simulate to fast or too slow for a seconds or so, when the frame rate changes, resulting in + // the appearance of more or less damping. While this is technically incorrect, it likely won't be notced, and seems to be better + // then the alternative of running in fixed time. + + float timeScale = GetTimeManager().GetFixedDeltaTime() / GetPhysicsManager().m_SmoothedClothDeltaTime; + Vector3f adjustedGravity = GetPhysicsManager().m_Gravity*timeScale; + scene->setGravity ((NxVec3&)adjustedGravity); + + // same adjustment for external forces + int numCloths = scene->getNbCloths(); + NxCloth **cloths = scene->getCloths(); + for (int j=0; j<numCloths; j++) + cloths[j]->setExternalAcceleration(cloths[j]->getExternalAcceleration() * timeScale); + + scene->simulate (GetPhysicsManager().m_SmoothedClothDeltaTime); + scene->flushStream(); + scene->fetchResults(NX_RIGID_BODY_FINISHED, true); + + for (int j=0; j<numCloths; j++) + ((SkinnedCloth*)(cloths[j]->userData))->ReadBackSkinnedBuffers(); +#endif + return NULL; +} + +NxScene* PhysicsManager::GetClothingScene () +{ +#if ENABLE_CLOTH + int minVertices = std::numeric_limits<int>::max(); + std::vector<NxScene*>::iterator found = gClothingScenes.begin(); + for (std::vector<NxScene*>::iterator i = gClothingScenes.begin(); i!= gClothingScenes.end(); i++) + { + if ((*i) == NULL) + { + NxSceneDesc sceneDesc; + // after simulating the cloth, we still need to transform the results, which can also be threaded. + // so, we use our own threading wrapped around PhysX. No need to enable this, and upper comment suggests + // it has issues. + sceneDesc.flags &= ~NX_SF_SIMULATE_SEPARATE_THREAD; + // Create the scene if it doesn't exist + (*i) = gPhysicsSDK->createScene(sceneDesc); + (*i)->setGravity (NxVec3 (0.0F, -9.81F, 0.0F)); + (*i)->setTiming (1.0F, 8, NX_TIMESTEP_VARIABLE); +#if UNITY_PS3 + NxCellConfig::setSceneParamInt((*i),NxCellConfig::NX_CELL_SCENE_PARAM_SPU_CLOTH, 0); +#endif + } + + int numVertices = 0; + int numCloths = (**i).getNbCloths(); + NxCloth **cloths = (**i).getCloths(); + for (int j=0; j<numCloths; j++) + numVertices += cloths[j]->getNumberOfParticles(); + if (numVertices < minVertices) + { + found = i; + minVertices = numVertices; + } + // No need to look further - this one is empty. + if (numVertices == 0) + return *found; + } + return *found; +#else + return NULL; +#endif +} + +void PhysicsManager::Update () +{ + PROFILER_AUTO(gPhysicsInterpolationProfile, NULL) + + SetTransformMessageEnabled (false); + + // Also disable rigidbody transform changed message, otherwise interpolation will affect physics results. + // This is not done in SetTransformMessageEnabled, as we need the rigidbody messages in the physics fixed update + // so kinematic child rigidbodies are moved with their parents. + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody), kTransformChanged.messageID, false); + + // Interpolation time is [0...1] between the two steps + // Extrapolation time the delta time since the last fixed step + float dynamicTime = GetTimeManager().GetCurTime(); + float step = GetTimeManager().GetFixedDeltaTime(); + float fixedTime = GetTimeManager().GetFixedTime(); + float interpolationTime = clamp01 ((dynamicTime - fixedTime) / step); + float extrapolationTime = dynamicTime - fixedTime; +// AssertIf (t < 0.0F || t > 1.0F); + + // Update interpolated position + for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();i++) + { + Rigidbody* body = i->body; + if (i->disabled || body->IsSleeping()) + continue; + + Transform& transform = body->GetComponent(Transform); + + Quaternionf rot; + Vector3f pos; + RigidbodyInterpolation interpolation = body->GetInterpolation(); + // Interpolate between this physics and last physics frame + if (interpolation == kInterpolate) + { + pos = Lerp(i->position, body->GetPosition(), interpolationTime); + rot = Slerp(i->rotation, body->GetRotation(), interpolationTime); + transform.SetPositionAndRotationSafe (pos, rot); + } + // Extrapolate current position using velocity + else if (interpolation == kExtrapolate) + { + pos = body->GetPosition() + body->GetVelocity() * extrapolationTime; + rot = AngularVelocityToQuaternion(body->GetAngularVelocity(), extrapolationTime) * body->GetRotation(); + transform.SetPositionAndRotationSafe (pos, rot); + } + } + SetTransformMessageEnabled (true); + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody), kTransformChanged.messageID, true); +} + +template<class TransferFunction> +void PhysicsManager::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + + TRANSFER_SIMPLE (m_Gravity); + TRANSFER_SIMPLE (m_DefaultMaterial); + TRANSFER_PROPERTY (float, m_BounceThreshold, GetBounceThreshold, SetBounceThreshold); + TRANSFER_PROPERTY (float, m_SleepVelocity, GetSleepVelocity, SetSleepVelocity); + TRANSFER_PROPERTY (float, m_SleepAngularVelocity, GetSleepAngularVelocity, SetSleepAngularVelocity); + TRANSFER_PROPERTY (float, m_MaxAngularVelocity, GetMaxAngularVelocity, SetMaxAngularVelocity); + TRANSFER_PROPERTY (float, m_MinPenetrationForPenalty, GetMinPenetrationForPenalty, SetMinPenetrationForPenalty); + TRANSFER_PROPERTY (int, m_SolverIterationCount, GetSolverIterationCount, SetSolverIterationCount); + TRANSFER (m_RaycastsHitTriggers); + transfer.Align(); + transfer.Transfer (m_LayerCollisionMatrix, "m_LayerCollisionMatrix", kHideInEditorMask); +} + +#if ENABLE_CLUSTER_SYNC +template<class TransferFunction> +void PhysicsManager::ClusterTransfer(TransferFunction& transfer) +{ + TRANSFER(m_SmoothedClothDeltaTime); +} +#endif + +void PhysicsManager::SetupDefaultMaterial () +{ + m_CachedDefaultMaterial = m_DefaultMaterial; + if (m_CachedDefaultMaterial) + m_CachedDefaultMaterial->CopyMaterialToDefault (); + else + { + NxMaterialDesc material; + material.dynamicFriction = 0.6F; + material.staticFriction = 0.6F; + GetDynamicsScene ().getMaterialFromIndex (0)->loadFromDesc (material); + } +} + +void PhysicsManager::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + RETURNIFNOPHYSX_VOID(); + GetDynamicsScene ().setGravity((const NxVec3&)m_Gravity); + SetupCollisionLayerMatrix (); + + /// FIXME: We need to check this since when we load from disk we don't have a physicsmanager setup yet. + if (GetManagerPtrFromContext (ManagerContext::kPhysicsManager)) + { + SetupDefaultMaterial (); + } +} + +NxActor* PhysicsManager::GetNULLActor () +{ + if (gNULLActor) + return gNULLActor; + + NxBodyDesc bodyDesc; + NxActorDesc actorDesc; + bodyDesc.massSpaceInertia = NxVec3 (1.0F, 1.0F, 1.0F); + bodyDesc.mass = 1.0F; + actorDesc.body = &bodyDesc; + + gNULLActor = GetInactiveDynamicsScene ().createActor (actorDesc); + return gNULLActor; +} + + +Vector3f PhysicsManager::GetGravity () +{ + return m_Gravity; +} + +void PhysicsManager::WakeUpScene () +{ + NxU32 nbActors = GetDynamicsScene().getNbActors(); + NxActor** actors = GetDynamicsScene().getActors(); + for (int i =0; i<nbActors; i++) + { + if (actors[i]->isDynamic()) + actors[i]->wakeUp(); + } +} + +void PhysicsManager::SetGravity (const Vector3f& value) +{ + if (m_Gravity != value) + { + m_Gravity = value; + GetDynamicsScene ().setGravity ((const NxVec3&)value); + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_2_a1)) + WakeUpScene (); + SetDirty (); + } +} + +void PhysicsManager::SetMinPenetrationForPenalty (float value) +{ + RETURNIFNOPHYSX_VOID(); + GetDynamicsSDK ().setParameter (NX_SKIN_WIDTH, value); + SetDirty (); +} + +float PhysicsManager::GetMinPenetrationForPenalty () +{ + RETURNIFNOPHYSX(1.0f); + return GetDynamicsSDK ().getParameter (NX_SKIN_WIDTH); +} + +void PhysicsManager::SetBounceThreshold (float value) +{ + RETURNIFNOPHYSX_VOID(); + GetDynamicsSDK ().setParameter (NX_BOUNCE_THRESHOLD, -value); + SetDirty (); +} + +float PhysicsManager::GetBounceThreshold () +{ + return -GetDynamicsSDK ().getParameter (NX_BOUNCE_THRESHOLD); +} + +void PhysicsManager::SetSleepVelocity (float value) +{ + RETURNIFNOPHYSX_VOID(); + GetDynamicsSDK ().setParameter (NX_DEFAULT_SLEEP_LIN_VEL_SQUARED, value * value); + SetDirty (); +} + +float PhysicsManager::GetSleepVelocity () +{ + return Sqrt (GetDynamicsSDK ().getParameter (NX_DEFAULT_SLEEP_LIN_VEL_SQUARED)); +} + +void PhysicsManager::SetSleepAngularVelocity (float value) +{ + RETURNIFNOPHYSX_VOID(); + GetDynamicsSDK ().setParameter (NX_DEFAULT_SLEEP_ANG_VEL_SQUARED, value * value); + SetDirty (); +} + +float PhysicsManager::GetSleepAngularVelocity () +{ + return Sqrt (GetDynamicsSDK ().getParameter (NX_DEFAULT_SLEEP_ANG_VEL_SQUARED)); +} + +void PhysicsManager::SetMaxAngularVelocity (float value) +{ + RETURNIFNOPHYSX_VOID(); + GetDynamicsSDK ().setParameter (NX_MAX_ANGULAR_VELOCITY, value); + SetDirty (); +} + +float PhysicsManager::GetMaxAngularVelocity () +{ + return GetDynamicsSDK ().getParameter (NX_MAX_ANGULAR_VELOCITY); +} + +void PhysicsManager::SetSolverIterationCount (int value) +{ + RETURNIFNOPHYSX_VOID(); + m_DefaultIterationCount = clamp(value, 1, 100); + SetDirty (); +} + +class CollideShapesReport : public NxUserEntityReport<NxShape*> +{ +public: + + NxSphere sphere; + bool checkShapes; + PhysicsManager::ColliderCache& cache; + CollideShapesReport (PhysicsManager::ColliderCache& c) : cache (c) {} + + virtual bool onEvent(NxU32 nbEntities, NxShape** entities) + { + int offset = cache.size (); + cache.reserve (offset + nbEntities); + for (int i=0;i<nbEntities;i++) + { + if (entities[i]->checkOverlapSphere (sphere)) + cache.push_back ((Collider*)entities[i]->userData); + } + return true; + } +}; + +PhysicsManager::ColliderCache& PhysicsManager::OverlapSphere (const Vector3f& p, float radius, int mask) +{ + PROFILER_AUTO(gSphereOverlapProfile, NULL) + m_ColliderCache.clear (); + CollideShapesReport report (m_ColliderCache); + report.sphere = NxSphere ((NxVec3&)p, radius); + report.checkShapes = true; + + GetDynamicsScene ().overlapSphereShapes (report.sphere, NX_ALL_SHAPES, 0, NULL, &report, mask); + return m_ColliderCache; +} + +bool PhysicsManager::SphereTest (const Vector3f& p, float radius, int mask) +{ + PROFILER_AUTO(gSphereTestProfile, NULL) + NxSphere sphere((NxVec3&)p, radius); + return GetDynamicsScene ().checkOverlapSphere (sphere, NX_ALL_SHAPES, mask); +} + + +bool PhysicsManager::RaycastTest (const Ray& ray, float distance, int mask) +{ + PROFILER_AUTO(gRaycastProfile, NULL) + + AssertIf (!IsNormalized (ray.GetDirection ())); + if (distance == std::numeric_limits<float>::infinity()) + distance = NX_MAX_F32; + return GetDynamicsScene ().raycastAnyShape ((NxRay&)ray, NX_ALL_SHAPES, mask, distance); +} + +bool PhysicsManager::Raycast (const Ray& ray, float distance, RaycastHit& outHit, int mask) +{ + AssertIf (!IsNormalized (ray.GetDirection ())); + PROFILER_AUTO(gRaycastProfile, NULL) + + if (distance == std::numeric_limits<float>::infinity()) + distance = NX_MAX_F32; + + NxRaycastHit hit; + NxShape* shape = GetDynamicsScene ().raycastClosestShape ((NxRay&)ray, NX_ALL_SHAPES, hit, mask, distance); + if (shape) + { + NxToRaycastHit(hit, outHit); + return true; + } + else + return false; +} + +bool PhysicsManager::CapsuleCast (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, RaycastHit& outHit, int mask) +{ + AssertIf (!IsNormalized (direction)); + PROFILER_AUTO(gCapsuleCastProfile, NULL) + + if (distance == std::numeric_limits<float>::infinity()) + // CapsuleCasts fail when using NX_MAX_F32 here. + // So pick a lower "high" number instead. + distance = 1000000.0f; + + NxCapsule testCapsule; + testCapsule.radius = radius; + testCapsule.p0 = (const NxVec3&)p0; + testCapsule.p1 = (const NxVec3&)p1; + + NxSweepQueryHit hit; + NxU32 nb = GetDynamicsScene ().linearCapsuleSweep (testCapsule, (const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS, NULL, 1, &hit, NULL, mask); + if (nb) + { + NxToRaycastHit(hit, distance, outHit); + return true; + } + else + return false; +} + +void NxToRaycastHit (const NxRaycastHit& hit, RaycastHit& outHit) +{ + outHit.collider = (Collider*)hit.shape->userData; + outHit.point = (const Vector3f&)hit.worldImpact; + outHit.normal = (const Vector3f&)hit.worldNormal; + outHit.faceID = hit.faceID; + outHit.distance = hit.distance; + outHit.uv.x = hit.u; + outHit.uv.y = hit.v; +} + +void NxToRaycastHit (const NxSweepQueryHit& hit, float sweepDistance, RaycastHit& outHit) +{ + outHit.collider = (Collider*)hit.hitShape->userData; + outHit.point = (const Vector3f&)hit.point; + outHit.normal = (const Vector3f&)hit.normal; + outHit.faceID = hit.faceID; + outHit.distance = hit.t * sweepDistance; + outHit.uv.x = 0; + outHit.uv.y = 0; +} + +bool PhysicsManager::CapsuleTest (Vector3f start, Vector3f end, float radius, int mask) +{ + PROFILER_AUTO(gCapsuleTestProfile, NULL) + NxCapsule capsule; + capsule.p0 = Vec3ToNx(start); + capsule.p1 = Vec3ToNx(end); + capsule.radius = radius; + + return GetDynamicsScene ().checkOverlapCapsule (capsule, NX_ALL_SHAPES, mask); +} + + +const PhysicsManager::RaycastHits& PhysicsManager::RaycastAll (const Ray& ray, float distance, int mask) +{ + AssertIf (!IsNormalized (ray.GetDirection ())); + PROFILER_AUTO(gRaycastAllProfile, NULL) + + if (distance == std::numeric_limits<float>::infinity()) + distance = NX_MAX_F32; + + static vector<RaycastHit> hits; + hits.resize(0); + RaycastCollector collector; + collector.hits = &hits; + + GetDynamicsScene ().raycastAllShapes ((NxRay&)ray, collector, NX_ALL_SHAPES, mask, distance); + + return hits; +} + +#define kCapsuleCastMaxHits 128 +const PhysicsManager::RaycastHits& PhysicsManager::CapsuleCastAll (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, int mask) +{ + AssertIf (!IsNormalized (direction)); + PROFILER_AUTO(gCapsuleCastAllProfile, NULL) + + if (distance == std::numeric_limits<float>::infinity()) + // CapsuleCasts fail when using NX_MAX_F32 here. + // So pick a lower "high" number instead. + distance = 1000000.0f; + + static vector<RaycastHit> outHits; + + NxCapsule testCapsule; + testCapsule.radius = radius; + testCapsule.p0 = (const NxVec3&)p0; + testCapsule.p1 = (const NxVec3&)p1; + + NxSweepQueryHit hits[kCapsuleCastMaxHits]; + NxU32 nb = GetDynamicsScene ().linearCapsuleSweep (testCapsule, (const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS|NX_SF_ALL_HITS, NULL, kCapsuleCastMaxHits, hits, NULL, mask); + + outHits.resize(nb); + for (int i=0; i<nb; i++) + NxToRaycastHit(hits[i], distance, outHits[i]); + + return outHits; +} + +void PhysicsManager::IgnoreCollision (Collider& lhs, Collider& rhs, bool ignore) +{ + if (lhs.m_Shape == NULL || rhs.m_Shape == NULL) + { + ErrorString ("Ignore collision failed. Both colliders need to be activated when calling this IgnoreCollision"); + return; + } + + if (ignore) + GetDynamicsScene().setShapePairFlags(*lhs.m_Shape, *rhs.m_Shape, NX_IGNORE_PAIR); + else + GetDynamicsScene().setShapePairFlags(*lhs.m_Shape, *rhs.m_Shape, 0); +} + +void PhysicsManager::IgnoreCollision(int layer1, int layer2, bool ignore) +{ + if (layer1 >= kNumLayers || layer2 >= kNumLayers) + { + ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers)); + return; + } + + Assert (kNumLayers <= m_LayerCollisionMatrix.size()); + Assert (kNumLayers <= sizeof(m_LayerCollisionMatrix[0])*8); + + GetDynamicsScene ().setGroupCollisionFlag(layer1, layer2, !ignore); + if (ignore) + { + m_LayerCollisionMatrix[layer1] &= ~(1<<layer2); + m_LayerCollisionMatrix[layer2] &= ~(1<<layer1); + } + else + { + m_LayerCollisionMatrix[layer1] |= 1<<layer2; + m_LayerCollisionMatrix[layer2] |= 1<<layer1; + } + SetDirty(); +} + +bool PhysicsManager::GetIgnoreCollision(int layer1, int layer2) +{ + if (layer1 >= kNumLayers || layer2 >= kNumLayers) + { + ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers)); + return false; + } + + return !GetDynamicsScene ().getGroupCollisionFlag(layer1, layer2); +} + +void PhysicsManager::SetupCollisionLayerMatrix() +{ + for (int i=0;i<kNumLayers;i++) + { + for (int j=0;j<kNumLayers;j++) + { + if (i <= j) + { + bool enabled = m_LayerCollisionMatrix[i] & (1<<j); + GetDynamicsScene().setGroupCollisionFlag(i, j, enabled); + } + } + } +} + + +void PhysicsManager::AddBody(int depth, ListNode<Rigidbody>& node) +{ + depth = min(kMaxSortedActorsDepth-1, depth); + if (depth >= kMaxSortedActorsDepth-1) + { + ErrorString("Too deep hierarchy to perform rigidbody ordering. Nested rigidbodies might look strange"); + depth = kMaxSortedActorsDepth-1; + } + m_SortedActors[depth].push_back(node); +} + +NxScene& GetDynamicsScene () +{ + AssertIf (gPhysicsScene == NULL); + return *gPhysicsScene; +} + +NxScene& GetInactiveDynamicsScene () +{ + AssertIf (gInactivePhysicsScene == NULL); + return *gInactivePhysicsScene; +} + + +NxPhysicsSDK& GetDynamicsSDK () +{ + AssertIf (gPhysicsSDK == NULL); + return *gPhysicsSDK; +} + +void PhysicsManager::ProcessRecordedReports() +{ + SetDisableImmediateDestruction(true); + + /// Report recorded triggers. + /// We can't call the recorded triggers immediately because novodex still keeps a write lock at that time. + /// So we can't do much with physics at that time. + for (int i=0;i<m_RecordedTriggers.size ();i++) + { + RecordedTrigger& trigger = m_RecordedTriggers[i]; + + VerifyObjectPtr(trigger.collider); + VerifyObjectPtr(trigger.trigger); + + Unity::Component* masterOfCollider = AttachedRigidbodyOrCollider (*trigger.collider); + Unity::Component* masterOfTrigger = AttachedRigidbodyOrCollider (*trigger.trigger); + MessageIdentifier messageID; + if (trigger.status == NX_TRIGGER_ON_ENTER) + messageID = kEnterTrigger; + else if (trigger.status == NX_TRIGGER_ON_LEAVE) + messageID = kExitTrigger; + else + messageID = kStayTrigger; + + trigger.trigger->SendMessage (messageID, trigger.collider, ClassID (Collider)); + masterOfCollider->SendMessage (messageID, trigger.trigger, ClassID (Collider)); + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + { + if (masterOfCollider->GetGameObjectPtr() != trigger.collider->GetGameObjectPtr() && trigger.collider->GetIsTrigger()) + trigger.collider->SendMessage (messageID, trigger.trigger, ClassID (Collider)); + if (masterOfTrigger->GetGameObjectPtr() != trigger.trigger->GetGameObjectPtr()) + masterOfTrigger->SendMessage (messageID, trigger.collider, ClassID (Collider)); + } + } + m_RecordedTriggers.clear (); + + /// Report recorded contact events. + for (int i=0;i<m_RecordedContacts.size ();i++) + { + Collision& contact = m_RecordedContacts[i]; + VerifyObjectPtr(contact.thisRigidbody); + VerifyObjectPtr(contact.otherRigidbody); + VerifyObjectPtr(contact.thisCollider); + VerifyObjectPtr(contact.otherCollider); + + if (contact.status & NX_NOTIFY_ON_START_TOUCH) + { + contact.flipped = false; + GetRigidbodyOrCollider(contact)->SendMessage (kEnterContact, &contact, ClassID (Collision)); + contact.flipped = true; + GetOtherRigidbodyOrCollider(contact)->SendMessage (kEnterContact, &contact, ClassID (Collision)); + } + if (contact.status & NX_NOTIFY_ON_END_TOUCH) + { + contact.flipped = false; + GetRigidbodyOrCollider(contact)->SendMessage (kExitContact, &contact, ClassID (Collision)); + contact.flipped = true; + GetOtherRigidbodyOrCollider(contact)->SendMessage (kExitContact, &contact, ClassID (Collision)); + } + if (contact.status & NX_NOTIFY_ON_TOUCH) + { + contact.flipped = false; + GetRigidbodyOrCollider(contact)->SendMessage (kStayContact, &contact, ClassID (Collision)); + contact.flipped = true; + GetOtherRigidbodyOrCollider(contact)->SendMessage (kStayContact, &contact, ClassID (Collision)); + } + } + m_RecordedContacts.clear (); + + /// Report recorded joint breaks + for (int i=0;i<m_RecordedJointBreaks.size ();i++) + { + RecordedJointBreak& record = m_RecordedJointBreaks[i]; + Joint* joint = record.joint; + if (joint && joint->IsActive()) + { + joint->GetGameObject().SendMessage (kJointBreak, record.impulse, ClassID (float)); + } + joint = record.joint; + if (joint && joint->GetGameObjectPtr()) + { + SetDisableImmediateDestruction(false); + DestroyObjectHighLevel(joint, true); + SetDisableImmediateDestruction(true); + } + } + m_RecordedJointBreaks.clear(); + + SetDisableImmediateDestruction(false); +} + +// When using raycasts or other physics functionality in edit mode (such as is done when generating terrain lightmaps), +// this needs to be called to make sure all updates to colliders in the scene are taken into account. +void PhysicsManager::RefreshWhenPaused() +{ + gPhysicsScene->simulate(0); + gPhysicsScene->flushCaches(); + gPhysicsScene->flushStream(); + gPhysicsScene->fetchResults(NX_RIGID_BODY_FINISHED, true); + ProcessRecordedReports(); +} + +#if ENABLE_SCRIPTING +ScriptingArrayPtr ConvertNativeRaycastHitsToManaged(const PhysicsManager::RaycastHits& hits) +{ + ScriptingArrayPtr arr = CreateScriptingArray(hits.size() > 0 ? &hits[0] : NULL, hits.size(), GetScriptingManager().GetCommonClasses().raycastHit); + RaycastHit* firstElement = Scripting::GetScriptingArrayStart<RaycastHit>(arr); + for (int i=0;i<hits.size();i++) + { + firstElement[i].collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (firstElement[i].collider)); + } + return arr; +} +#endif + +GET_MANAGER (PhysicsManager) +GET_MANAGER_PTR (PhysicsManager) +IMPLEMENT_CLASS_HAS_INIT (PhysicsManager) +IMPLEMENT_OBJECT_SERIALIZE (PhysicsManager) +IMPLEMENT_CLUSTER_SERIALIZE (PhysicsManager) +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/PhysicsManager.h b/Runtime/Dynamics/PhysicsManager.h new file mode 100644 index 0000000..654ea8f --- /dev/null +++ b/Runtime/Dynamics/PhysicsManager.h @@ -0,0 +1,235 @@ +#ifndef PHYSICSMANAGER_H +#define PHYSICSMANAGER_H + +#include "Runtime/BaseClasses/GameManager.h" +#include "Runtime/Math/Vector2.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Quaternion.h" +#include "Runtime/Geometry/Ray.h" +#include "Runtime/Misc/MessageParameters.h" +#include "Runtime/Utilities/LinkedList.h" +#include "Runtime/Threads/JobScheduler.h" +#include "Runtime/ClusterRenderer/ClusterRendererDefines.h" + +class Collider; +class CharacterController; +class SkinnedMeshRenderer; +class PhysicMaterial; +namespace Unity { class Component; } +namespace Unity { class Joint; } +class NxActor; +class NxScene; +class NxShape; +class NxUserControllerHitReport; +struct NxRaycastHit; +struct NxSweepQueryHit; +class NxMaterialDesc; +struct PhysicsStats; +struct RaycastHit +{ + Vector3f point; + Vector3f normal; + UInt32 faceID; + float distance; + Vector2f uv; + Collider* collider; +}; + +struct RigidbodyInterpolationInfo : public ListElement +{ + Vector3f position; + Quaternionf rotation; + Rigidbody* body; + int disabled; +}; + +class PhysicsManager : public GlobalGameManager { + public: + + PhysicsManager (MemLabelId label, ObjectCreationMode mode); + // ~PhysicsManager (); declared-by-macro + + REGISTER_DERIVED_CLASS (PhysicsManager, GlobalGameManager) + DECLARE_OBJECT_SERIALIZE (PhysicsManager) + DECLARE_CLUSTER_SERIALIZE (PhysicsManager) + + void FixedUpdate (); + void Update (); + void SkinnedClothUpdate (); + void ResetInterpolatedTransformPosition (); + void RefreshWhenPaused (); + void RecreateScene (); + + void GetPerformanceStats(PhysicsStats& physicsStats); + + void UpdateSkinnedClothes(); + void FinishUpdatingSkinnedClothes (); + + static void InitializeClass (); + static void CleanupClass (); + + Vector3f GetGravity (); + void SetGravity (const Vector3f& value); + + void SetMinPenetrationForPenalty (float value); + float GetMinPenetrationForPenalty (); + + void SetBounceThreshold (float value); + float GetBounceThreshold (); + + void SetSleepVelocity (float value); + float GetSleepVelocity (); + + void SetSleepAngularVelocity (float value); + float GetSleepAngularVelocity (); + + void SetMaxAngularVelocity (float value); + float GetMaxAngularVelocity (); + + void SetSolverIterationCount (int value); + int GetSolverIterationCount () { return m_DefaultIterationCount; } + + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + // Overlap sphere + typedef std::vector<Collider*> ColliderCache; + ColliderCache& OverlapSphere (const Vector3f& p, float radius, int mask); + bool SphereTest (const Vector3f& p, float radius, int mask); + + bool CapsuleTest (Vector3f start, Vector3f end, float radius, int mask); + + typedef std::vector<RaycastHit> RaycastHits; + + bool RaycastTest (const Ray& ray, float distance, int mask); + bool Raycast (const Ray& ray, float distance, RaycastHit& hit, int mask); + bool CapsuleCast (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, RaycastHit& outHit, int mask); + const RaycastHits& RaycastAll (const Ray& ray, float distance, int mask); + const RaycastHits& CapsuleCastAll (const Vector3f &p0, const Vector3f &p1, float radius, const Vector3f &direction, float distance, int mask); + + NxActor* GetNULLActor (); + + bool GetRaycastsHitTriggers () { return m_RaycastsHitTriggers; } + + void IgnoreCollision (Collider& a, Collider& b, bool ignore); + void IgnoreCollision (int layer1, int layer2, bool ignore); + bool GetIgnoreCollision(int layer1, int layer2); + UInt32 GetLayerCollisionMask(int layer) {return m_LayerCollisionMatrix[layer];} + + bool IsRigidbodyTransformMessageEnabled() {return m_RigidbodyTransformMessageEnabled;} + public: + + void AddBody(int depth, ListNode<Rigidbody>& node); + NxScene* GetClothingScene (); + + // Only for internal use + void SetTransformMessageEnabled(bool enable); + + List<RigidbodyInterpolationInfo>& GetInterpolatedBodies() { return m_InterpolatedBodies; } + + struct RecordedTrigger + { + int status; + Collider* trigger; + Collider* collider; + }; + + struct RecordedJointBreak + { + float impulse; + PPtr<Unity::Joint> joint; + }; + + typedef std::vector<RecordedTrigger> RecordedTriggers; + typedef std::vector<Collision> RecordedContacts; + typedef std::vector<RecordedJointBreak> RecordedJointBreaks; + +private: + static void CleanupReports(); + void CreateReports(); + void ProcessRecordedReports(); + void SetupCollisionLayerMatrix(); + void WakeUpScene (); + static void *SimulateClothingScene (void *voidIndex); + + static void ReleaseMaterials(std::vector<PhysicMaterial*>& materials, std::vector<NxMaterialDesc>& materialDescs); + + + Vector3f m_Gravity;///< The gravity applied to all rigid bodies in the scene + virtual void Reset (); + + #if DOXYGEN + + /// The minimum contact penetration value in order to apply a penalty force. (Default 0.01) range { 0 , infinity } + float m_MinPenetrationForPenalty; + /// The penalty force applied to the bodies in an interpenetrating contact is scaled by this value. (Default 0.6) range { 0, 2 } + float m_PenetrationPenaltyForce; + /// A contact with a relative velocity below this will not bounce. (Default 2) range { 0, infinity } + float m_BounceThreshold; + /// The default linear velocity, below which objects start going to sleep. This value can be overridden per rigidbodies with scripting. (Default 0.15) range { 0, infinity } + float m_SleepVelocity; + /// The default linear velocity, below which objects start going to sleep. This value can be overridden per rigidbodies with scripting. (Default 0.14) range { 0, infinity } + float m_SleepAngularVelocity; + /// The maximum angular velocity permitted for any rigid bodies. This can be overridden per rigidbodies with scripting. (Default 7) range { 0, infinity } + float m_MaxAngularVelocity; + + #endif + + bool m_RaycastsHitTriggers; + + bool m_RigidbodyTransformMessageEnabled; + void SetupDefaultMaterial (); + + + PPtr<PhysicMaterial> m_DefaultMaterial; + PhysicMaterial* m_CachedDefaultMaterial; + + ColliderCache m_ColliderCache; + +// typedef std::set<PPtr<PhysicMaterial> > Materials; +// Materials m_Materials; + + RecordedTriggers m_RecordedTriggers; + RecordedContacts m_RecordedContacts; + RecordedJointBreaks m_RecordedJointBreaks; + /// Solver accuracy. Higher value costs more performance. (Default 4) range { 1, 30 } + int m_DefaultIterationCount; + + std::vector<SInt32> m_DisableTransformMessage; + typedef List<RigidbodyInterpolationInfo> InterpolatedBodiesList; + typedef InterpolatedBodiesList::iterator InterpolatedBodiesIterator; + InterpolatedBodiesList m_InterpolatedBodies; + float m_SmoothedClothDeltaTime; + + enum { kMaxSortedActorsDepth = 64 }; + typedef List< ListNode<Rigidbody> > RigidbodyList; + RigidbodyList m_SortedActors[kMaxSortedActorsDepth]; +#if ENABLE_MULTITHREADED_SKINNING + JobScheduler::JobGroupID m_ClothJobGroup; +#endif + dynamic_array<SkinnedMeshRenderer*> m_ActiveSkinnedMeshes; + + std::vector<UInt32> m_LayerCollisionMatrix; + friend class PhysicMaterial; + +#if ENABLE_CLUSTER_SYNC +#ifdef DEBUG + friend class ClusterTransfer; +#endif // DEBUG +#endif // ENABLE_CLUSTER_SYNC +}; + +enum { kContactNothingGroup = 0, kContactEnterExitGroup = 1, kContactTouchGroup = 2 }; + +PhysicsManager &GetPhysicsManager (); +PhysicsManager *GetPhysicsManagerPtr (); +class NxScene& GetDynamicsScene (); +class NxScene& GetInactiveDynamicsScene (); +class NxPhysicsSDK& GetDynamicsSDK (); +void NxToRaycastHit (const NxRaycastHit& hit, RaycastHit& outHit); +void NxToRaycastHit (const NxSweepQueryHit& hit, float sweepDistance, RaycastHit& outHit); + +#if ENABLE_SCRIPTING +ScriptingArrayPtr ConvertNativeRaycastHitsToManaged(const PhysicsManager::RaycastHits& hits); +#endif + +#endif diff --git a/Runtime/Dynamics/PhysicsModule.cpp b/Runtime/Dynamics/PhysicsModule.cpp new file mode 100644 index 0000000..09bd13c --- /dev/null +++ b/Runtime/Dynamics/PhysicsModule.cpp @@ -0,0 +1,141 @@ +#include "UnityPrefix.h" + +#if ENABLE_PHYSICS +#include "Runtime/Interfaces/IPhysics.h" +#include "PhysicsManager.h" +#include "RigidBody.h" +#include "Runtime/Dynamics/PhysicsManager.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "External/PhysX/builds/SDKs/Cooking/include/NxCooking.h" +#include "Runtime/Dynamics/SkinnedCloth.h" +#include "Runtime/Dynamics/CapsuleCollider.h" +#include "Runtime/Dynamics/Collider.h" +#include "Runtime/Dynamics/MeshCollider.h" +#include "Runtime/Math/Random/rand.h" +#include "Runtime/Dynamics/NxMeshCreation.h" +#include "Runtime/Dynamics/nxmemorystream.h" + +//only implementation. +class PhysicsModule : public IPhysics +{ +public: + virtual void SetRigidBodyState( Rigidbody& rigidbody, const RigidBodyState& state ) + { + rigidbody.SetPosition(state.position); + rigidbody.SetRotation(state.rotation); + rigidbody.SetVelocity(state.velocity); + rigidbody.SetAngularVelocity(state.avelocity); + } + + virtual void GetRigidBodyState( const Rigidbody& rigidbody, RigidBodyState* result) + { + result->position = rigidbody.GetPosition(); + result->rotation = rigidbody.GetRotation(); + result->velocity = rigidbody.GetVelocity(); + result->avelocity = rigidbody.GetAngularVelocity(); + } + + virtual Vector3f GetRigidBodyVelocity( const Rigidbody& rigidbody) + { + return rigidbody.GetVelocity(); + } + +#if ENABLE_PROFILER + virtual void GetProfilerStats(PhysicsStats& stats) + { + GetPhysicsManager().GetPerformanceStats(stats); + } +#endif + + virtual Vector3f GetGravity() + { + return GetPhysicsManager().GetGravity(); + } + + virtual void* CreateNxMeshFromNxStream(bool convex, const NxStream& stream) + { + return convex ? (void*)GetDynamicsSDK ().createConvexMesh(stream) : (void*)GetDynamicsSDK ().createTriangleMesh(stream); + } + + virtual void ReleaseNxTriangleMesh(NxTriangleMesh& mesh) + { + GetDynamicsSDK ().releaseTriangleMesh (mesh); + } + + virtual void ReleaseNxConvexMesh(NxConvexMesh& mesh) + { + GetDynamicsSDK ().releaseConvexMesh (mesh); + } + +#if ENABLE_CLOTH + virtual void SetUpSkinnedBuffersOnSkinnedCloth (SkinnedCloth& cloth, void *vertices, void *normals, void *tangents, size_t bufferStride) + { + cloth.SetUpSkinnedBuffers(vertices,normals,tangents,bufferStride); + } +#endif + + virtual void CapsuleColliderSetHeight(CapsuleCollider& collider, float height) + { + collider.SetHeight(height); + } + + virtual ScriptingObjectPtr ConvertContactToMono (Collision* input) + { + return ::ConvertContactToMono(input); + } + + virtual int GetColliderMaterialInstanceID(Collider& collider) + { + return collider.GetMaterial().GetInstanceID(); + } + + virtual int GetColliderSharedMeshInstanceID(MeshCollider& collider) + { + return collider.GetSharedMesh().GetInstanceID(); + } + + virtual bool CreateNxStreamFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType, MemoryStream& stream ) + { + return ::CreateNxStreamFromUnityMesh (mesh, convex, scalematrix, transformType, stream ); + } + + virtual void* CreateNxMeshFromUnityMesh (Mesh* mesh, bool convex, const Matrix4x4f& scalematrix, TransformType transformType ) + { + return ::CreateNxMeshFromUnityMesh (mesh, convex, scalematrix, transformType ); + } + + virtual MemoryStream* CreateNxStreamFromUnityMesh(Mesh& meshData, bool convex) + { + return ::CreateNxStreamFromUnityMesh(meshData,convex); + } + + virtual void DeleteMemoryStream(MemoryStream* stream) + { + delete stream; + } + + virtual void ReleaseHeightField(NxHeightField& heightField) + { + GetDynamicsSDK().releaseHeightField(heightField); + } + + virtual NxHeightField* CreateNxHeightField(NxHeightFieldDesc& desc) + { + return GetDynamicsSDK().createHeightField(desc); + } + +}; + +void InitializePhysicsModule () +{ + SetIPhysics(UNITY_NEW_AS_ROOT(PhysicsModule, kMemPhysics, "PhysicsInterface", "")); +} + +void CleanupPhysicsModule () +{ + PhysicsModule* module = reinterpret_cast<PhysicsModule*> (GetIPhysics ()); + UNITY_DELETE(module, kMemPhysics); + SetIPhysics (NULL); +} + +#endif
\ No newline at end of file diff --git a/Runtime/Dynamics/PhysicsModule.h b/Runtime/Dynamics/PhysicsModule.h new file mode 100644 index 0000000..f700b51 --- /dev/null +++ b/Runtime/Dynamics/PhysicsModule.h @@ -0,0 +1,3 @@ +#pragma once +void InitializePhysicsModule (); +void CleanupPhysicsModule ();
\ No newline at end of file diff --git a/Runtime/Dynamics/PhysicsModule.jam b/Runtime/Dynamics/PhysicsModule.jam new file mode 100644 index 0000000..15b2147 --- /dev/null +++ b/Runtime/Dynamics/PhysicsModule.jam @@ -0,0 +1,108 @@ +rule PhysicsModule_ReportCpp +{ + local files = + PhysicsModule.jam + BoxCollider.cpp + BoxCollider.h + CapsuleCollider.cpp + CapsuleCollider.h + CharacterController.cpp + CharacterController.h + CharacterJoint.cpp + CharacterJoint.h + Cloth.cpp + Cloth.h + Collider.cpp + Collider.h + ConfigurableJoint.cpp + ConfigurableJoint.h + ConstantForce.cpp + ConstantForce.h + DeformableMesh.cpp + DeformableMesh.h + FixedJoint.cpp + FixedJoint.h + HingeJoint.cpp + HingeJoint.h + Joint.cpp + Joint.h + JointDescriptions.h + Joints.h + MeshCollider.cpp + MeshCollider.h + PhysicMaterial.cpp + PhysicMaterial.h + PhysicsManager.cpp + PhysicsManager.h + PrimitiveCollider.h + RaycastCollider.cpp + RaycastCollider.h + RaycastHit.cpp + RaycastHit.h + Rigidbody.cpp + Rigidbody.h + SkinnedCloth.cpp + SkinnedCloth.h + SphereCollider.cpp + SphereCollider.h + SpringJoint.cpp + SpringJoint.h + WheelCollider.cpp + WheelCollider.h + PhysXRaycast.cpp + PhysXRaycast.h + TerrainCollider.cpp + TerrainCollider.h + ClothRenderer.cpp + ClothRenderer.h + PhysicsModuleRegistration.cpp + PhysicsModule.cpp + NxMeshCreation.cpp + NxMeshCreation.h + ExtractDataFromMesh.cpp + ExtractDataFromMesh.h + nxmemorystream.cpp + nxmemorystream.h + ; + + return Runtime/Dynamics/$(files) ; +} + +rule PhysicsModule_ReportTxt +{ + return Runtime/Dynamics/ScriptBindings/NewDynamics.txt ; +} + +rule PhysicsModule_ReportIncludes +{ + return + Runtime/Dynamics + External/PhysX/builds/SDKs/Foundation/include + External/PhysX/builds/SDKs/Physics/include + External/PhysX/builds/SDKs/PhysXLoader/include + ; +} + +rule PhysicsModule_ReportLibraries +{ + local libs = ; + if $(target) in MacEditor MacStandalonePlayer + { + libs += + $(TOP)/External/PhysX/builds/SDKs/lib/osxstatic/novodex_cooking.a + $(TOP)/External/PhysX/builds/SDKs/lib/osxstatic/novodex_release.a + ; + } + return $(libs) ; +} + +rule PhysicsModule_Init +{ + OverrideModule Physics : GetModule_Cpp : byOverridingWithMethod : PhysicsModule_ReportCpp ; + OverrideModule Physics : GetModule_Txt : byOverridingWithMethod : PhysicsModule_ReportTxt ; + OverrideModule Physics : GetModule_Inc : byOverridingWithMethod : PhysicsModule_ReportIncludes ; + OverrideModule Physics : GetModule_Lib : byChainingWithMethod : PhysicsModule_ReportLibraries ; +} + + +#RegisterModule Physics ;
\ No newline at end of file diff --git a/Runtime/Dynamics/PhysicsModuleRegistration.cpp b/Runtime/Dynamics/PhysicsModuleRegistration.cpp new file mode 100644 index 0000000..8e5b0d3 --- /dev/null +++ b/Runtime/Dynamics/PhysicsModuleRegistration.cpp @@ -0,0 +1,67 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "Runtime/BaseClasses/ClassRegistration.h" +#include "Runtime/Modules/ModuleRegistration.h" + +static void RegisterPhysicsClasses (ClassRegistrationContext& context) +{ + REGISTER_CLASS (Rigidbody) + REGISTER_CLASS (PhysicsManager) + REGISTER_CLASS (Collider) + REGISTER_CLASS (Joint) + REGISTER_CLASS (HingeJoint) + REGISTER_CLASS (MeshCollider) + REGISTER_CLASS (BoxCollider) + REGISTER_CLASS (ConstantForce) + REGISTER_CLASS (PhysicMaterial) + REGISTER_CLASS (SphereCollider) + REGISTER_CLASS (CapsuleCollider) + REGISTER_CLASS (FixedJoint) + REGISTER_CLASS (RaycastCollider) + REGISTER_CLASS (CharacterController) + REGISTER_CLASS (CharacterJoint) + REGISTER_CLASS (SpringJoint) + REGISTER_CLASS (WheelCollider) + REGISTER_CLASS (ConfigurableJoint) + +#if ENABLE_CLOTH + REGISTER_CLASS (Cloth) + REGISTER_CLASS (InteractiveCloth) + REGISTER_CLASS (ClothRenderer) + REGISTER_CLASS (SkinnedCloth) +#endif + +#if ENABLE_TERRAIN + REGISTER_CLASS (TerrainCollider) +#endif +} + +#if ENABLE_MONO || UNITY_WINRT +void ExportNewDynamics (); +#if UNITY_EDITOR +void ExportColliderUtil (); +#endif + +static void RegisterPhysicsICallModule () +{ + ///@TODO: Maybe this ifdef should be moved to the cspreprocess generated code instead???? (For all modules) + +#if !INTERNAL_CALL_STRIPPING + ExportNewDynamics (); +#if UNITY_EDITOR + ExportColliderUtil (); +#endif +#endif +} +#endif + +extern "C" EXPORT_MODULE void RegisterModule_Physics () +{ + ModuleRegistrationInfo info; + info.registerClassesCallback = &RegisterPhysicsClasses; +#if ENABLE_MONO || UNITY_WINRT + info.registerIcallsCallback = &RegisterPhysicsICallModule; +#endif + RegisterModuleInfo (info); +} +#endif
\ No newline at end of file diff --git a/Runtime/Dynamics/PhysicsTest.cpp b/Runtime/Dynamics/PhysicsTest.cpp new file mode 100644 index 0000000..aa5575e --- /dev/null +++ b/Runtime/Dynamics/PhysicsTest.cpp @@ -0,0 +1,93 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" + +#if ENABLE_UNIT_TESTS + +#include "Runtime/Dynamics/BoxCollider.h" +#include "Runtime/Dynamics/RigidBody.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/Graphics/Transform.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" + + +void RunPhysicsTests (); + +void SetParentAttachesColliderToRigidbody (); +void RemovingRididbodyComponentReattachesStaticCollider (); +void RemovingRididbodyComponentReattachesColliderToNextRigidbody (); + +///@TODO: Tests for Joints losing connectiong with rigidbody etc. There seems to be no C++ code handling this properly??? + + +void RunPhysicsTests () +{ + SetParentAttachesColliderToRigidbody (); + RemovingRididbodyComponentReattachesStaticCollider (); + RemovingRididbodyComponentReattachesColliderToNextRigidbody (); +} + + +// Changing parent of collider will reattach collider to rigidbody and deatch it. +void SetParentAttachesColliderToRigidbody () +{ + GameObject& root = CreateGameObject("Root", "Transform", "Rigidbody", NULL); + GameObject& colliderChild = CreateGameObject("Child", "Transform", "BoxCollider", NULL); + colliderChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform)); + + Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != root.QueryComponent(Rigidbody)); + Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == root.GetComponent(Rigidbody).GetActor()); + + colliderChild.GetComponent(Transform).SetParent(NULL); + + Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData == NULL); + + colliderChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform)); + + Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != root.QueryComponent(Rigidbody)); + Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == root.GetComponent(Rigidbody).GetActor()); +} + +/// @TODO: Test Modify hierarchy through prefab instantiate change. For example parenting change. Does it set up shapes correctly. +/// Manually verify that we dont call Recreate too often, especially when destroying a whole hierarchy. + +void RemovingRididbodyComponentReattachesStaticCollider () +{ + // Rigidbody + // - Collider + // 1) Remove Rigidbody component + // -> Rigidbody Cleanup is called once. No create is called. + // -> Collider becomes static collider + + GameObject& root = CreateGameObject("Root", "Transform", "Rigidbody", NULL); + GameObject& colliderChild = CreateGameObject("Child", "Transform", "BoxCollider", NULL); + colliderChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform)); + + Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != root.QueryComponent(Rigidbody)); + Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == root.GetComponent(Rigidbody).GetActor()); + DestroyObjectHighLevel(root.QueryComponent(Rigidbody)); + Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData == NULL); +} + +void RemovingRididbodyComponentReattachesColliderToNextRigidbody () +{ + // Rigidbody + // - Rigidbody + // - Collider + // 1) Remove Rigidbody component + // -> Rigidbody Cleanup is called once. No create is called. + // -> Collider + + GameObject& root = CreateGameObject("Root", "Transform", "Rigidbody", NULL); + GameObject& rbChild = CreateGameObject("Root", "Transform", "Rigidbody", NULL); + GameObject& colliderChild = CreateGameObject("Child", "Transform", "BoxCollider", NULL); + rbChild.GetComponent(Transform).SetParent(root.QueryComponent(Transform)); + colliderChild.GetComponent(Transform).SetParent(rbChild.QueryComponent(Transform)); + + Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData != NULL); + Assert(&colliderChild.GetComponent(BoxCollider).GetShape()->getActor() == rbChild.GetComponent(Rigidbody).GetActor()); + + DestroyObjectHighLevel(root.QueryComponent(Rigidbody)); + Assert(colliderChild.GetComponent(BoxCollider).GetShape()->getActor().userData == root.GetComponent(Rigidbody).GetActor()); +} + +#endif
\ No newline at end of file diff --git a/Runtime/Dynamics/PrimitiveCollider.h b/Runtime/Dynamics/PrimitiveCollider.h new file mode 100644 index 0000000..ec11c98 --- /dev/null +++ b/Runtime/Dynamics/PrimitiveCollider.h @@ -0,0 +1,7 @@ +#ifndef PRIMITIVECOLLIDER_H +#define PRIMITIVECOLLIDER_H + +#include "SphereCollider.h" +#include "BoxCollider.h" + +#endif diff --git a/Runtime/Dynamics/RaycastCollider.cpp b/Runtime/Dynamics/RaycastCollider.cpp new file mode 100644 index 0000000..79bfa90 --- /dev/null +++ b/Runtime/Dynamics/RaycastCollider.cpp @@ -0,0 +1,212 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "RaycastCollider.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" + +#define GET_SHAPE() ((class NxCapsuleShape*)m_Shape) + +/* + - i am not sure about the getscaled extents calculation. +*/ + +const float RaycaseCollider_kMinSize = 0.00001F; + +// Novodex bugs with thin raycast triggers. It likes the fat hairy ones! +// Remove this as soon as they have fixed this!!! +const float kMinTriggerSize = 0.05F; + + +using namespace std; + +RaycastCollider::RaycastCollider(MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +RaycastCollider::~RaycastCollider () +{ +} + +void RaycastCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if (m_Shape) + { + // Apply changed values + SetLength (m_Length); + SetCenter (m_Center); + } + + Super::AwakeFromLoad (awakeMode); +} + +void RaycastCollider::SmartReset () +{ + Super::Reset (); + AABB aabb; + if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb)) + { + SetLength (aabb.GetExtent ().y); + SetCenter (aabb.GetCenter ()); + } + else + { + SetLength (1.0F); + SetCenter (Vector3f::zero); + } +} + +void RaycastCollider::Reset () +{ + Super::Reset (); + m_Length = 1.0F; + m_Center = Vector3f::zero; +} + +Vector3f RaycastCollider::GetGlobalCenter () const +{ + return GetComponent (Transform).TransformPoint (m_Center); +} + +float RaycastCollider::GetGlobalLength () const +{ + Vector3f scale = GetComponent (Transform).GetWorldScaleLossy (); + + float absoluteHeight = max (Abs (m_Length * scale.y), RaycaseCollider_kMinSize); + return absoluteHeight; +} + +void RaycastCollider::Create (const Rigidbody* ignoreAttachRigidbody) +{ + if (m_Shape) + Cleanup (); + + NxCapsuleShapeDesc shapeDesc; + shapeDesc.radius = GetIsTrigger() ? kMinTriggerSize : RaycaseCollider_kMinSize; + shapeDesc.height = GetGlobalLength (); + shapeDesc.flags |= NX_SWEPT_SHAPE; + + FinalizeCreate (shapeDesc, true, ignoreAttachRigidbody); +} + +void RaycastCollider::SetLength (float height) +{ + if (m_Length != height) + { + SetDirty (); + m_Length = height; + } + + if (GET_SHAPE ()) + GET_SHAPE ()->setHeight (GetGlobalLength ()); +} + +void RaycastCollider::SetCenter (const Vector3f& center) +{ + if (m_Center != center) + { + m_Center = center; + SetDirty (); + } + + if (GET_SHAPE ()) + TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged); +} + +void RaycastCollider::ScaleChanged () +{ + PROFILE_MODIFY_STATIC_COLLIDER + + NxCapsuleShape* shape = GET_SHAPE (); + shape->setHeight (GetGlobalLength ()); +} + +Matrix4x4f RaycastCollider::CalculateTransform () const +{ + Transform& transform = GetComponent (Transform); + Vector3f p = transform.TransformPoint (m_Center - Vector3f (0.0F, m_Length * .5F, 0.0F)); + + Quaternionf rotation = transform.GetRotation (); + rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180)); + + Matrix4x4f matrix; + matrix.SetTR (p, rotation); + + return matrix; +} + +void RaycastCollider::FetchPoseFromTransform () +{ + AssertIf (HasActorRigidbody ()); + + Transform& transform = GetComponent (Transform); + Vector3f p = transform.TransformPoint (m_Center - Vector3f (0.0F, m_Length * .5F, 0.0F)); + AssertFiniteParameter(p) + m_Shape->getActor().setGlobalPosition ((const NxVec3&)p); + + Quaternionf rotation = transform.GetRotation (); + rotation *= AxisAngleToQuaternion (Vector3f::xAxis, Deg2Rad (180)); + + AssertFiniteParameter(rotation) + + m_Shape->getActor().setGlobalOrientationQuat ((const NxQuat&)rotation); +} + +bool RaycastCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix) +{ + Matrix4x4f childMatrix = CalculateTransform (); + Matrix4x4f parentMatrix = anyParent.GetWorldToLocalMatrixNoScale (); + MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix); + ErrorFiniteParameterReturnFalse(matrix) + return true; +} + +void RaycastCollider::TransformChanged (int changeMask) +{ + if (m_Shape) + { + if (!m_Shape->getActor ().userData) + { + 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 (changeMask & Transform::kScaleChanged) + ScaleChanged (); + } +} + +void RaycastCollider::InitializeClass () +{ + REGISTER_MESSAGE (RaycastCollider, kTransformChanged, TransformChanged, int); +} + +template<class TransferFunction> +void RaycastCollider::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.Align(); + TRANSFER_SIMPLE (m_Center); + TRANSFER_SIMPLE (m_Length); +} + +IMPLEMENT_CLASS_HAS_INIT (RaycastCollider) +IMPLEMENT_OBJECT_SERIALIZE (RaycastCollider) + +#undef GET_SHAPE +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/RaycastCollider.h b/Runtime/Dynamics/RaycastCollider.h new file mode 100644 index 0000000..53e824c --- /dev/null +++ b/Runtime/Dynamics/RaycastCollider.h @@ -0,0 +1,49 @@ +#ifndef RAYCASTCOLLIDER_H +#define RAYCASTCOLLIDER_H + +#include "Collider.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Vector2.h" + + +class RaycastCollider : public Collider +{ + public: + REGISTER_DERIVED_CLASS (RaycastCollider, Collider) + DECLARE_OBJECT_SERIALIZE (RaycastCollider) + + RaycastCollider (MemLabelId label, ObjectCreationMode mode); + + virtual void Reset (); + virtual void SmartReset (); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + float GetLength () const { return m_Length; } + void SetLength (float f); + + Vector3f GetCenter () const { return m_Center; } + void SetCenter (const Vector3f& center); + + float GetGlobalLength () const; + Vector3f GetGlobalCenter () const; + + void TransformChanged (int changeMask); + static void InitializeClass (); + static void CleanupClass () {} + Matrix4x4f CalculateTransform () const; + + protected: + + + ///@TODO ADD DIRECTION + virtual void FetchPoseFromTransform (); + virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix); + + virtual void Create (const Rigidbody* ignoreAttachRigidbody); + void ScaleChanged (); + + Vector3f m_Center; + float m_Length;///< range { 0, infinity } +}; + +#endif diff --git a/Runtime/Dynamics/RaycastHit.cpp b/Runtime/Dynamics/RaycastHit.cpp new file mode 100644 index 0000000..b888d5e --- /dev/null +++ b/Runtime/Dynamics/RaycastHit.cpp @@ -0,0 +1,48 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "RaycastHit.h" +#include "MeshCollider.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Terrain/Heightmap.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Dynamics/TerrainCollider.h" + +Vector2f CalculateRaycastTexcoord (Collider* collider, const Vector2f& uv, const Vector3f& pos, UInt32 face, int texcoord) +{ + MeshCollider* meshCollider = dynamic_pptr_cast<MeshCollider*> (collider); + if (meshCollider != NULL) + { + Mesh* mesh = meshCollider->GetSharedMesh(); + if (mesh == NULL) + return Vector2f::zero; + UInt32 indices[3]; + if (!mesh->ExtractTriangle (face, indices)) + return Vector2f::zero; + StrideIterator<Vector2f> uvs; + if (texcoord == 1 && mesh->IsAvailable (kShaderChannelTexCoord1)) + uvs = mesh->GetUvBegin (1); + else if (mesh->IsAvailable (kShaderChannelTexCoord0)) + uvs = mesh->GetUvBegin (0); + else + return Vector2f::zero; + Vector2f interpolated = uvs[indices[1]] * uv.x; + interpolated += uvs[indices[2]] * uv.y; + interpolated += uvs[indices[0]] * (1.0F - (uv.y + uv.x)); + return interpolated; + } +#if ENABLE_TERRAIN + TerrainCollider* terrainCollider = dynamic_pptr_cast<TerrainCollider*> (collider); + if (terrainCollider) + { + Vector2f uv; + Vector3f scale = terrainCollider->GetCachedInvSize(); + Vector3f transformPos = terrainCollider->GetComponent(Transform).GetPosition(); + uv.x = scale.x * (pos.x - transformPos.x); + uv.y = scale.z * (pos.z - transformPos.z); + return uv; + } + else +#endif // ENABLE_TERRAIN + return Vector2f::zero; +} +#endif //ENABLE_PHYSICS diff --git a/Runtime/Dynamics/RaycastHit.h b/Runtime/Dynamics/RaycastHit.h new file mode 100644 index 0000000..2f0f9e8 --- /dev/null +++ b/Runtime/Dynamics/RaycastHit.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Runtime/Math/Vector2.h" +#include "Collider.h" + +Vector2f CalculateRaycastTexcoord (Collider* collider, const Vector2f& uv, const Vector3f& pos, UInt32 face, int texcoord); diff --git a/Runtime/Dynamics/RigidBody.h b/Runtime/Dynamics/RigidBody.h new file mode 100644 index 0000000..6c278b1 --- /dev/null +++ b/Runtime/Dynamics/RigidBody.h @@ -0,0 +1,266 @@ +#ifndef RIGIDBODY_H +#define RIGIDBODY_H + +#include "Runtime/GameCode/Behaviour.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Math/Quaternion.h" +#include "Runtime/Math/Matrix4x4.h" +#include "PhysicsManager.h" + +class Transform; +class Quaternionf; +struct RootMotionData; + +namespace Unity { class Joint; } + +enum RigidbodyInterpolation { kNoInterpolation = 0, kInterpolate = 1, kExtrapolate = 2 }; + + +class Rigidbody : public Unity::Component { + public: + REGISTER_DERIVED_CLASS (Rigidbody, Component) + DECLARE_OBJECT_SERIALIZE (Rigidbody) + + Rigidbody (MemLabelId label, ObjectCreationMode mode); + // virtual ~Rigidbody(); declared-by-macro + virtual void Reset (); + + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + virtual void Deactivate (DeactivateOperation operation); + + static void InitializeClass (); + static void CleanupClass () {} + + // Is the rigid body affected by gravity? + void SetUseGravity (bool gravity); + bool GetUseGravity () const; + + // Mass of the rigid body + void SetMass (float mass); + float GetMass () const; + + // Center of Mass of the rigid body in local space + void SetCenterOfMass (const Vector3f& centerOfMass); + Vector3f GetCenterOfMass () const; + + // Center of Mass of the rigid body in world space + Vector3f GetWorldCenterOfMass () const; + + // The rotation of the inertia tensor + void SetInertiaTensorRotation (const Quaternionf& inertia); + Quaternionf GetInertiaTensorRotation () const; + + // The diagonal inertia tensor of mass relative to the center of mass and inertia rotation + void SetInertiaTensor (const Vector3f& inertia); + Vector3f GetInertiaTensor () const; + + // Does the rigid body modify the rotation? + void SetFreezeRotation (bool freezeRotation); + bool GetFreezeRotation () const; + + void SetConstraints (int flags); + int GetConstraints () const; + + void SetIsKinematic (bool isKinematic); + bool GetIsKinematic () const; + + void SetCollisionDetectionMode (int ccd); + int GetCollisionDetectionMode () const { return m_CollisionDetection; } + + void SetDensity (float density); + + // Get Position and rotation - This can be different from transform state when using interpolation + Vector3f GetPosition () const; + Quaternionf GetRotation () const; + + // Set pos&rot this will cause transform.position/rotation to be updated delayed after the next fixed step + void SetPosition (const Vector3f& p); + void SetRotation (const Quaternionf& q); + + /// Move to a position + /// This happens one frame delayed. + void MovePosition (const Vector3f& pos); + void MoveRotation (const Quaternionf& rot); + + // Velocity + Vector3f GetVelocity () const; + void SetVelocity (const Vector3f& velocity); + + // Angular velocity + Vector3f GetAngularVelocity () const; + void SetAngularVelocity (const Vector3f& velocity); + + /// The linear drag coefficient. 0 means no drag. range { 0, infinity } + float GetDrag () const; + void SetDrag (float damping); + + // The angular drag coefficient. 0 means no drag. range { 0, infinity } + void SetAngularDrag (float damping); + float GetAngularDrag () const; + + /// Lets you set the maximum angular velocity permitted for this rigid body. Because for various computations, the rotation + /// of an object is linearized, quickly rotating actors introduce error into the simulation, which leads to bad things. + /// + /// However, because some rigid bodies, such as car wheels, should be able to rotate quickly, you can override the default setting + /// on a per-rigid body basis with the below call. Note that objects such as wheels which are approximated with spherical or + /// other smooth collision primitives can be simulated with stability at a much higher angular velocity than, say, a box that + /// has corners. + void SetMaxAngularVelocity (float maxAngularVelocity); + float GetMaxAngularVelocity () const; + + // The velocity of a point given in world coordinates + // if it were attached to the actor and moving with it. + Vector3f GetPointVelocity (const Vector3f& worldPoint) const; + + // The velocity of a point given in world coordinates + // if it were attached to the actor and moving with it. + Vector3f GetRelativePointVelocity (const Vector3f& localPoint) const; + + enum ForceMode + { + FORCE, ///< parameter has unit of mass * distance/ time^2, i.e. a force + IMPULSE, ///< parameter has unit of mass * distance /time + VELOCITY_CHANGE, ///< parameter has unit of distance / time, i.e. the effect is mass independent: a velocity change. + SMOOTH_IMPULSE, ///< same as NX_IMPULSE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse. + SMOOTH_VELOCITY_CHANGE, ///< same as NX_VELOCITY_CHANGE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse. + ACCELERATION ///< parameter has unit of distance/ time^2, i.e. an acceleration. It gets treated just like a force except the mass is not divided out before integration. + }; + + enum { kCCDModeOff = 0, kCCDModeNormal = 1, kCCDModeDynamic = 2 }; + + enum { + kFreezeNone = 0, + kFreezePositionX = (1<<1), + kFreezePositionY = (1<<2), + kFreezePositionZ = (1<<3), + kFreezeRotationX = (1<<4), + kFreezeRotationY = (1<<5), + kFreezeRotationZ = (1<<6), + kFreezePosition = kFreezePositionX | kFreezePositionY | kFreezePositionZ, + kFreezeRotation = kFreezeRotationX | kFreezeRotationY | kFreezeRotationZ, + kFreezeAll = kFreezePosition | kFreezeRotation, + }; + + // Applies a force (or impulse) defined in the global coordinate frame, + // acting at a particular point in global coordinates on the rigid body. + // Note that if the force does not act along the center of mass of the actor, this + // will also add the corresponding torque. + // Forces should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep. + void AddForceAtPosition (const Vector3f& force, const Vector3f& position, int mode = FORCE); + + // Applies a force (or impulse) defined in the global coordinate frame, + // This will not induce torque. + // Forces should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep. + void AddForce (const Vector3f& force, int mode = FORCE); + + // Applies a force (or impulse) defined in the local coordinate frame, + // This will not induce torque. + // Forces should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep. + void AddRelativeForce (const Vector3f& force, int mode = FORCE); + + // Applies an (eventually impulsive) torque defined in the global coordinate frame to the actor. + // Torques should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep. + void AddTorque (const Vector3f& torque, int mode = FORCE); + + // Applies an (eventually impulsive) torque defined in the local coordinate frame to the actor. + // Torques should be applied inside FixedUpdate only, because forces are reset at the end of every fixed timestep. + void AddRelativeTorque (const Vector3f& torque, int mode = FORCE); + + + void AddExplosionForce (float force, const Vector3f& position, float radius, float upwardsModifier, int forceMode = FORCE); + void ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance); + + bool GetDetectCollisions() const; + void SetDetectCollisions(bool enable); + + RigidbodyInterpolation GetInterpolation () { return (RigidbodyInterpolation)m_Interpolate; } + void SetInterpolation (RigidbodyInterpolation interpolation); + + bool GetUseConeFriction () const; + void SetUseConeFriction (bool cone); + + bool IsSleeping (); + void Sleep (); + void WakeUp (); + + void SetSolverIterationCount (int iterationCount); + int GetSolverIterationCount () const; + + void SetSleepVelocity (float value); + float GetSleepVelocity () const; + + void SetSleepAngularVelocity (float value); + float GetSleepAngularVelocity () const; + + void TransformChanged (int mask); + void ApplyRootMotionBuiltin (RootMotionData* rootMotion); + + bool SweepTest (const Vector3f &direction, float distance, RaycastHit& outHit); + const PhysicsManager::RaycastHits& SweepTestAll (const Vector3f &direction, float distance); + + virtual void CheckConsistency (); + + // Testing API + NxActor* GetActor() { return m_Actor; } + + private: + + void SortParentedRigidbodies (); + void FetchPoseFromTransform (); + virtual void SupportedMessagesDidChange (int supported); + void UpdateSortedBody (); + + void Create (bool active); + void CleanupInternal (bool recreateColliders); + void UpdateInterpolationNode (); + + void UpdateMassDistribution (); + ListNode<Rigidbody> m_SortedNode; + class NxActor* m_Actor; + + float m_Mass;///< The mass of the body. range { 0.0000001, 1000000000 } + float m_Drag;///< The linear drag coefficient. 0 means no damping. range { 0, infinity } + float m_AngularDrag; ///< The angular drag coefficient. 0 means no damping. range { 0, infinity } + + UInt8 m_ActiveScene; + UInt8 m_ImplicitTensor; + + bool m_UseGravity; + bool m_IsKinematic; + int m_Constraints; + int m_CollisionDetection;///< enum { Discrete = 0, Continuous = 1, Continuous Dynamic = 2 } + int m_CachedCollisionDetection; + + public: + + /// This is used to prevent read back from kinematic rigidbodies. + /// * setGlobalPosition sets the position immediately. No triggers will be activated. + /// No velocity applied -> thus no friction for rigidbodies sitting on animated objects. + /// * moveGlobalPosition sets the position delayed during simulate. Simply calling moveGlobalPosition for all kinematic objects + /// doesn't work because then the physics update loop will update delayed, causing small epsilon precision errors to accumulate + /// from the local to world, then world to local transformation. If the transform which has the kinematic rigidbody attached + /// is not actually animated, the ragdoll will slowly drift apart. + /// On top of that, it is slow since we read back the position twice for animated ragdolls and characters. + /// ->> The solution is to store if we should read back the update. + /// If we are setting the position directly from a Transform update, there is no reason to update the transform + /// Also the world position wont change during simulation since it is kinematic rigidbody. + + + UInt8 m_Interpolate;///< enum { None = 0, Interpolate = 1, Extrapolate = 2 } + int m_DisableReadUpdateTransform; + int m_DisableInterpolation; + RigidbodyInterpolationInfo* m_InterpolationInfo; + + friend class Collider; + friend class Unity::Joint; + friend class PhysicsManager; +}; + +/// Notifications: +/// bool RigidbodyChanged (const Rigidbody& b); +extern MessageIdentifier kRigidbodyChanged; + + + +#endif diff --git a/Runtime/Dynamics/Rigidbody.cpp b/Runtime/Dynamics/Rigidbody.cpp new file mode 100644 index 0000000..6ca8af6 --- /dev/null +++ b/Runtime/Dynamics/Rigidbody.cpp @@ -0,0 +1,1097 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "RigidBody.h" +#include "Collider.h" +#include "Joint.h" +#include "PhysicsManager.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/BaseClasses/SupportedMessageOptimization.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "NxWrapperUtility.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/BaseClasses/MessageHandler.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Utilities/ValidateArgs.h" +#include "Runtime/GameCode/RootMotionData.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/Misc/BuildSettings.h" + +using namespace Unity; +using namespace std; + +PROFILER_INFORMATION(gSweepTestProfile, "Rigidbody.SweepTest", kProfilerPhysics) +PROFILER_INFORMATION(gSweepTestAllProfile, "Rigidbody.SweepTestAll", kProfilerPhysics) + +/* + - Rigid bodies can't change the inertia tensor + - Transfer function should be optimize to serialize into desc data if rigid body doesn't exit yet (Possibly always) + + - Make it so you can have multiple rigidbodies inside of each other and they update in the right order! + What about sleeping?????? + +*/ + + +inline Quaternionf QuatFromNx(const NxQuat& q) +{ + return Quaternionf(q.x, q.y, q.z, q.w); +} + + + +Rigidbody::Rigidbody (MemLabelId label, ObjectCreationMode mode) + : Super(label, mode), + m_SortedNode(this) +{ + m_Actor = NULL; + m_ImplicitTensor = true; + m_InterpolationInfo = NULL; + m_DisableReadUpdateTransform = 0; + m_CollisionDetection = kCCDModeOff; + m_CachedCollisionDetection = kCCDModeOff; +} + +Rigidbody::~Rigidbody () +{ + CleanupInternal (false); +} + +void Rigidbody::Reset () +{ + Super::Reset (); + + if (m_Actor) + { + SetMass(1.0F); + SetAngularDrag(0.05f); + SetDrag(0.0F); + SetConstraints(kFreezeNone); + SetIsKinematic(false); + SetUseGravity(true); + SetCollisionDetectionMode(kCCDModeOff); + } + else + { + m_Mass = 1.0F; + m_AngularDrag = 0.05f; + m_Drag = 0.0f; + m_Constraints = kFreezeNone; + m_IsKinematic = false; + m_UseGravity = true; + m_CollisionDetection = kCCDModeOff; + m_CachedCollisionDetection = kCCDModeOff; + } + m_Interpolate = kNoInterpolation; +} + +void Rigidbody::CheckConsistency () +{ + Super::CheckConsistency (); + m_Mass = max(0.0000001F, m_Mass); +} + +// Because Component -> Deactivate can be called but IsActive might still return true, we need to pass the active state! +void Rigidbody::Create (bool isActive) +{ + if (m_Actor == NULL || (bool)m_ActiveScene != isActive) + { + NxBodyDesc bodyDesc; + bodyDesc.solverIterationCount = GetPhysicsManager ().GetSolverIterationCount (); + NxActorDesc actorDesc; + + if (m_Actor) + { + m_Actor->saveBodyToDesc (bodyDesc); + CleanupInternal (true); + } + else + { + bodyDesc.massSpaceInertia = NxVec3 (1.0F, 1.0F, 1.0F); + bodyDesc.mass = m_Mass; + bodyDesc.linearDamping = m_Drag; + bodyDesc.angularDamping = m_AngularDrag; + bodyDesc.mass = m_Mass; + if (!m_UseGravity) + bodyDesc.flags |= NX_BF_DISABLE_GRAVITY; + if (m_IsKinematic) + bodyDesc.flags |= NX_BF_KINEMATIC; + bodyDesc.flags |= m_Constraints; + } + actorDesc.body = &bodyDesc; + actorDesc.userData = this; + + if (isActive) + { + //;;printf_console ("Creating active physics actor\n"); + m_Actor = GetDynamicsScene ().createActor (actorDesc); + SupportedMessagesDidChange (GetGameObject ().GetSupportedMessages ()); + m_ActiveScene = true; + } + else + { + //;;printf_console ("Creating inactive physics actor\n"); + m_Actor = GetInactiveDynamicsScene ().createActor (actorDesc); + + m_ActiveScene = false; + } + + UpdateInterpolationNode(); + } + + if (!isActive && m_Actor != NULL) + { + Assert(m_Actor->getNbShapes() == 0); + } +} + + +void Rigidbody::SupportedMessagesDidChange (int supported) +{ + if (m_Actor) + { + if (supported & kHasCollisionStay) + m_Actor->setGroup (kContactTouchGroup); + else if (supported & (kHasCollisionStay | kHasCollisionEnterExit)) + m_Actor->setGroup (kContactEnterExitGroup); + else + m_Actor->setGroup (kContactNothingGroup); + } +} + +void Rigidbody::CleanupInternal (bool recreateColliders) +{ + if (m_Actor) + { + int shapeCount = m_Actor->getNbShapes (); + NxShape*const * shapes = m_Actor->getShapes (); + + Collider** colliders; + ALLOC_TEMP(colliders, Collider*, shapeCount) + + for (int i=0;i<shapeCount;i++) + { + Collider* collider = (Collider*)shapes[i]->userData; + AssertIf (collider == NULL); + colliders[i] = collider; + collider->Cleanup (); + } + + if (m_ActiveScene) + { + //;;printf_console ("Deleting active physics actor"); + GetDynamicsScene ().releaseActor (*m_Actor); + } + else + { + //;;printf_console ("Deleting inactive physics actor"); + GetInactiveDynamicsScene ().releaseActor (*m_Actor); + } + m_Actor = NULL; + + if (recreateColliders) + { + for (int i=0;i<shapeCount;i++) + { + colliders[i]->RecreateCollider (this); + } + } + + delete m_InterpolationInfo; + m_InterpolationInfo = NULL; + m_CachedCollisionDetection = m_CollisionDetection; + } + Assert(m_Actor == NULL); + m_SortedNode.RemoveFromList(); +} + +Vector3f Rigidbody::GetWorldCenterOfMass () const +{ + return Vec3FromNx (m_Actor->getCMassGlobalPosition()); +} + +void Rigidbody::Deactivate (DeactivateOperation operation) +{ + // When we are about to destroy a rigidbody, we don't destroy it immediately + // Instead we destroy it in the rigidbody destructor. + // This way scripts have no chance of accessing the colliders. + if (operation == kWillDestroyGameObjectDeactivate) + ; + else + { + Create (false); + } + m_SortedNode.RemoveFromList(); + + Super::Deactivate (operation); +} + +void Rigidbody::UpdateSortedBody () +{ + m_SortedNode.RemoveFromList(); + if (m_ActiveScene) + GetPhysicsManager().AddBody(GetTransformDepth(GetComponent(Transform)), m_SortedNode); +} + +void Rigidbody::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + + AssertIf (GameObject::GetMessageHandler ().HasMessageCallback (ClassID (Rigidbody), kTransformChanged.messageID) == false); + + Create (IsActive ()); + + // When modifying already loaded rigidbody + // Apply properties through immediate mode function + if (!(awakeMode & kDidLoadFromDisk)) + { + SetIsKinematic(m_IsKinematic); + SetMass(m_Mass); + SetDrag(m_Drag); + SetAngularDrag(m_AngularDrag); + SetUseGravity(m_UseGravity); + SetCollisionDetectionMode(m_CollisionDetection); + SetConstraints(m_Constraints); + } + + UpdateInterpolationNode(); + + if (IsActive()) + FetchPoseFromTransform(); + + if (!GetIsKinematic()) + m_DisableReadUpdateTransform = 0; + + if (awakeMode & kActivateAwakeFromLoad) + { + MessageData data; + GetComponent(Transform).BroadcastMessageAny(kForceRecreateCollider, data); + } + + UpdateSortedBody (); +} + +void Rigidbody::ClosestPointOnBounds (const Vector3f& position, Vector3f& outPosition, float& outSqrDistance) +{ + // No collider - just the distance to the center of mass + int count = m_Actor->getNbShapes(); + if (count == 0) + { + outPosition = GetWorldCenterOfMass(); + outSqrDistance = SqrMagnitude(position - outPosition); + return; + } + + outSqrDistance = std::numeric_limits<float>::infinity(); + + NxShape*const * shapes = m_Actor->getShapes(); + for (int i=0;i<count;i++) + { + NxBounds3 bounds; + shapes[i]->getWorldBounds(bounds); + AABB aabb; + bounds.getCenter((NxVec3&)aabb.GetCenter()); + bounds.getExtents((NxVec3&)aabb.GetExtent()); + + Vector3f closest; + float sqrDistance; + + CalculateClosestPoint(position, aabb, closest, sqrDistance); + if (sqrDistance < outSqrDistance) + { + outPosition = closest; + outSqrDistance = sqrDistance; + } + } +} + +void Rigidbody::AddExplosionForce (float force, const Vector3f& position, float radius, float upwardsModifier, int forceMode) +{ + Vector3f pointOnSurface; + float sqrDistance; + + Vector3f offsetPosition = position - Vector3f(0.0F, upwardsModifier, 0.0F); + if (upwardsModifier == 0.0F) + { + ClosestPointOnBounds(position, pointOnSurface, sqrDistance); + } + else + { + /// Upwards modifier will not modify the distance + /// But it will modify the point on the surface + ClosestPointOnBounds(position, pointOnSurface, sqrDistance); + + float tmpDistance; + ClosestPointOnBounds(offsetPosition, pointOnSurface, tmpDistance); + } + + // Linear distance fall off + float distanceScale; + if (radius > Vector3f::epsilon) + distanceScale = 1.0F - clamp01(sqrtf(sqrDistance) / radius); + else + distanceScale = 1.0F; + + // Calculate normalized direction towards surface point + Vector3f direction = pointOnSurface - offsetPosition; + + float length = Magnitude(direction); + if (length > Vector3f::epsilon) + direction /= length; + else + direction = Vector3f (0.0F, 1.0F, 0.0F); + + AddForceAtPosition(force * distanceScale * direction, pointOnSurface, forceMode); +} + +void Rigidbody::InitializeClass () +{ + REGISTER_MESSAGE (Rigidbody, kTransformChanged, TransformChanged, int); + REGISTER_MESSAGE_PTR (Rigidbody, kAnimatorMoveBuiltin, ApplyRootMotionBuiltin, RootMotionData); +} + +template<class TransferFunction> +void Rigidbody::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.SetVersion (2); + TRANSFER_SIMPLE (m_Mass); + TRANSFER_SIMPLE (m_Drag); + TRANSFER_SIMPLE (m_AngularDrag); + TRANSFER_SIMPLE (m_UseGravity); + TRANSFER (m_IsKinematic); + + transfer.Transfer (m_Interpolate, "m_Interpolate"); + if (transfer.IsOldVersion(1)) + { + bool freezeRotation; + transfer.Transfer (freezeRotation, "m_FreezeRotation"); + if (freezeRotation) + m_Constraints = kFreezeRotation; + else + m_Constraints = kFreezeNone; + } + else + { + transfer.Align(); + + // Hide in editor and show using custom inspector instead. + transfer.Transfer (m_Constraints, "m_Constraints", kHideInEditorMask | kGenerateBitwiseDifferences); + } + + TRANSFER (m_CollisionDetection); +} + +void Rigidbody::SetUseGravity (bool value) +{ + AssertIf (m_Actor == NULL); + SetDirty (); + if (value) + { + m_Actor->clearBodyFlag (NX_BF_DISABLE_GRAVITY); + m_Actor->wakeUp(); + } + else + m_Actor->raiseBodyFlag (NX_BF_DISABLE_GRAVITY); + m_UseGravity = value; +} + +bool Rigidbody::GetUseGravity () const +{ + AssertIf (m_Actor == NULL); + return !m_Actor->readBodyFlag (NX_BF_DISABLE_GRAVITY); +} + +void Rigidbody::SetFreezeRotation (bool value) +{ + if (value) + SetConstraints (GetConstraints() | kFreezeRotation); + else + SetConstraints (GetConstraints() & ~kFreezeRotation); +} + +bool Rigidbody::GetFreezeRotation () const +{ + return (GetConstraints () & kFreezeRotation) == kFreezeRotation; +} + +void Rigidbody::SetConstraints (int value) +{ + AssertIf (m_Actor == NULL); + + // If we are removing constraints, wake up the rigidbody, so it can start moving. + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_3_a1) && m_Constraints & ~value) + WakeUp(); + + SetDirty (); + value &= NX_BF_FROZEN; + m_Actor->clearBodyFlag (NX_BF_FROZEN); + m_Actor->raiseBodyFlag ((NxBodyFlag)value); + m_Constraints = value; +} + +int Rigidbody::GetConstraints () const +{ + AssertIf (m_Actor == NULL); + + int value = 0; + for (int i = kFreezePositionX; i <= kFreezeRotationZ; i<<=1) + { + if (m_Actor->readBodyFlag ((NxBodyFlag)i)) + value |= i; + } + return value; +} + +void Rigidbody::SetIsKinematic (bool value) +{ + AssertIf (m_Actor == NULL); + + SetDirty (); + if (value) + m_Actor->raiseBodyFlag (NX_BF_KINEMATIC); + else + m_Actor->clearBodyFlag (NX_BF_KINEMATIC); + m_IsKinematic = value; + UpdateInterpolationNode (); + + m_DisableReadUpdateTransform = 0; +} + +bool Rigidbody::GetIsKinematic () const +{ + AssertIf (m_Actor == NULL); + return m_Actor->readBodyFlag (NX_BF_KINEMATIC); +} + +void Rigidbody::UpdateMassDistribution () +{ + AssertIf (m_Actor == NULL); + + if (m_ImplicitTensor) + { + NxShape*const * shapes = m_Actor->getShapes(); + int count = m_Actor->getNbShapes(); + for (int i=0;i<count;i++) + { + Collider* coll = (Collider*)(shapes[i]->userData); + + // Triggers don't have mass, and RaycastColliders are too thing to give good mathematical useful results. + if (!shapes[i]->getFlag(NX_TRIGGER_ENABLE) && !(coll && coll->GetClassID() == 140)) + { + m_Actor->updateMassFromShapes (0.0F, m_Mass); + return; + } + } + + // no usable shapes - reset actor CoG and inertia + m_Actor->setMass (m_Mass); + m_Actor->setCMassOffsetLocalPosition ((const NxVec3&)Vector3f::zero); + m_Actor->setMassSpaceInertiaTensor ((const NxVec3&)Vector3f::one); + } +} + +void Rigidbody::SetMass (float mass) +{ + AssertIf (m_Actor == NULL); + SetDirty (); + m_Mass = mass; + + if (m_ImplicitTensor) + UpdateMassDistribution(); + else + m_Actor->setMass (mass); +} + +float Rigidbody::GetMass () const +{ + AssertIf (m_Actor == NULL); + return m_Actor->getMass (); +} + +void Rigidbody::SetCenterOfMass (const Vector3f& centerOfMass) +{ + m_Actor->setCMassOffsetLocalPosition ((const NxVec3&)centerOfMass); + m_ImplicitTensor = false; +} + +Vector3f Rigidbody::GetCenterOfMass () const +{ + return Vec3FromNx(m_Actor->getCMassLocalPosition ()); +} + +void Rigidbody::SetInertiaTensorRotation (const Quaternionf& inertia) +{ + m_ImplicitTensor = false; + NxMat33 matrix ((const NxQuat&)inertia); + m_Actor->setCMassOffsetLocalOrientation (matrix); +} + +Quaternionf Rigidbody::GetInertiaTensorRotation () const +{ + NxMat33 matrix = m_Actor->getCMassLocalOrientation (); + NxQuat quat (matrix); + return (const Quaternionf&)quat; +} + +void Rigidbody::SetInertiaTensor (const Vector3f& inertia) +{ + m_ImplicitTensor = false; + if (inertia.x > std::numeric_limits<float>::epsilon() && + inertia.y > std::numeric_limits<float>::epsilon() && + inertia.z > std::numeric_limits<float>::epsilon()) + m_Actor->setMassSpaceInertiaTensor ((const NxVec3&)inertia); + else + ErrorStringObject ("Inertia tensor must be larger then zero in all coordinates.", this); +} + +Vector3f Rigidbody::GetInertiaTensor () const +{ + return Vec3FromNx(m_Actor->getMassSpaceInertiaTensor ()); +} + +Vector3f Rigidbody::GetVelocity () const +{ + AssertIf (m_Actor == NULL); + return Vec3FromNx(m_Actor->getLinearVelocity ()); +} + +void Rigidbody::SetVelocity (const Vector3f& velocity) +{ + AssertIf (m_Actor == NULL); + ABORT_INVALID_VECTOR3 (velocity, velocity, rigidbody); + m_Actor->setLinearVelocity ((const NxVec3&)velocity); +} + +Vector3f Rigidbody::GetAngularVelocity () const +{ + AssertIf (m_Actor == NULL); + return Vec3FromNx(m_Actor->getAngularVelocity ()); +} + +void Rigidbody::SetAngularVelocity (const Vector3f& velocity) +{ + AssertIf (m_Actor == NULL); + ABORT_INVALID_VECTOR3 (velocity, angularVelocity, rigidbody); + m_Actor->setAngularVelocity ((const NxVec3&)velocity); +} + +void Rigidbody::SetDrag (float damping) +{ + AssertIf (m_Actor == NULL); + SetDirty (); + m_Drag = damping; + m_Actor->setLinearDamping (damping); +} + +float Rigidbody::GetDrag () const +{ + AssertIf (m_Actor == NULL); + return m_Actor->getLinearDamping (); +} + +void Rigidbody::SetAngularDrag (float damping) +{ + AssertIf (m_Actor == NULL); + SetDirty (); + m_AngularDrag = damping; + m_Actor->setAngularDamping (damping); +} + +float Rigidbody::GetAngularDrag () const +{ + AssertIf (m_Actor == NULL); + return m_Actor->getAngularDamping (); +} + +void Rigidbody::SetMaxAngularVelocity (float maxAngularVelocity) +{ + AssertIf (m_Actor == NULL); + return m_Actor->setMaxAngularVelocity (maxAngularVelocity); +} + +float Rigidbody::GetMaxAngularVelocity () const +{ + AssertIf (m_Actor == NULL); + return m_Actor->getMaxAngularVelocity (); +} + +void Rigidbody::SetSolverIterationCount (int iterationCount) +{ + return m_Actor->setSolverIterationCount(iterationCount); +} + +int Rigidbody::GetSolverIterationCount () const +{ + return m_Actor->getSolverIterationCount(); +} + +void Rigidbody::SetSleepVelocity (float value) +{ + m_Actor->setSleepLinearVelocity(value); +} + +float Rigidbody::GetSleepVelocity () const +{ + return m_Actor->getSleepLinearVelocity(); +} + +void Rigidbody::SetSleepAngularVelocity (float value) +{ + m_Actor->setSleepAngularVelocity(value); +} + +float Rigidbody::GetSleepAngularVelocity () const +{ + return m_Actor->getSleepAngularVelocity(); +} + +void Rigidbody::SetInterpolation (RigidbodyInterpolation interpolation) +{ + m_Interpolate = interpolation; + UpdateInterpolationNode(); +} + +Vector3f Rigidbody::GetPointVelocity (const Vector3f& worldPoint) const +{ + AssertIf (m_Actor == NULL); + return Vec3FromNx(m_Actor->getPointVelocity ((const NxVec3&)worldPoint)); +} + +Vector3f Rigidbody::GetRelativePointVelocity (const Vector3f& localPoint) const +{ + AssertIf (m_Actor == NULL); + return Vec3FromNx(m_Actor->getLocalPointVelocity ((const NxVec3&)localPoint)); +} + +void Rigidbody::AddForceAtPosition (const Vector3f& force, const Vector3f& position, int mode) +{ + ABORT_INVALID_VECTOR3 (force, force, rigidbody) + ABORT_INVALID_VECTOR3 (position, position, rigidbody) + AssertIf (m_Actor == NULL); + if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC)) + m_Actor->addForceAtPos ((const NxVec3&)force, (const NxVec3&)position, (NxForceMode)mode); +} + +void Rigidbody::AddForce (const Vector3f& force, int mode) +{ + ABORT_INVALID_VECTOR3 (force, force, rigidbody) + AssertIf (m_Actor == NULL); + if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC)) + m_Actor->addForce ((const NxVec3&)force, (NxForceMode)mode); +} + +void Rigidbody::AddRelativeForce (const Vector3f& force, int mode) +{ + ABORT_INVALID_VECTOR3 (force, force, rigidbody) + AssertIf (m_Actor == NULL); + if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC)) + m_Actor->addLocalForce ((const NxVec3&)force, (NxForceMode)mode); +} + +void Rigidbody::AddTorque (const Vector3f& torque, int mode) +{ + ABORT_INVALID_VECTOR3 (torque, torque, rigidbody) + AssertIf (m_Actor == NULL); + if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC)) + m_Actor->addTorque ((const NxVec3&)torque, (NxForceMode)mode); +} + +void Rigidbody::AddRelativeTorque (const Vector3f& torque, int mode) +{ + ABORT_INVALID_VECTOR3 (torque, torque, rigidbody) + AssertIf (m_Actor == NULL); + if (!m_Actor->readBodyFlag(NX_BF_KINEMATIC)) + m_Actor->addLocalTorque ((const NxVec3&)torque, (NxForceMode)mode); +} + +void Rigidbody::FetchPoseFromTransform () +{ + AssertIf (m_Actor == NULL); + Transform& transform = GetComponent (Transform); + Vector3f pos = transform.GetPosition (); + Quaternionf rot = transform.GetRotation (); + NxMat33 nxrot ((const NxQuat&)rot); + NxMat34 pose (nxrot, Vec3ToNx(pos)); + + AssertFiniteParameter(pos) + AssertFiniteParameter(rot) + + if (GetIsKinematic()) + { + m_Actor->setGlobalPose (pose); + // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated! + m_Actor->moveGlobalPose (pose); + m_DisableReadUpdateTransform = 1; + } + else + { + m_Actor->setGlobalPose (pose); + } +} + + +void Rigidbody::TransformChanged (int mask) +{ + if (m_Actor) + { + bool kine = GetIsKinematic(); + + // When reading transform positions back from PhysX, we don't want to write back to PhysX again. + // However, when a Rigidbody is kinematic or sleeping, and is moved because a parent rigidbody has changed, + // we still want to update the position. + if (GetPhysicsManager().IsRigidbodyTransformMessageEnabled() || kine || m_Actor->isSleeping()) + { + Transform& transform = GetComponent (Transform); + const int posRotMask = Transform::kRotationChanged | Transform::kPositionChanged; + + if ((mask & posRotMask) == posRotMask || (mask & Transform::kScaleChanged)) + { + Vector3f pos = transform.GetPosition (); + Quaternionf rot = transform.GetRotation (); + NxMat33 nxrot ((const NxQuat&)rot); + NxMat34 pose (nxrot, Vec3ToNx(pos)); + + AssertFiniteParameter(pos) + AssertFiniteParameter(rot) + + + if (kine) + { + if ((mask & Transform::kAnimatePhysics) == 0) + m_Actor->setGlobalPose (pose); + // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated! + m_Actor->moveGlobalPose (pose); + m_DisableReadUpdateTransform = 1; + } + else + { + m_Actor->setGlobalPose (pose); + if (m_InterpolationInfo) + m_InterpolationInfo->disabled = 1; + } + } + else if (mask & Transform::kRotationChanged) + { + Quaternionf rot = transform.GetRotation (); + AssertFiniteParameter(rot) + if (kine) + { + if ((mask & Transform::kAnimatePhysics) == 0) + m_Actor->setGlobalOrientationQuat ((const NxQuat&)rot); + // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated! + m_Actor->moveGlobalOrientationQuat ((const NxQuat&)rot); + m_DisableReadUpdateTransform = 1; + } + else + { + m_Actor->setGlobalOrientationQuat ((const NxQuat&)rot); + + if (m_InterpolationInfo) + m_InterpolationInfo->disabled = 1; + } + } + else if (mask & Transform::kPositionChanged) + { + Vector3f pos = transform.GetPosition (); + AssertFiniteParameter(pos) + + if (kine) + { + if ((mask & Transform::kAnimatePhysics) == 0) + m_Actor->setGlobalPosition ((const NxVec3&)pos); + // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated! + m_Actor->moveGlobalPosition ((const NxVec3&)pos); + m_DisableReadUpdateTransform = 1; + } + else + { + m_Actor->setGlobalPosition ((const NxVec3&)pos); + if (m_InterpolationInfo) + m_InterpolationInfo->disabled = 1; + } + + } + } + } + //printf_console ("Changed transform :%s\n", GetName ().c_str ()); +} + +void Rigidbody::ApplyRootMotionBuiltin (RootMotionData* rootMotion) +{ + if (m_Actor == NULL || rootMotion->didApply) + return; + + if(GetIsKinematic()) + { + SetPosition(GetPosition() + rootMotion->deltaPosition); + SetRotation(rootMotion->targetRotation); + } + else + { + Quaternionf rotation = GetRotation(); + Quaternionf invRotation = Inverse(rotation); + + // Get the physics velocity in local space + Vector3f physicsVelocityLocal = RotateVectorByQuat(invRotation, GetVelocity()); + + // Get the local space velocity and blend it with the physics velocity on the y-axis based on gravity weight + // We do this in local space in order to support moving on a curve earth with gravity changing direction + Vector3f animVelocityGlobal = rootMotion->deltaPosition * GetInvDeltaTime(); + Vector3f localVelocity = RotateVectorByQuat (invRotation, animVelocityGlobal); + localVelocity.y = Lerp (localVelocity.y, physicsVelocityLocal.y, rootMotion->gravityWeight); + + // If we use gravity, when we are in a jumping root motion, we have to cancel out the gravity + // applied by physX by default. When doing for example a jump the only thing affecting velocity should be the animation data. + // The animation already has gravity applied in the animation data so to speak... + if (GetUseGravity()) + AddForce(GetPhysicsManager().GetGravity() * -Lerp(1.0F, 0.0F, rootMotion->gravityWeight)); + + // Apply velocity & rotation + Vector3f globalVelocity = RotateVectorByQuat(rotation, localVelocity); + SetVelocity(globalVelocity); + MoveRotation(rootMotion->targetRotation); + } + + rootMotion->didApply = true; +} + + +Vector3f Rigidbody::GetPosition () const +{ + AssertIf (m_Actor == NULL); + return Vec3FromNx (m_Actor->getGlobalPosition ()); +} + +Quaternionf Rigidbody::GetRotation () const +{ + AssertIf (m_Actor == NULL); + return QuatFromNx(m_Actor->getGlobalOrientationQuat ()); +} + +void Rigidbody::SetPosition (const Vector3f& position) +{ + ABORT_INVALID_VECTOR3 (position, position, rigidbody); + if (GetIsKinematic()) + { + m_Actor->setGlobalPosition ((const NxVec3&)position); + // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated! + m_Actor->moveGlobalPosition ((const NxVec3&)position); + m_DisableReadUpdateTransform = 0; + } + else + { + if (m_InterpolationInfo) + m_InterpolationInfo->disabled = 1; + + m_Actor->setGlobalPosition ((const NxVec3&)position); + } +} + +void Rigidbody::SetRotation (const Quaternionf& rotation) +{ + ABORT_INVALID_QUATERNION (rotation, rotation, rigidbody); + + if (GetIsKinematic()) + { + m_Actor->setGlobalOrientationQuat ((const NxQuat&)rotation); + // Novodex workaround ->Move global pose needs to always be called in order to make sure triggers get activated! + m_Actor->moveGlobalOrientationQuat ((const NxQuat&)rotation); + m_DisableReadUpdateTransform = 0; + } + else + { + if (m_InterpolationInfo) + m_InterpolationInfo->disabled = 1; + + m_Actor->setGlobalOrientationQuat ((const NxQuat&)rotation); + } +} + +void Rigidbody::MovePosition (const Vector3f& position) +{ + ABORT_INVALID_VECTOR3 (position, position, rigidbody); + if (GetIsKinematic()) + { + m_Actor->moveGlobalPosition ((const NxVec3&)position); + m_DisableReadUpdateTransform = 0; + } + else + { + m_Actor->setGlobalPosition ((const NxVec3&)position); + } +} + +void Rigidbody::MoveRotation (const Quaternionf& rotation) +{ + ABORT_INVALID_QUATERNION (rotation, rotation, rigidbody); + + if (GetIsKinematic()) + { + m_Actor->moveGlobalOrientation ((const NxQuat&)rotation); + m_DisableReadUpdateTransform = 0; + } + else + { + m_Actor->setGlobalOrientation ((const NxQuat&)rotation); + } +} + +void Rigidbody::SetDensity (float density) +{ + if (m_Actor) + m_Actor->updateMassFromShapes (density, 0.0F); +} + +bool Rigidbody::IsSleeping () +{ + return m_Actor->isSleeping(); +} + +void Rigidbody::Sleep () +{ + return m_Actor->putToSleep(); +} + +void Rigidbody::WakeUp () +{ + return m_Actor->wakeUp(); +} + + +bool Rigidbody::GetDetectCollisions() const +{ + if (m_Actor) + return !m_Actor->readActorFlag(NX_AF_DISABLE_COLLISION); + else + return true; +} + +void Rigidbody::SetDetectCollisions(bool enable) +{ + if (m_Actor) + { + if (enable) + m_Actor->clearActorFlag (NX_AF_DISABLE_COLLISION); + else + m_Actor->raiseActorFlag (NX_AF_DISABLE_COLLISION); + } +} + +bool Rigidbody::GetUseConeFriction() const +{ + if (m_Actor) + return !m_Actor->readActorFlag(NX_AF_FORCE_CONE_FRICTION); + else + return true; +} + +void Rigidbody::SetUseConeFriction(bool enable) +{ + if (m_Actor) + { + if (enable) + m_Actor->clearActorFlag (NX_AF_FORCE_CONE_FRICTION); + else + m_Actor->raiseActorFlag (NX_AF_FORCE_CONE_FRICTION); + } +} + +void Rigidbody::UpdateInterpolationNode () +{ + if (m_Interpolate == kNoInterpolation || !m_ActiveScene) + { + delete m_InterpolationInfo; + m_InterpolationInfo = NULL; + } + else + { + if (m_InterpolationInfo == NULL) + { + m_InterpolationInfo = new RigidbodyInterpolationInfo(); + RigidbodyInterpolationInfo& info = *m_InterpolationInfo; + info.body = this; + info.disabled = 1; + info.position = Vector3f::zero; + info.rotation = Quaternionf::identity(); + GetPhysicsManager().GetInterpolatedBodies().push_back(*m_InterpolationInfo); + } + } +} + +void Rigidbody::SetCollisionDetectionMode (int ccd) +{ + if (ccd != m_CachedCollisionDetection) + { + m_CollisionDetection = ccd; + m_CachedCollisionDetection = ccd; + + if (m_Actor) + { + int shapeCount = m_Actor->getNbShapes (); + NxShape*const * shapes = m_Actor->getShapes (); + + for (int i=0;i<shapeCount;i++) + { + Collider* collider = (Collider*)shapes[i]->userData; + collider->ReCreate(); + } + } + + SetDirty(); + } +} + + +bool Rigidbody::SweepTest (const Vector3f &direction, float distance, RaycastHit& outHit) +{ + AssertIf (!IsNormalized (direction)); + PROFILER_AUTO(gSweepTestProfile, NULL) + + if (m_Actor) + { + if (distance == std::numeric_limits<float>::infinity()) + // CapsuleCasts fail when using NX_MAX_F32 here. + // So pick a lower "high" number instead. + distance = 1000000.0f; + + NxSweepQueryHit hit; + NxU32 nb = m_Actor->linearSweep ((const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS, NULL, 1, &hit, NULL); + if (nb) + { + NxToRaycastHit(hit, distance, outHit); + return true; + } + } + return false; +} + +#define kSweepMaxHits 128 +const PhysicsManager::RaycastHits& Rigidbody::SweepTestAll (const Vector3f &direction, float distance) +{ + AssertIf (!IsNormalized (direction)); + PROFILER_AUTO(gSweepTestAllProfile, NULL) + + if (distance == std::numeric_limits<float>::infinity()) + // CapsuleCasts fail when using NX_MAX_F32 here. + // So pick a lower "high" number instead. + distance = 1000000.0f; + + static vector<RaycastHit> outHits; + + if (m_Actor) + { + NxSweepQueryHit hits[kSweepMaxHits]; + NxU32 nb = m_Actor->linearSweep ((const NxVec3&)direction * distance, NX_SF_DYNAMICS|NX_SF_STATICS|NX_SF_ALL_HITS, NULL, kSweepMaxHits, hits, NULL); + + outHits.resize(nb); + for (int i=0; i<nb; i++) + NxToRaycastHit(hits[i], distance, outHits[i]); + } + return outHits; +} + + +IMPLEMENT_CLASS_HAS_INIT (Rigidbody) +IMPLEMENT_OBJECT_SERIALIZE (Rigidbody) +#endif // ENABLE_PHYSICS diff --git a/Runtime/Dynamics/ScriptBindings/NewDynamics.txt b/Runtime/Dynamics/ScriptBindings/NewDynamics.txt new file mode 100644 index 0000000..9a93bab --- /dev/null +++ b/Runtime/Dynamics/ScriptBindings/NewDynamics.txt @@ -0,0 +1,1797 @@ +C++RAW + + +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" + +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Dynamics/RigidBody.h" +#include "Runtime/Geometry/Ray.h" +#include "Runtime/Dynamics/PhysicsManager.h" +#include "Runtime/Geometry/Sphere.h" +#include "Runtime/Dynamics/Joints.h" +#include "Runtime/Dynamics/ConstantForce.h" +#include "Runtime/Terrain/Heightmap.h" + +#include "Runtime/Dynamics/CapsuleCollider.h" +#include "Runtime/Dynamics/BoxCollider.h" +#include "Runtime/Dynamics/SphereCollider.h" +#include "Runtime/Dynamics/RaycastCollider.h" +#include "Runtime/Dynamics/WheelCollider.h" +#include "Runtime/Dynamics/MeshCollider.h" +#include "Runtime/Dynamics/PhysicMaterial.h" +#include "Runtime/Dynamics/CharacterController.h" +#include "Runtime/Dynamics/CharacterJoint.h" +#include "Runtime/Dynamics/ConfigurableJoint.h" +#include "Runtime/Dynamics/SpringJoint.h" +#include "Runtime/Geometry/AABB.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Dynamics/Cloth.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/Dynamics/SkinnedCloth.h" +#include "Runtime/Dynamics/ClothRenderer.h" +#include "Runtime/Dynamics/RaycastHit.h" +#include "Runtime/Dynamics/TerrainCollider.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/ScriptingExportUtility.h" +#include "Runtime/Terrain/TerrainData.h" +#include "Runtime/Scripting/Scripting.h" + +using namespace Unity; + +CSRAW +#if ENABLE_PHYSICS +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Collections; + +#pragma warning disable 649 + +namespace UnityEngine +{ + +// Option for how to apply a force using Rigidbody.AddForce. +ENUM ForceMode + + // Add a continuous force to the rigidbody, using its mass. + Force = 0, + + // Add a continuous acceleration to the rigidbody, ignoring its mass. + Acceleration = 5, + + // Add an instant force impulse to the rigidbody, using its mass. + Impulse = 1, + + // Add an instant velocity change to the rigidbody, ignoring its mass. + VelocityChange = 2, +END + +// Global physics properties and helper methods. +CONDITIONAL ENABLE_PHYSICS +NONSEALED_CLASS Physics + + // The gravity applied to all rigid bodies in the scene. + THREAD_SAFE + CUSTOM_PROP static Vector3 gravity { return GetPhysicsManager ().GetGravity (); } { SCRIPTINGAPI_THREAD_CHECK(get_gravity) return GetPhysicsManager ().SetGravity (value); } + + + // The minimum contact penetration value in order to apply a penalty force (default 0.05). Must be positive. + CUSTOM_PROP static float minPenetrationForPenalty { return GetPhysicsManager ().GetMinPenetrationForPenalty (); } { return GetPhysicsManager ().SetMinPenetrationForPenalty (value); } + + // Two colliding objects with a relative velocity below this will not bounce (default 2). Must be positive. + CUSTOM_PROP static float bounceThreshold { return GetPhysicsManager ().GetBounceThreshold (); } { return GetPhysicsManager ().SetBounceThreshold (value); } + + OBSOLETE warning Please use bounceThreshold instead. + CSRAW static public float bounceTreshold { get { return bounceThreshold; } set { bounceThreshold = value; } } + + // The default linear velocity, below which objects start going to sleep (default 0.15). Must be positive. + CUSTOM_PROP static float sleepVelocity { return GetPhysicsManager ().GetSleepVelocity (); } { return GetPhysicsManager ().SetSleepVelocity (value); } + + // The default angular velocity, below which objects start sleeping (default 0.14). Must be positive. + CUSTOM_PROP static float sleepAngularVelocity { return GetPhysicsManager ().GetSleepAngularVelocity (); } { return GetPhysicsManager ().SetSleepAngularVelocity (value); } + + // The default maximimum angular velocity permitted for any rigid bodies (default 7). Must be positive. + CUSTOM_PROP static float maxAngularVelocity { return GetPhysicsManager ().GetMaxAngularVelocity (); } { return GetPhysicsManager ().SetMaxAngularVelocity (value); } + + // The default solver iteration count permitted for any rigid bodies (default 7). Must be positive. + CUSTOM_PROP static int solverIterationCount { return GetPhysicsManager ().GetSolverIterationCount (); } { return GetPhysicsManager ().SetSolverIterationCount (value); } + + CUSTOM private static bool Internal_Raycast (Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float distance, int layermask) + { + hitInfo->collider = NULL; + + float dirLength = Magnitude (direction); + if (dirLength > Vector3f::epsilon) + { + Vector3f normalizedDirection = direction / dirLength; + Ray ray (origin, normalizedDirection); + + bool didHit = GetPhysicsManager ().Raycast (ray, distance, *hitInfo, layermask); + + if (didHit) + { + hitInfo->collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (hitInfo->collider)); + return true; + } + else + { + return false; + } + } + else + return false; + } + + CUSTOM private static bool Internal_CapsuleCast (Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitInfo, float distance, int layermask) + { + hitInfo->collider = NULL; + + float dirLength = Magnitude (direction); + if (dirLength > Vector3f::epsilon) + { + Vector3f normalizedDirection = direction / dirLength; + + bool didHit = GetPhysicsManager ().CapsuleCast (point1, point2, radius, normalizedDirection, distance, *hitInfo, layermask); + + if (didHit) + { + hitInfo->collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (hitInfo->collider)); + return true; + } + else + { + return false; + } + } + else + return false; + } + + CUSTOM private static bool Internal_RaycastTest (Vector3 origin, Vector3 direction, float distance, int layermask) + { + float dirLength = Magnitude (direction); + if (dirLength > Vector3f::epsilon) + { + Vector3f normalizedDirection = direction / dirLength; + Ray ray (origin, normalizedDirection); + return GetPhysicsManager ().RaycastTest (ray, distance, layermask); + } + else + return false; + } + + // Casts a ray against all colliders in the scene. + CSRAW static public bool Raycast (Vector3 origin, Vector3 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return Internal_RaycastTest (origin, direction, distance, layerMask); + } + + // Casts a ray against all colliders in the scene and returns detailed information on what was hit. + CSRAW static public bool Raycast (Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return Internal_Raycast (origin, direction, out hitInfo, distance, layerMask); + } + + // Same as above using /ray.origin/ and /ray.direction/ instead of /origin/ and /direction/. + CSRAW static public bool Raycast (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return Raycast (ray.origin, ray.direction, distance, layerMask); + } + + // Same as above using /ray.origin/ and /ray.direction/ instead of /origin/ and /direction/. + CSRAW static public bool Raycast (Ray ray, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return Raycast (ray.origin, ray.direction, out hitInfo, distance, layerMask); + } + + /// *listonly* + CSRAW static public RaycastHit[] RaycastAll (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return RaycastAll (ray.origin, ray.direction, distance, layerMask); + } + + // Casts a ray through the scene and returns all hits. Note that order is not guaranteed. + CUSTOM static RaycastHit[] RaycastAll (Vector3 origin, Vector3 direction, float distance = Mathf.Infinity, int layermask = DefaultRaycastLayers) + { + float dirLength = Magnitude (direction); + if (dirLength > Vector3f::epsilon) + { + Vector3f normalizedDirection = direction / dirLength; + Ray ray (origin, normalizedDirection); + + const PhysicsManager::RaycastHits& hits = GetPhysicsManager ().RaycastAll (ray, distance, layermask); + + return ConvertNativeRaycastHitsToManaged(hits); + } + else + { + return CreateEmptyStructArray(GetMonoManager().GetCommonClasses().raycastHit); + } + } + + // Returns true if there is any collider intersecting the line between /start/ and /end/. + CSRAW static public bool Linecast (Vector3 start, Vector3 end, int layerMask = DefaultRaycastLayers) + { + Vector3 dir = end - start; + return Raycast (start, dir, dir.magnitude, layerMask); + } + + // Returns true if there is any collider intersecting the line between /start/ and /end/. + CSRAW static public bool Linecast (Vector3 start, Vector3 end, out RaycastHit hitInfo, int layerMask = DefaultRaycastLayers) + { + Vector3 dir = end - start; + return Raycast (start, dir, out hitInfo, dir.magnitude, layerMask); + } + + // Returns an array with all colliders touching or inside the sphere. + CUSTOM static Collider[] OverlapSphere (Vector3 position, float radius, int layerMask = AllLayers) + { + const vector<Collider*>& colliders = GetPhysicsManager ().OverlapSphere (position, radius, layerMask); + + return CreateScriptingArrayFromUnityObjects(colliders, ScriptingClassFor(Collider)); + } + /// *listonly* + CSRAW static public bool CapsuleCast (Vector3 point1, Vector3 point2, float radius, Vector3 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + RaycastHit hitInfo; + return Internal_CapsuleCast (point1, point2, radius, direction, out hitInfo, distance, layerMask); + } + // Casts a capsule against all colliders in the scene and returns detailed information on what was hit. + CSRAW static public bool CapsuleCast (Vector3 point1, Vector3 point2, float radius, Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return Internal_CapsuleCast (point1, point2, radius, direction, out hitInfo, distance, layerMask); + } + + // Casts a sphere against all colliders in the scene and returns detailed information on what was hit. + + CSRAW static public bool SphereCast (Vector3 origin, float radius, Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return Internal_CapsuleCast (origin, origin, radius, direction, out hitInfo, distance, layerMask); + } + + /// *listonly* + CSRAW static public bool SphereCast (Ray ray, float radius, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + RaycastHit hitInfo; + return Internal_CapsuleCast (ray.origin, ray.origin, radius, ray.direction, out hitInfo, distance, layerMask); + } + // Casts a sphere against all colliders in the scene and returns detailed information on what was hit. + + CSRAW static public bool SphereCast (Ray ray, float radius, out RaycastHit hitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return Internal_CapsuleCast (ray.origin, ray.origin, radius, ray.direction, out hitInfo, distance, layerMask); + } + + + // Like [[Physics.CapsuleCast]], but this function will return all hits the capsule sweep intersects. + + CUSTOM static RaycastHit[] CapsuleCastAll (Vector3 point1, Vector3 point2, float radius, Vector3 direction, float distance = Mathf.Infinity, int layermask = DefaultRaycastLayers) + { + float dirLength = Magnitude (direction); + if (dirLength > Vector3f::epsilon) + { + Vector3f normalizedDirection = direction / dirLength; + + const PhysicsManager::RaycastHits& hits = GetPhysicsManager ().CapsuleCastAll (point1, point2, radius, normalizedDirection, distance, layermask); + + return ConvertNativeRaycastHitsToManaged(hits); + } + else + { + return CreateEmptyStructArray(GetMonoManager().GetCommonClasses().raycastHit); + } + } + + // Like [[Physics.SphereCast]], but this function will return all hits the sphere sweep intersects. + + + CSRAW static public RaycastHit[] SphereCastAll (Vector3 origin, float radius, Vector3 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return CapsuleCastAll (origin, origin, radius, direction, distance, layerMask); + } + // @param ray The starting point and direction of the ray into which the sphere sweep is cast. + /// *listonly* + + CSRAW static public RaycastHit[] SphereCastAll (Ray ray, float radius, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers) + { + return CapsuleCastAll (ray.origin, ray.origin, radius, ray.direction, distance, layerMask); + } + + + // Returns true if there are any colliders overlapping the sphere defined by /position/ and /radius/ in world coordinates. + CUSTOM static bool CheckSphere (Vector3 position, float radius, int layerMask = DefaultRaycastLayers) + { + return GetPhysicsManager ().SphereTest (position, radius, layerMask); + } + + // Returns true if there are any colliders overlapping the capsule defined by the axis going from /start/ and /end/ and having /radius/ in world coordinates + CUSTOM static bool CheckCapsule (Vector3 start, Vector3 end, float radius, int layermask = DefaultRaycastLayers) + { + return GetPhysicsManager().CapsuleTest(start, end, radius, layermask); + } + + //*undocumented* DEPRECATED + CSRAW public const int kIgnoreRaycastLayer = 1 << 2; + //*undocumented* DEPRECATED + CSRAW public const int kDefaultRaycastLayers = ~kIgnoreRaycastLayer; + //*undocumented* DEPRECATED + CSRAW public const int kAllLayers = ~0; + + CSRAW public const int IgnoreRaycastLayer = 1 << 2; + CSRAW public const int DefaultRaycastLayers = ~IgnoreRaycastLayer; + CSRAW public const int AllLayers = ~0; + + // *undocumented* DEPRECATED + OBSOLETE warning penetrationPenaltyForce has no effect. + CUSTOM_PROP static float penetrationPenaltyForce { return 0; } { } + + + // Makes the collision detection system ignore all collisions between /collider1/ and /collider2/. + CUSTOM static void IgnoreCollision (Collider collider1, Collider collider2, bool ignore = true) + { + GetPhysicsManager().IgnoreCollision(*collider1, *collider2, ignore); + } + + // Makes the collision detection system ignore all collisions between any collider in /layer1/ and any collider in /layer2/. + CUSTOM static void IgnoreLayerCollision (int layer1, int layer2, bool ignore = true) + { + GetPhysicsManager().IgnoreCollision(layer1, layer2, ignore); + } + + // Are collisions between /layer1/ and /layer2/ being ignored? + CUSTOM static bool GetIgnoreLayerCollision (int layer1, int layer2) + { + return GetPhysicsManager().GetIgnoreCollision(layer1, layer2); + } + +END + +// Use these flags to constrain motion of Rigidbodies. +ENUM RigidbodyConstraints + // No constraints + None = 0, + + // Freeze motion along the X-axis. + FreezePositionX = 0x02, + + // Freeze motion along the Y-axis. + FreezePositionY = 0x04, + + // Freeze motion along the Z-axis. + FreezePositionZ = 0x08, + + // Freeze rotation along the X-axis. + FreezeRotationX = 0x10, + + // Freeze rotation along the Y-axis. + FreezeRotationY = 0x20, + + // Freeze rotation along the Z-axis. + FreezeRotationZ = 0x40, + + // Freeze motion along all axes. + FreezePosition = 0x0e, + + // Freeze rotation along all axes. + FreezeRotation = 0x70, + + // Freeze rotation and motion along all axes. + FreezeAll = 0x7e, + +END + +// Control of an object's position through physics simulation. +CONDITIONAL ENABLE_PHYSICS +CLASS Rigidbody : Component + + // The velocity vector of the rigidbody. + AUTO_PROP Vector3 velocity GetVelocity SetVelocity + + // The angular velocity vector of the rigidbody. + AUTO_PROP Vector3 angularVelocity GetAngularVelocity SetAngularVelocity + + // The drag of the object. + AUTO_PROP float drag GetDrag SetDrag + + // The angular drag of the object. + AUTO_PROP float angularDrag GetAngularDrag SetAngularDrag + + // The mass of the rigidbody. + AUTO_PROP float mass GetMass SetMass + + // Sets the mass based on the attached colliders assuming a constant density. + AUTO void SetDensity (float density); + + // Controls whether gravity affects this rigidbody. + AUTO_PROP bool useGravity GetUseGravity SetUseGravity + + + // Controls whether physics affects the rigidbody. + AUTO_PROP bool isKinematic GetIsKinematic SetIsKinematic + + // Controls whether physics will change the rotation of the object. + AUTO_PROP bool freezeRotation GetFreezeRotation SetFreezeRotation + + // Controls which degrees of freedom are alowed for the simulation of this Rigidbody. + AUTO_PROP RigidbodyConstraints constraints GetConstraints SetConstraints + + // The Rigidbody's collision detection mode. + AUTO_PROP CollisionDetectionMode collisionDetectionMode GetCollisionDetectionMode SetCollisionDetectionMode + + + // Adds a force to the rigidbody. As a result the rigidbody will start moving. + CUSTOM void AddForce (Vector3 force, ForceMode mode = ForceMode.Force) { self->AddForce (force, mode); } + + // Adds a force to the rigidbody. As a result the rigidbody will start moving. + CSRAW public void AddForce (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddForce (new Vector3 (x, y, z), mode); } + + // Adds a force to the rigidbody relative to its coordinate system. + CUSTOM void AddRelativeForce (Vector3 force, ForceMode mode = ForceMode.Force) { self->AddRelativeForce (force, mode); } + + // Adds a force to the rigidbody relative to its coordinate system. + CSRAW public void AddRelativeForce (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddRelativeForce (new Vector3 (x, y, z), mode); } + + // Adds a torque to the rigidbody. + CUSTOM void AddTorque (Vector3 torque, ForceMode mode = ForceMode.Force) { self->AddTorque (torque, mode); } + + + // Adds a torque to the rigidbody. + CSRAW public void AddTorque (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddTorque (new Vector3 (x, y, z), mode); } + + // Adds a torque to the rigidbody relative to the rigidbodie's own coordinate system. + CUSTOM void AddRelativeTorque (Vector3 torque, ForceMode mode = ForceMode.Force) { self->AddRelativeTorque (torque, mode); } + + // Adds a torque to the rigidbody relative to the rigidbodie's own coordinate system. + CSRAW public void AddRelativeTorque (float x, float y, float z, ForceMode mode = ForceMode.Force) { AddRelativeTorque (new Vector3 (x, y, z), mode); } + + // Applies /force/ at /position/. As a result this will apply a torque and force on the object. + CUSTOM void AddForceAtPosition (Vector3 force, Vector3 position, ForceMode mode = ForceMode.Force) { self->AddForceAtPosition (force, position, mode); } + + + // Applies a force to the rigidbody that simulates explosion effects. The explosion force will fall off linearly with distance to the rigidbody. + CUSTOM void AddExplosionForce(float explosionForce, Vector3 explosionPosition, float explosionRadius, float upwardsModifier = 0.0F, ForceMode mode = ForceMode.Force) { self->AddExplosionForce (explosionForce, explosionPosition, explosionRadius, upwardsModifier, mode); } + + // The closest point to the bounding box of the attached colliders. + CUSTOM Vector3 ClosestPointOnBounds (Vector3 position) + { + float dist; Vector3f outpos; + self->ClosestPointOnBounds(position, outpos, dist); + return outpos; + } + + + // The velocity relative to the rigidbody at the point /relativePoint/. + AUTO Vector3 GetRelativePointVelocity (Vector3 relativePoint); + + // The velocity of the rigidbody at the point /worldPoint/ in global space. + AUTO Vector3 GetPointVelocity (Vector3 worldPoint); + + + // The center of mass relative to the transform's origin. + AUTO_PROP Vector3 centerOfMass GetCenterOfMass SetCenterOfMass + + // The center of mass of the rigidbody in world space (RO). + AUTO_PROP Vector3 worldCenterOfMass GetWorldCenterOfMass + + // The rotation of the inertia tensor. + AUTO_PROP Quaternion inertiaTensorRotation GetInertiaTensorRotation SetInertiaTensorRotation + + // The diagonal inertia tensor of mass relative to the center of mass. + AUTO_PROP Vector3 inertiaTensor GetInertiaTensor SetInertiaTensor + + // Should collision detection be enabled? (By default always enabled) + AUTO_PROP bool detectCollisions GetDetectCollisions SetDetectCollisions + + // Force cone friction to be used for this rigidbody. + AUTO_PROP bool useConeFriction GetUseConeFriction SetUseConeFriction + + // The position of the rigidbody. + AUTO_PROP Vector3 position GetPosition SetPosition + + // The rotation of the rigdibody. + AUTO_PROP Quaternion rotation GetRotation SetRotation + + // Moves the rigidbody to /position/. + AUTO void MovePosition (Vector3 position); + + // Rotates the rigidbody to /rotation/. + AUTO void MoveRotation (Quaternion rot); + + // Interpolation allows you to smooth out the effect of running physics at a fixed frame rate. + AUTO_PROP RigidbodyInterpolation interpolation GetInterpolation SetInterpolation + + // Forces a rigidbody to sleep at least one frame. + AUTO void Sleep (); + + // Is the rigidbody sleeping? + AUTO bool IsSleeping (); + + // Forces a rigidbody to wake up. + AUTO void WakeUp (); + + // Allows you to override the solver iteration count per rigidbody. + AUTO_PROP int solverIterationCount GetSolverIterationCount SetSolverIterationCount + + // The linear velocity, below which objects start going to sleep. (Default 0.14) range { 0, infinity } + AUTO_PROP float sleepVelocity GetSleepVelocity SetSleepVelocity + + // The angular velocity, below which objects start going to sleep. (Default 0.14) range { 0, infinity } + AUTO_PROP float sleepAngularVelocity GetSleepAngularVelocity SetSleepAngularVelocity + + // The maximimum angular velocity of the rigidbody. (Default 7) range { 0, infinity } + AUTO_PROP float maxAngularVelocity GetMaxAngularVelocity SetMaxAngularVelocity + + + // OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider. + CSNONE void OnCollisionEnter (Collision collisionInfo); + + // OnCollisionEnter is called when this collider/rigidbody has stopped touching another rigidbody/collider. + CSNONE void OnCollisionExit (Collision collisionInfo); + + // OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider. + CSNONE void OnCollisionStay (Collision collisionInfo); + + // Tests if a rigidbody would collide with anything, if it was moved through the scene. + CUSTOM public bool SweepTest (Vector3 direction, out RaycastHit hitInfo, float distance = Mathf.Infinity) + { + hitInfo->collider = NULL; + + float dirLength = Magnitude (direction); + if (dirLength > Vector3f::epsilon) + { + Vector3f normalizedDirection = direction / dirLength; + + bool didHit = self->SweepTest (normalizedDirection, distance, *hitInfo); + + if (didHit) + { + hitInfo->collider = reinterpret_cast<Collider*>(ScriptingGetObjectReference (hitInfo->collider)); + return true; + } + else + { + return false; + } + } + else + return false; + } + // Like [[Rigidbody.SweepTest]], but returns all hits. + CUSTOM RaycastHit[] SweepTestAll (Vector3 direction, float distance = Mathf.Infinity) + { + float dirLength = Magnitude (direction); + if (dirLength > Vector3f::epsilon) + { + Vector3f normalizedDirection = direction / dirLength; + + const PhysicsManager::RaycastHits& hits = self->SweepTestAll (normalizedDirection, distance); + + return ConvertNativeRaycastHitsToManaged(hits); + } + else + { + return CreateScriptingArray<RaycastHit>(NULL, 0, GetMonoManager().GetCommonClasses().raycastHit); + } + } + + //*undocumented* DEPRECATED + OBSOLETE warning use Rigidbody.maxAngularVelocity instead. + CSRAW public void SetMaxAngularVelocity (float a) { maxAngularVelocity = a; } +END + +// [[Rigidbody]] interpolation mode. +ENUM RigidbodyInterpolation + // No Interpolation. + None = 0, + + // Interpolation will always lag a little bit behind but can be smoother than extrapolation. + Interpolate = 1, + + // Extrapolation will predict the position of the rigidbody based on the current velocity. + Extrapolate = 2 +END + +// The JointMotor is used to motorize a joint. +CONDITIONAL ENABLE_PHYSICS +STRUCT JointMotor + CSRAW private float m_TargetVelocity; + CSRAW private float m_Force; + CSRAW private bool m_FreeSpin; + + // The motor will apply a force up to /force/ to achieve /targetVelocity/. + CSRAW public float targetVelocity { get { return m_TargetVelocity; } set { m_TargetVelocity = value; } } + + // The motor will apply a force. + CSRAW public float force { get { return m_Force; } set { m_Force = value; } } + + // If /freeSpin/ is enabled the motor will only accelerate but never slow down. + CSRAW public bool freeSpin { get { return m_FreeSpin; } set { m_FreeSpin = value; } } +END + +// JointSpring is used add a spring force to [[HingeJoint]] and [[PhysicMaterial]]. +CONDITIONAL ENABLE_PHYSICS +STRUCT JointSpring + + // The spring forces used to reach the target position + CSRAW public float spring; + + // The damper force uses to dampen the spring + CSRAW public float damper; + + // The target position the joint attempts to reach. + CSRAW public float targetPosition; + +// We have to keep those as public variables because of a bug in the C# raycast sample. +END + +// JointLimits is used by the [[HingeJoint]] to limit the joints angle. +CONDITIONAL ENABLE_PHYSICS +STRUCT JointLimits + CSRAW private float m_Min; + CSRAW private float m_MinBounce; + CSRAW private float m_MinHardness; + CSRAW private float m_Max; + CSRAW private float m_MaxBounce; + CSRAW private float m_MaxHardness; + + // The lower limit of the joint. When the joint angle or position is below it, + CSRAW public float min { get { return m_Min; } set { m_Min = value; } } + + // The bounciness of the joint when hitting the lower limit of the joint. + CSRAW public float minBounce { get { return m_MinBounce; } set { m_MinBounce = value; } } + + // The upper limit of the joint. When the joint angle or position is above it, + CSRAW public float max { get { return m_Max; } set { m_Max = value; } } + + // The bounciness of the joint when hitting the upper limit of the joint. + CSRAW public float maxBounce { get { return m_MaxBounce; } set { m_MaxBounce = value; } } +END + + +C++RAW + +struct MonoJointMotor +{ + float targetVelocity; + float force; + short freeSpin;// bool's need to be shorts in mono but in novodex bools are ints + + MonoJointMotor (const JointMotor& motor) + { + targetVelocity = motor.targetVelocity; + force = motor.force; + freeSpin = motor.freeSpin; + } + + operator JointMotor () const + { + JointMotor motor; + motor.targetVelocity = targetVelocity; + motor.force = force; + motor.freeSpin = freeSpin; + return motor; + } +}; + + + +// Joint is the base class for all joints. +CONDITIONAL ENABLE_PHYSICS +NONSEALED_CLASS Joint : Component + + // A reference to another rigidbody this joint connects to. + AUTO_PTR_PROP Rigidbody connectedBody GetConnectedBody SetConnectedBody + + // The Direction of the axis around which the body is constrained. + AUTO_PROP Vector3 axis GetAxis SetAxis + + // The Position of the anchor around which the joints motion is constrained. + AUTO_PROP Vector3 anchor GetAnchor SetAnchor + + // The Position of the connected anchor around which the joints motion is constrained. + AUTO_PROP Vector3 connectedAnchor GetConnectedAnchor SetConnectedAnchor + + // Should the connected anchor position be used? + AUTO_PROP bool autoConfigureConnectedAnchor GetAutoConfigureConnectedAnchor SetAutoConfigureConnectedAnchor + + // The force that needs to be applied for this joint to break. + AUTO_PROP float breakForce GetBreakForce SetBreakForce + + // The torque that needs to be applied for this joint to break. + AUTO_PROP float breakTorque GetBreakTorque SetBreakTorque + + // Called when a joint attached to the same game object broke. + CSNONE void OnJointBreak (float breakForce); +END + +// The HingeJoint groups together 2 rigid bodies, constraining them to move like connected by a hinge. +CONDITIONAL ENABLE_PHYSICS +CLASS HingeJoint : Joint + + // Setting the motor, limit, spring automatically enabled them. + + // The motor will apply a force up to a maximum force to achieve the target velocity in degrees per second. + AUTO_PROP JointMotor motor GetMotor SetMotor + + // The limits of the hinge joint. + AUTO_PROP JointLimits limits GetLimits SetLimits + + // The spring attempts to reach a target angle by adding spring and damping forces. + AUTO_PROP JointSpring spring GetSpring SetSpring + + // Enables the joint's motor. + AUTO_PROP bool useMotor GetUseMotor SetUseMotor + + // Enables the joint's limits. + AUTO_PROP bool useLimits GetUseLimits SetUseLimits + + // Enables the joint's spring. + AUTO_PROP bool useSpring GetUseSpring SetUseSpring + + // The angular velocity of the joint in degrees per second. + AUTO_PROP float velocity GetVelocity + + // The current angle in degrees of the joint relative to its rest position. (RO) + AUTO_PROP float angle GetAngle +END + + + +// The spring joint ties together 2 rigid bodies, spring forces will be automatically applied to keep the object at the given distance. +CONDITIONAL ENABLE_PHYSICS +CLASS SpringJoint : Joint + + // The spring force used to keep the two objects together + AUTO_PROP float spring GetSpring SetSpring + // The damper force used to dampen the spring force + AUTO_PROP float damper GetDamper SetDamper + + // The minimum distance between the bodies relative to their initial distance + AUTO_PROP float minDistance GetMinDistance SetMinDistance + + // The maximum distance between the bodies relative to their initial distance + AUTO_PROP float maxDistance GetMaxDistance SetMaxDistance +END + +// The Fixed joint groups together 2 rigidbodies, making them stick together in their bound position. +CONDITIONAL ENABLE_PHYSICS +CLASS FixedJoint : Joint + +END + +// The limits defined by the [[CharacterJoint]] +CONDITIONAL ENABLE_PHYSICS +STRUCT SoftJointLimit + CSRAW float m_Limit; + CSRAW float m_Bounciness; + CSRAW float m_Spring; + CSRAW float m_Damper; + + // The limit position/angle of the joint. + CSRAW public float limit { get { return m_Limit; } set { m_Limit = value; } } + + // If greater than zero, the limit is soft. The spring will pull the joint back. + CSRAW public float spring { get { return m_Spring; } set { m_Spring = value; } } + + // If spring is greater than zero, the limit is soft. + CSRAW public float damper { get { return m_Damper; } set { m_Damper = value; } } + + // When the joint hits the limit, it can be made to bounce off it. + CSRAW public float bounciness { get { return m_Bounciness; } set { m_Bounciness = value; } } + + OBSOLETE error Use SoftJointLimit.bounciness instead + CSRAW public float bouncyness { get { return m_Bounciness; } set { m_Bounciness = value; } } +END + + +// The [[ConfigurableJoint]] attempts to attain position / velocity targets based on this flag +CSRAW [Flags] +ENUM JointDriveMode + // Don't apply any forces to reach the target + None = 0, + // Try to reach the specified target position + Position = 1, + // Try to reach the specified target velocity + Velocity = 2, + // Try to reach the specified target position and velocity + PositionAndVelocity = 3 +END + +// Determines how to snap physics joints back to its constrained position when it drifts off too much +ENUM JointProjectionMode + // Don't snap at all + None = 0, + // Snap both position and rotation + PositionAndRotation = 1, + // Snap Position only + PositionOnly = 2 +END + +// How the joint's movement will behave along its local X axis +CONDITIONAL ENABLE_PHYSICS +STRUCT JointDrive + CSRAW int m_Mode; + CSRAW float m_PositionSpring; + CSRAW float m_PositionDamper; + CSRAW float m_MaximumForce; + + // Whether the drive should attempt to reach position, velocity, both or nothing + CSRAW public JointDriveMode mode { get { return (JointDriveMode)m_Mode; } set { m_Mode = (int)value; } } + + // Strength of a rubber-band pull toward the defined direction. Only used if /mode/ includes Position. + CSRAW public float positionSpring { get { return m_PositionSpring; } set { m_PositionSpring = value; } } + + // Resistance strength against the Position Spring. Only used if /mode/ includes Position. + CSRAW public float positionDamper { get { return m_PositionDamper; } set { m_PositionDamper = value; } } + + // Amount of force applied to push the object toward the defined direction. + CSRAW public float maximumForce { get { return m_MaximumForce; } set { m_MaximumForce = value; } } +END + +// Character Joints are mainly used for Ragdoll effects. They are an extended ball-socket joint which allows you to limit the joint on each axis. +CONDITIONAL ENABLE_PHYSICS +CLASS CharacterJoint : Joint + + // The secondary axis around which the joint can rotate + AUTO_PROP Vector3 swingAxis GetSwingAxis SetSwingAxis + + // The lower limit around the primary axis of the character joint. + AUTO_PROP SoftJointLimit lowTwistLimit GetLowTwistLimit SetLowTwistLimit + + // The upper limit around the primary axis of the character joint. + AUTO_PROP SoftJointLimit highTwistLimit GetHighTwistLimit SetHighTwistLimit + + // The limit around the primary axis of the character joint. + AUTO_PROP SoftJointLimit swing1Limit GetSwing1Limit SetSwing1Limit + + // The limit around the primary axis of the character joint. + AUTO_PROP SoftJointLimit swing2Limit GetSwing2Limit SetSwing2Limit + + //*undocumented* + AUTO_PROP Quaternion targetRotation GetTargetRotation SetTargetRotation + //*undocumented* + AUTO_PROP Vector3 targetAngularVelocity GetTargetAngularVelocity SetTargetAngularVelocity + //*undocumented* + AUTO_PROP JointDrive rotationDrive GetRotationDrive SetRotationDrive + +END + +// Constrains movement for a [[ConfigurableJoint]] along the 6 axes. +ENUM ConfigurableJointMotion + // Motion along the axis will be locked + Locked = 0, + // Motion along the axis will be limited by the respective limit + Limited = 1, + // Motion along the axis will be completely free and completely unconstrained + Free = 2 +END + +// Control [[ConfigurableJoint]]'s rotation with either X & YZ or Slerp Drive +ENUM RotationDriveMode + // Use XY & Z Drive + XYAndZ = 0, + // Use Slerp drive + Slerp = 1 +END + +// The configurable joint is an extremely flexible joint giving you complete control over rotation and linear motion. +CONDITIONAL ENABLE_PHYSICS +CLASS ConfigurableJoint : Joint + + // The joint's secondary axis. + AUTO_PROP Vector3 secondaryAxis GetSecondaryAxis SetSecondaryAxis + + // Allow movement along the X axis to be Free, completely Locked, or Limited according to Linear Limit + AUTO_PROP ConfigurableJointMotion xMotion GetXMotion SetXMotion + // Allow movement along the Y axis to be Free, completely Locked, or Limited according to Linear Limit + AUTO_PROP ConfigurableJointMotion yMotion GetYMotion SetYMotion + // Allow movement along the Z axis to be Free, completely Locked, or Limited according to Linear Limit + AUTO_PROP ConfigurableJointMotion zMotion GetZMotion SetZMotion + + // Allow rotation around the X axis to be Free, completely Locked, or Limited according to Low and High Angular XLimit + AUTO_PROP ConfigurableJointMotion angularXMotion GetAngularXMotion SetAngularXMotion + // Allow rotation around the Y axis to be Free, completely Locked, or Limited according to Angular YLimit + AUTO_PROP ConfigurableJointMotion angularYMotion GetAngularYMotion SetAngularYMotion + // Allow rotation around the Z axis to be Free, completely Locked, or Limited according to Angular ZLimit + AUTO_PROP ConfigurableJointMotion angularZMotion GetAngularZMotion SetAngularZMotion + + // Boundary defining movement restriction, based on distance from the joint's origin + AUTO_PROP SoftJointLimit linearLimit GetLinearLimit SetLinearLimit + + // Boundary defining lower rotation restriction, based on delta from original rotation + AUTO_PROP SoftJointLimit lowAngularXLimit GetLowAngularXLimit SetLowAngularXLimit + + // Boundary defining upper rotation restriction, based on delta from original rotation. + AUTO_PROP SoftJointLimit highAngularXLimit GetHighAngularXLimit SetHighAngularXLimit + + // Boundary defining rotation restriction, based on delta from original rotation + AUTO_PROP SoftJointLimit angularYLimit GetAngularYLimit SetAngularYLimit + + // Boundary defining rotation restriction, based on delta from original rotation + AUTO_PROP SoftJointLimit angularZLimit GetAngularZLimit SetAngularZLimit + + // The desired position that the joint should move into + AUTO_PROP Vector3 targetPosition GetTargetPosition SetTargetPosition + // The desired velocity that the joint should move along + AUTO_PROP Vector3 targetVelocity GetTargetVelocity SetTargetVelocity + + // Definition of how the joint's movement will behave along its local X axis + AUTO_PROP JointDrive xDrive GetXDrive SetXDrive + + // Definition of how the joint's movement will behave along its local Y axis + AUTO_PROP JointDrive yDrive GetYDrive SetYDrive + + // Definition of how the joint's movement will behave along its local Z axis + AUTO_PROP JointDrive zDrive GetZDrive SetZDrive + + // This is a [[Quaternion]]. It defines the desired rotation that the joint should rotate into. + AUTO_PROP Quaternion targetRotation GetTargetRotation SetTargetRotation + // This is a [[Vector3]]. It defines the desired angular velocity that the joint should rotate into. + AUTO_PROP Vector3 targetAngularVelocity GetTargetAngularVelocity SetTargetAngularVelocity + // Control the object's rotation with either X & YZ or Slerp Drive by itself + AUTO_PROP RotationDriveMode rotationDriveMode GetRotationDriveMode SetRotationDriveMode + + // Definition of how the joint's rotation will behave around its local X axis. Only used if Rotation Drive Mode is Swing & Twist + AUTO_PROP JointDrive angularXDrive GetAngularXDrive SetAngularXDrive + + // Definition of how the joint's rotation will behave around its local Y and Z axes. Only used if Rotation Drive Mode is Swing & Twist + AUTO_PROP JointDrive angularYZDrive GetAngularYZDrive SetAngularYZDrive + + // Definition of how the joint's rotation will behave around all local axes. Only used if Rotation Drive Mode is Slerp Only + AUTO_PROP JointDrive slerpDrive GetSlerpDrive SetSlerpDrive + + + // Properties to track to snap the object back to its constrained position when it drifts off too much + AUTO_PROP JointProjectionMode projectionMode GetProjectionMode SetProjectionMode + + // Distance from the Connected Body that must be exceeded before the object snaps back to an acceptable position + AUTO_PROP float projectionDistance GetProjectionDistance SetProjectionDistance + + // Difference in angle from the Connected Body that must be exceeded before the object snaps back to an acceptable position + AUTO_PROP float projectionAngle GetProjectionAngle SetProjectionAngle + + + // If enabled, all Target values will be calculated in world space instead of the object's local space + AUTO_PROP bool configuredInWorldSpace GetConfiguredInWorldSpace SetConfiguredInWorldSpace + + // If enabled, the two connected rigidbodies will be swapped, as if the joint was attached to the other body. + AUTO_PROP bool swapBodies GetSwapBodies SetSwapBodies + +END + + +// A force applied constantly. +CONDITIONAL ENABLE_PHYSICS +CLASS ConstantForce : Behaviour + // The force applied to the rigidbody every frame. + CUSTOM_PROP Vector3 force { return self->m_Force; } { self->m_Force = value; } + + // The force - relative to the rigid bodies coordinate system - applied every frame. + CUSTOM_PROP Vector3 relativeForce { return self->m_RelativeForce; } { self->m_RelativeForce = value; } + + // The torque applied to the rigidbody every frame. + CUSTOM_PROP Vector3 torque { return self->m_Torque; } { self->m_Torque = value; } + + // The torque - relative to the rigid bodies coordinate system - applied every frame. + CUSTOM_PROP Vector3 relativeTorque { return self->m_RelativeTorque; } { self->m_RelativeTorque = value; } +END + +// The collision detection mode constants used for [[Rigidbody.collisionDetectionMode]]. +ENUM CollisionDetectionMode + // Continuous collision detection is off for this Rigidbody. + Discrete = 0, + // Continuous collision detection is on for colliding with static mesh geometry. + Continuous = 1, + // Continuous collision detection is on for colliding with static and dynamic geometry. + ContinuousDynamic = 2 +END + +// A base class of all colliders. +CONDITIONAL ENABLE_PHYSICS +NONSEALED_CLASS Collider : Component + // Enabled Colliders will collide with other colliders, disabled Colliders won't. + AUTO_PROP bool enabled GetEnabled SetEnabled + + // The rigidbody the collider is attached to. + AUTO_PTR_PROP Rigidbody attachedRigidbody GetRigidbody + + // Is the collider a trigger? + AUTO_PROP bool isTrigger GetIsTrigger SetIsTrigger + + // The material used by the collider. + CUSTOM_PROP PhysicMaterial material + { + PhysicMaterial* material = self->GetMaterial (); + PhysicMaterial* instance = &PhysicMaterial::GetInstantiatedMaterial (material, *self); + if (instance != material) + self->SetMaterial (instance); + return Scripting::ScriptingWrapperFor (instance); + } + { + self->SetMaterial (value); + } + + // The closest point to the bounding box of the attached collider. + CUSTOM Vector3 ClosestPointOnBounds (Vector3 position) + { + float dist; Vector3f outpos; + self->ClosestPointOnBounds(position, outpos, dist); + return outpos; + } + + // The shared physic material of this collider. + CUSTOM_PROP PhysicMaterial sharedMaterial { return Scripting::ScriptingWrapperFor (self->GetMaterial ()); } { self->SetMaterial (value); } + + // The world space bounding volume of the collider. + AUTO_PROP Bounds bounds GetBounds + + CUSTOM private static bool Internal_Raycast (Collider col, Ray ray, out RaycastHit hitInfo, float distance) + { + hitInfo->collider = NULL; + + bool didHit = col->Raycast (ray, distance, *hitInfo); + if (didHit) + { + #if UNITY_WINRT + hitInfo->collider = reinterpret_cast<Collider*> (ScriptingGetObjectReference((Collider*)col)); + #else + hitInfo->collider = reinterpret_cast<Collider*> (col.GetScriptingObject()); + #endif + return true; + } + else + { + return false; + } + } + + // Casts a [[Ray]] that ignores all Colliders except this one. + + CSRAW public bool Raycast (Ray ray, out RaycastHit hitInfo, float distance) + { + return Internal_Raycast (this, ray, out hitInfo, distance); + } + + // OnTriggerEnter is called when the [[Collider]] /other/ enters the [[class-BoxCollider|trigger]]. + CSNONE void OnTriggerEnter (Collider other); + + // OnTriggerExit is called when the [[Collider]] /other/ has stopped touching the [[class-BoxCollider|trigger]]. + CSNONE void OnTriggerExit (Collider other); + + // OnTriggerStay is called ''almost'' all the frames for every [[Collider]] __other__ that is touching the [[class-BoxCollider|trigger]]. + CSNONE void OnTriggerStay (Collider other); + + // OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider. + + CSNONE void OnCollisionEnter (Collision collisionInfo); + + // OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider. + CSNONE void OnCollisionExit (Collision collisionInfo); + + // OnCollisionStay is called once per frame for every collider/rigidbody that is touching rigidbody/collider. + CSNONE void OnCollisionStay (Collision collisionInfo); +END + +// A box-shaped primitive collider. +CONDITIONAL ENABLE_PHYSICS +CLASS BoxCollider : Collider + // The center of the box, measured in the object's local space. + AUTO_PROP Vector3 center GetCenter SetCenter + + // The size of the box, measured in the object's local space. + AUTO_PROP Vector3 size GetSize SetSize + + //*undocumented* DEPRECATED + OBSOLETE warning use BoxCollider.size instead. + CSRAW public Vector3 extents { get { return size * 0.5F; } set { size = value * 2.0F; } } +END + + +// A sphere-shaped primitive collider. +CONDITIONAL ENABLE_PHYSICS +CLASS SphereCollider : Collider + // The center of the sphere, measured in the object's local space. + AUTO_PROP Vector3 center GetCenter SetCenter + + // The radius of the sphere, measured in the object's local space. + AUTO_PROP float radius GetRadius SetRadius +END + +// A mesh collider allows you to do [[wiki:class-MeshCollider|collision detection]] between meshes and primitives. +CONDITIONAL ENABLE_PHYSICS +CLASS MeshCollider : Collider + //*undocumented* deprecated with version 1.5 (should implement modifiable mesh interface) + OBSOLETE warning mesh has been replaced with sharedMesh and will be deprecated + CSRAW public Mesh mesh { get { return sharedMesh; } set { sharedMesh = value; } } + + // The mesh object used for collision detection + AUTO_PTR_PROP Mesh sharedMesh GetSharedMesh SetSharedMesh + + // Use a convex collider from the mesh. + AUTO_PROP bool convex GetConvex SetConvex + + // Uses interpolated normals for sphere collisions instead of flat polygonal normals. + AUTO_PROP bool smoothSphereCollisions GetSmoothSphereCollisions SetSmoothSphereCollisions + +END + +// A capsule-shaped primitive collider. +CONDITIONAL ENABLE_PHYSICS +CLASS CapsuleCollider : Collider + // The center of the capsule, measured in the object's local space. + AUTO_PROP Vector3 center GetCenter SetCenter + + // The radius of the sphere, measured in the object's local space. + AUTO_PROP float radius GetRadius SetRadius + + // The height of the capsule meased in the object's local space. + AUTO_PROP float height GetHeight SetHeight + + // The direction of the capsule. + AUTO_PROP int direction GetDirection SetDirection + +END + +OBSOLETE warning Use WheelCollider or BoxCollider instead, RaycastCollider is unreliable +CONDITIONAL ENABLE_PHYSICS +CLASS RaycastCollider : Collider + OBSOLETE warning Use WheelCollider or BoxCollider instead, RaycastCollider is unreliable + AUTO_PROP Vector3 center GetCenter SetCenter + + OBSOLETE warning Use WheelCollider or BoxCollider instead, RaycastCollider is unreliable + AUTO_PROP float length GetLength SetLength +END + + +// WheelFrictionCurve is used by the [[WheelCollider]] to describe friction properties of the wheel tire. +CONDITIONAL ENABLE_PHYSICS +STRUCT WheelFrictionCurve + CSRAW private float m_ExtremumSlip; + CSRAW private float m_ExtremumValue; + CSRAW private float m_AsymptoteSlip; + CSRAW private float m_AsymptoteValue; + CSRAW private float m_Stiffness; + + // Extremum point slip (default 1). + CSRAW public float extremumSlip { get { return m_ExtremumSlip; } set { m_ExtremumSlip = value; } } + // Force at the extremum slip (default 20000). + CSRAW public float extremumValue { get { return m_ExtremumValue; } set { m_ExtremumValue = value; } } + // Asymptote point slip (default 2). + CSRAW public float asymptoteSlip { get { return m_AsymptoteSlip; } set { m_AsymptoteSlip = value; } } + // Force at the asymptote slip (default 10000). + CSRAW public float asymptoteValue { get { return m_AsymptoteValue; } set { m_AsymptoteValue = value; } } + // Multiplier for the ::ref::extremumValue and ::ref::asymptoteValue values (default 1). + CSRAW public float stiffness { get { return m_Stiffness; } set { m_Stiffness = value; } } +END + + +// Contact information for the wheel, reported by [[WheelCollider]]. +CONDITIONAL ENABLE_PHYSICS +STRUCT WheelHit + CSRAW private Vector3 m_Point; + CSRAW private Vector3 m_Normal; + CSRAW private Vector3 m_ForwardDir; + CSRAW private Vector3 m_SidewaysDir; + CSRAW private float m_Force; + CSRAW private float m_ForwardSlip; + CSRAW private float m_SidewaysSlip; + #if UNITY_WINRT + CSRAW private int m_ColliderHandle; + #else + CSRAW private Collider m_Collider; + #endif + + // The other [[Collider]] the wheel is hitting. + CONDITIONAL !UNITY_WINRT + CSRAW public Collider collider { get { return m_Collider; } set { m_Collider = value; } } + + CONDITIONAL UNITY_WINRT + CSRAW public Collider collider + { + get + { + return UnityEngineInternal.ScriptingUtils.GCHandleToObject<Collider>(m_ColliderHandle); + } + set + { + m_ColliderHandle = GetColliderHandle(value); + } + } + + CONDITIONAL UNITY_WINRT + CUSTOM private static int GetColliderHandle(object collider) + { + ScriptingObjectOfType<Collider> col(collider); + return ScriptingGetObjectReference(col.GetPtr()); + } + + // The point of contact between the wheel and the ground. + CSRAW public Vector3 point { get { return m_Point; } set { m_Point = value; } } + // The normal at the point of contact. + CSRAW public Vector3 normal { get { return m_Normal; } set { m_Normal = value; } } + // The direction the wheel is pointing in. + CSRAW public Vector3 forwardDir { get { return m_ForwardDir; } set { m_ForwardDir = value; } } + // The sideways direction of the wheel. + CSRAW public Vector3 sidewaysDir { get { return m_SidewaysDir; } set { m_SidewaysDir = value; } } + + // The magnitude of the force being applied for the contact. + CSRAW public float force { get { return m_Force; } set { m_Force = value; } } + + // Tire slip in the rolling direction. Acceleration slip is negative, braking slip is positive. + CSRAW public float forwardSlip { get { return m_ForwardSlip; } set { m_Force = m_ForwardSlip; } } + + // Tire slip in the sideways direction. + CSRAW public float sidewaysSlip { get { return m_SidewaysSlip; } set { m_SidewaysSlip = value; } } +END + + + +// A special collider for vehicle wheels. +CONDITIONAL ENABLE_PHYSICS +CLASS WheelCollider : Collider + // The center of the wheel, measured in the object's local space. + AUTO_PROP Vector3 center GetCenter SetCenter + + // The radius of the wheel, measured in local space. + AUTO_PROP float radius GetRadius SetRadius + + // Maximum extension distance of wheel suspension, measured in local space. + AUTO_PROP float suspensionDistance GetSuspensionDistance SetSuspensionDistance + + // The parameters of wheel's suspension. The suspension attempts to reach a target position + AUTO_PROP JointSpring suspensionSpring GetSuspensionSpring SetSuspensionSpring + + // The mass of the wheel. Must be larger than zero. + AUTO_PROP float mass GetMass SetMass + + // Properties of tire friction in the direction the wheel is pointing in. + AUTO_PROP WheelFrictionCurve forwardFriction GetForwardFriction SetForwardFriction + + // Properties of tire friction in the sideways direction. + AUTO_PROP WheelFrictionCurve sidewaysFriction GetSidewaysFriction SetSidewaysFriction + + // Motor torque on the wheel axle. Positive or negative depending on direction. + AUTO_PROP float motorTorque GetMotorTorque SetMotorTorque + + // Brake torque. Must be positive. + AUTO_PROP float brakeTorque GetBrakeTorque SetBrakeTorque + + // Steering angle in degrees, always around the local y-axis. + AUTO_PROP float steerAngle GetSteerAngle SetSteerAngle + + + // Indicates whether the wheel currently collides with something (RO). + AUTO_PROP bool isGrounded IsGrounded + + C++RAW + + struct MonoWheelHit + { + Vector3f point; + Vector3f normal; + Vector3f forwardDir; + Vector3f sidewaysDir; + float force; + float forwardSlip; + float sidewaysSlip; +#if UNITY_WINRT + int colliderHandle; +#else + ScriptingObjectPtr collider; +#endif + }; + + + // Gets ground collision data for the wheel. + CUSTOM public bool GetGroundHit (out WheelHit hit) + { + WheelHit col; + bool didHit = self->GetGroundHit( col ); + if( didHit ) + { + hit->point = col.point; + hit->normal = col.normal; + hit->forwardDir = col.forwardDir; + hit->sidewaysDir = col.sidewaysDir; + hit->force = col.force; + hit->forwardSlip = col.forwardSlip; + hit->sidewaysSlip = col.sidewaysSlip; +#if UNITY_WINRT + hit->colliderHandle = ScriptingGetObjectReference (col.collider); +#else + hit->collider = Scripting::ScriptingWrapperFor( col.collider ); +#endif + return true; + } + return false; + } + + // Current wheel axle rotation speed, in rotations per minute (RO). + AUTO_PROP float rpm GetRpm +END + + +// Structure used to get information back from a raycast. +CONDITIONAL ENABLE_PHYSICS +STRUCT RaycastHit + CSRAW private Vector3 m_Point; + CSRAW private Vector3 m_Normal; + CSRAW private int m_FaceID; + CSRAW private float m_Distance; + CSRAW private Vector2 m_UV; + #if UNITY_WINRT + CSRAW private int m_ColliderHandle; + #else + CSRAW private Collider m_Collider; + #endif + + // The impact point in world space where the ray hit the collider. + CSRAW public Vector3 point { get { return m_Point; } set { m_Point = value; } } + + // The normal of the surface the ray hit. + CSRAW public Vector3 normal { get { return m_Normal; } set { m_Normal = value; } } + + // The barycentric coordinate of the triangle we hit. + CSRAW public Vector3 barycentricCoordinate { get { return new Vector3 (1.0F - (m_UV.y + m_UV.x), m_UV.x, m_UV.y); } set { m_UV = value; } } + + // The distance from the ray's origin to the impact point. + CSRAW public float distance { get { return m_Distance; } set { m_Distance = value; } } + + // The index of the triangle that was hit. + CSRAW public int triangleIndex { get { return m_FaceID; } } + + // Workaround for gcc/msvc gcc where passing small mono structures by value does not work + CUSTOM private static void CalculateRaycastTexCoord (out Vector2 output, Collider col, Vector2 uv, Vector3 point, int face, int index) + { + *output = CalculateRaycastTexcoord(col, uv, point, face, index); + } + + // The uv texture coordinate at the impact point. + CSRAW public Vector2 textureCoord { get { Vector2 coord; CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 0); return coord; } } + + // The secondary uv texture coordinate at the impact point. + CSRAW public Vector2 textureCoord2 { get { Vector2 coord; CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 1); return coord; } } + + OBSOLETE warning Use textureCoord2 instead + CSRAW public Vector2 textureCoord1 { get { Vector2 coord; CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 1); return coord; } } + + // The uv lightmap coordinate at the impact point. + CSRAW public Vector2 lightmapCoord { get { + Vector2 coord; + CalculateRaycastTexCoord(out coord, collider, m_UV, m_Point, m_FaceID, 1); + if( collider.renderer != null ) + { + Vector4 st = collider.renderer.lightmapTilingOffset; + coord.x = coord.x * st.x + st.z; + coord.y = coord.y * st.y + st.w; + } + return coord; + } } + + // The [[Collider]] that was hit. + CONDITIONAL !UNITY_WINRT + CSRAW public Collider collider { get { return m_Collider; } } + + CONDITIONAL UNITY_WINRT + CSRAW public Collider collider { get { return UnityEngineInternal.ScriptingUtils.GCHandleToObject<Collider>(m_ColliderHandle); } } + + // The [[Rigidbody]] of the collider that was hit. If the collider is not attached to a rigidbody then it is /null/. + CSRAW public Rigidbody rigidbody { get { return collider != null ? collider.attachedRigidbody : null; } } + + // The [[Transform]] of the rigidbody or collider that was hit. + CSRAW public Transform transform { get { + Rigidbody body = rigidbody; + if (body != null) + return body.transform; + else if (collider != null) + return collider.transform; + else + return null; + } } +END + +// Describes how physic materials of colliding objects are combined. +ENUM PhysicMaterialCombine + // Averages the friction/bounce of the two colliding materials. + Average = 0, + // Uses the smaller friction/bounce of the two colliding materials. + Minimum = 2, + // Multiplies the friction/bounce of the two colliding materials. + Multiply = 1, + // Uses the larger friction/bounce of the two colliding materials. + Maximum = 3 +END + +// Physics material describes how to handle colliding objects (friction, bounciness). +CONDITIONAL ENABLE_PHYSICS +CLASS PhysicMaterial : Object + + CUSTOM private static void Internal_CreateDynamicsMaterial ([Writable]PhysicMaterial mat, string name) + { + PhysicMaterial* material = NEW_OBJECT (PhysicMaterial); + SmartResetObject(*material); + material->SetNameCpp (name); + Scripting::ConnectScriptingWrapperToObject (mat.GetScriptingObject(), material); + } + + // Creates a new material. + CSRAW public PhysicMaterial () { Internal_CreateDynamicsMaterial (this,null); } + + // Creates a new material named /name/. + CSRAW public PhysicMaterial (string name){ Internal_CreateDynamicsMaterial (this,name); } + + // The friction used when already moving. This value has to be between 0 and 1. + AUTO_PROP float dynamicFriction GetDynamicFriction SetDynamicFriction + + // The friction used when an object is lying on a surface. Usually a value from 0 to 1. + AUTO_PROP float staticFriction GetStaticFriction SetStaticFriction + + // How bouncy is the surface? A value of 0 will not bounce. A value of 1 will bounce without any loss of energy. + AUTO_PROP float bounciness GetBounciness SetBounciness + + OBSOLETE error Use PhysicMaterial.bounciness instead + CSRAW public float bouncyness { get { return bounciness; } set { bounciness = value;} } + + // The direction of anisotropy. Anisotropic friction is enabled if the vector is not zero. + AUTO_PROP Vector3 frictionDirection2 GetFrictionDirection2 SetFrictionDirection2 + + // If anisotropic friction is enabled, dynamicFriction2 will be applied along frictionDirection2. + AUTO_PROP float dynamicFriction2 GetDynamicFriction2 SetDynamicFriction2 + + // If anisotropic friction is enabled, staticFriction2 will be applied along frictionDirection2. + AUTO_PROP float staticFriction2 GetStaticFriction2 SetStaticFriction2 + + // Determines how the friction is combined. + AUTO_PROP PhysicMaterialCombine frictionCombine GetFrictionCombine SetFrictionCombine + + // Determines how the bounciness is combined. + AUTO_PROP PhysicMaterialCombine bounceCombine GetBounceCombine SetBounceCombine + + // *undocumented* DEPRECATED + OBSOLETE warning use PhysicMaterial.frictionDirection2 instead. + CSRAW public Vector3 frictionDirection { get { return frictionDirection2; } set { frictionDirection2 = value; }} +END + +// Describes a contact point where the collision occurs. +CONDITIONAL ENABLE_PHYSICS +STRUCT ContactPoint + CSRAW internal Vector3 m_Point; + CSRAW internal Vector3 m_Normal; + CSRAW internal Collider m_ThisCollider; + CSRAW internal Collider m_OtherCollider; + + // The point of contact. + CSRAW public Vector3 point { get { return m_Point; } } + + // Normal of the contact point. + CSRAW public Vector3 normal { get { return m_Normal; } } + + // The first collider in contact. + CSRAW public Collider thisCollider { get { return m_ThisCollider; } } + + // The other collider in contact. + CSRAW public Collider otherCollider { get { return m_OtherCollider; } } +END + +// Describes collision. +CSRAW [StructLayout (LayoutKind.Sequential)] +CONDITIONAL ENABLE_PHYSICS +NONSEALED_CLASS Collision + CSRAW internal Vector3 m_RelativeVelocity; + CSRAW internal Rigidbody m_Rigidbody; + CSRAW internal Collider m_Collider; + + CSRAW internal ContactPoint[] m_Contacts; + + // The relative linear velocity of the two colliding objects (RO). + CSRAW public Vector3 relativeVelocity { get { return m_RelativeVelocity; } } + + // The [[Rigidbody]] we hit (RO). This is /null/ if the object we hit is a collider with no rigidbody attached. + CSRAW public Rigidbody rigidbody { get { return m_Rigidbody; } } + + // The [[Collider]] we hit (RO). + CSRAW + CSRAW public Collider collider { get { return m_Collider; } } + + // The [[Transform]] of the object we hit (RO). + CSRAW public Transform transform { get { return rigidbody != null ? rigidbody.transform : collider.transform; } } + + // The [[GameObject]] whose collider we are colliding with. (RO). + CSRAW public GameObject gameObject { get { return m_Rigidbody != null ? m_Rigidbody.gameObject : m_Collider.gameObject; } } + + // The contact points generated by the physics engine. + + CSRAW public ContactPoint[] contacts { get { return m_Contacts; } } + + //*undocumented* + CSRAW public virtual IEnumerator GetEnumerator () + { + return contacts.GetEnumerator (); + } + + //*undocumented* DEPRECATED + OBSOLETE warning use Collision.relativeVelocity instead. + CSRAW public Vector3 impactForceSum { get { return relativeVelocity; } } + //*undocumented* DEPRECATED + OBSOLETE warning will always return zero. + CSRAW public Vector3 frictionForceSum { get { return Vector3.zero; } } + + OBSOLETE warning Please use Collision.rigidbody, Collision.transform or Collision.collider instead + CSRAW public Component other { get { return m_Rigidbody != null ? (Component)m_Rigidbody : (Component)m_Collider; } } +END + + +// CollisionFlags is a bitmask returned by CharacterController.Move. +ENUM CollisionFlags + // CollisionFlags is a bitmask returned by CharacterController.Move. + None = 0, + + // CollisionFlags is a bitmask returned by CharacterController.Move. + Sides = 1, + + // CollisionFlags is a bitmask returned by CharacterController.Move. + Above = 2, + + // CollisionFlags is a bitmask returned by CharacterController.Move. + Below = 4, + + //*undocumented + CollidedSides = 1, + //*undocumented + CollidedAbove = 2, + //*undocumented + CollidedBelow = 4 +END + +// ControllerColliderHit is used by CharacterController.OnControllerColliderHit to give detailed information about the collision and how to deal with it. +CSRAW [StructLayout (LayoutKind.Sequential)] +CONDITIONAL ENABLE_PHYSICS +CLASS ControllerColliderHit + CSRAW internal CharacterController m_Controller; + CSRAW internal Collider m_Collider; + CSRAW internal Vector3 m_Point; + CSRAW internal Vector3 m_Normal; + CSRAW internal Vector3 m_MoveDirection; + CSRAW internal float m_MoveLength; + CSRAW internal int m_Push; + + // The controller that hit the collider + CSRAW public CharacterController controller { get { return m_Controller; } } + + // The collider that was hit by the controller + CSRAW public Collider collider { get { return m_Collider; } } + + // The rigidbody that was hit by the controller. + CSRAW public Rigidbody rigidbody { get { return m_Collider.attachedRigidbody; } } + + // The game object that was hit by the controller. + CSRAW public GameObject gameObject { get { return m_Collider.gameObject; } } + + // The transform that was hit by the controller. + CSRAW public Transform transform { get { return m_Collider.transform; } } + + // The impact point in world space. + CSRAW public Vector3 point { get { return m_Point; } } + + // The normal of the surface we collided with in world space. + CSRAW public Vector3 normal { get { return m_Normal; } } + + // Approximately the direction from the center of the capsule to the point we touch. + CSRAW public Vector3 moveDirection { get { return m_MoveDirection; } } + + // How far the character has travelled until it hit the collider. + CSRAW public float moveLength { get { return m_MoveLength; } } + + //*undocumented NOT IMPLEMENTED + CSRAW private bool push { get { return m_Push != 0; } set { m_Push = value ? 1 : 0; } } + + +END + + +// A CharacterController allows you to easily do movement constrained by collisions without having to deal with a rigidbody. +CONDITIONAL ENABLE_PHYSICS +CLASS CharacterController : Collider + + // Moves the character with /speed/. + AUTO bool SimpleMove (Vector3 speed); + + // A more complex move function taking absolute movement deltas. + AUTO CollisionFlags Move (Vector3 motion); + + + // Was the CharacterController touching the ground during the last move? + AUTO_PROP bool isGrounded IsGrounded + + // The current relative velocity of the Character (see notes). + AUTO_PROP Vector3 velocity GetVelocity + + + // What part of the capsule collided with the environment during the last CharacterController.Move call. + AUTO_PROP CollisionFlags collisionFlags GetCollisionFlags + + // The radius of the character's capsule + AUTO_PROP float radius GetRadius SetRadius + + // The height of the character's capsule + AUTO_PROP float height GetHeight SetHeight + + // The center of the character's capsule relative to the transform's position. + AUTO_PROP Vector3 center GetCenter SetCenter + + // The character controllers slope limit in degrees + AUTO_PROP float slopeLimit GetSlopeLimit SetSlopeLimit + + // The character controllers step offset in meters + AUTO_PROP float stepOffset GetStepOffset SetStepOffset + + + // OnControllerColliderHit is called when the controller hits a collider while performing a Move. + CSNONE void OnControllerColliderHit (ControllerColliderHit hit); + + + // Determines whether other rigidbodies or character controllers collide with this character controller (by default this is always enabled). + AUTO_PROP bool detectCollisions GetDetectCollisions SetDetectCollisions + +END + +CONDITIONAL ENABLE_CLOTH +// Base class used to simulate cloth physics - shared by both [[InteractiveCloth]] and [[SkinnedCloth]] +NONSEALED_CLASS Cloth : Component + + // Bending stiffness of the cloth. + AUTO_PROP float bendingStiffness GetBendingStiffness SetBendingStiffness + + // Stretching stiffness of the cloth. + AUTO_PROP float stretchingStiffness GetStretchingStiffness SetStretchingStiffness + + // Damp cloth motion. + AUTO_PROP float damping GetDamping SetDamping + + // The thickness of the cloth surface. + AUTO_PROP float thickness GetThickness SetThickness + + // A constant, external acceleration applied to the cloth. + AUTO_PROP Vector3 externalAcceleration GetExternalAcceleration SetExternalAcceleration + + // A random, external acceleration applied to the cloth. + AUTO_PROP Vector3 randomAcceleration GetRandomAcceleration SetRandomAcceleration + + // Should gravity affect the cloth simulation? + AUTO_PROP bool useGravity GetUseGravity SetUseGravity + + // Will the cloth collide with itself? + AUTO_PROP bool selfCollision GetSelfCollision SetSelfCollision + + // Is this cloth enabled? + AUTO_PROP bool enabled GetEnabled SetEnabled + + // The current vertex positions of the cloth object. + CUSTOM_PROP Vector3[] vertices + { + Vector3f* start = NULL; + if (self->GetVertices().size() > 0){ + start = &self->GetVertices()[0]; + } + return CreateScriptingArray(start, self->GetVertices().size(), GetMonoManager().GetCommonClasses().vector3); + } + + // The current normals of the cloth object. + CUSTOM_PROP Vector3[] normals + { + Vector3f* start = NULL; + if (self->GetNormals().size() > 0){ + start = &self->GetNormals()[0]; + } + return CreateScriptingArray(start, self->GetNormals().size(), GetMonoManager().GetCommonClasses().vector3); + } +END + +CONDITIONAL ENABLE_CLOTH +// The InteractiveCloth component is used to simulate objects with cloth physics. +CLASS InteractiveCloth : Cloth + + // The mesh used as base for the cloth object. + AUTO_PTR_PROP Mesh mesh GetMesh SetMesh + + // The friction of the cloth. + AUTO_PROP float friction GetFriction SetFriction + + // The density of the cloth. + AUTO_PROP float density GetDensity SetDensity + + // The pressure inside the cloth. + AUTO_PROP float pressure GetPressure SetPressure + + // How much force will be applied to colliding rigidbodies? + AUTO_PROP float collisionResponse GetCollisionResponse SetCollisionResponse + + // How far cloth vertices need to be stretched, before the cloth will tear. + AUTO_PROP float tearFactor GetTearFactor SetTearFactor + + // How far attached rigid bodies need to be stretched, before they will tear off. + AUTO_PROP float attachmentTearFactor GetAttachmentTearFactor SetAttachmentTearFactor + + // How much force will be applied to attached rigidbodies? + AUTO_PROP float attachmentResponse GetAttachmentResponse SetAttachmentResponse + + // Did the cloth tear? (RO) + AUTO_PROP bool isTeared GetIsTeared + + // Adds force /force/ to all vertices of the cloth mesh which are with /radius/ distance of /position/. + CUSTOM void AddForceAtPosition (Vector3 force, Vector3 position, float radius, ForceMode mode = ForceMode.Force) { self->AddForceAtPosition (force, position, radius, mode); } + + // Attaches a /collider/ to the cloth object. + CUSTOM void AttachToCollider (Collider collider, bool tearable = false, bool twoWayInteraction = false) { self->AttachToCollider (collider, tearable, twoWayInteraction); } + + // Detaches a /collider/ from the cloth object. + CUSTOM void DetachFromCollider (Collider collider) { self->DetachFromCollider(collider); } +END + +CONDITIONAL ENABLE_CLOTH +// The ClothSkinningCoefficient struct is used to set up how a [[SkinnedCloth]] component is allowed to move with respect to the [[SkinnedMeshRenderer]] it is attached to. +STRUCT ClothSkinningCoefficient + //Distance a vertex is allowed to travel from the skinned mesh vertex position. + CSRAW public float maxDistance; + //Distorts the sphere defined by the maxDistance based on skinned mesh normals. + CSRAW public float maxDistanceBias; + //Definition of a sphere a vertex is not allowed to enter. This allows collision against the animated cloth. + CSRAW public float collisionSphereRadius; + //Definition of a sphere a vertex is not allowed to enter. This allows collision against the animated cloth. + CSRAW public float collisionSphereDistance; +END + +CONDITIONAL ENABLE_CLOTH +// The SkinnedCloth component works together with the [[SkinnedMeshRenderer]] to simulate clothing on a character. +CLASS SkinnedCloth : Cloth + // The cloth skinning coefficients used to set up how the cloth interacts with the skinned mesh. + CUSTOM_PROP ClothSkinningCoefficient[] coefficients + { +#if !UNITY_WINRT + return CreateScriptingArray(&self->GetCoefficients()[0], self->GetCoefficients().size(), GetMonoManager().GetBuiltinMonoClass("ClothSkinningCoefficient")); +#else + return SCRIPTING_NULL; +#endif + } + { +#if !UNITY_WINRT + int count = mono_array_length_safe_wrapper(value); + if (count == self->GetCoefficients().size()) + self->SetCoefficients(&GetMonoArrayElement<SkinnedCloth::ClothConstrainCoefficients> (value, 0)); + else + ErrorString ("Number of coefficients must match number of vertices!"); +#endif + } + + // How much world-space movement of the character will affect cloth vertices. + AUTO_PROP float worldVelocityScale GetWorldVelocityScale SetWorldVelocityScale + + // How much world-space acceleration of the character will affect cloth vertices. + AUTO_PROP float worldAccelerationScale GetWorldAccelerationScale SetWorldAccelerationScale + + // Fade the cloth simulation in or out, and enabled or disable the SkinnedCloth. + CUSTOM void SetEnabledFading (bool enabled, float interpolationTime = 0.5f) { self->SetEnabledFading (enabled, interpolationTime); } +END + +CONDITIONAL ENABLE_CLOTH +// The ClothRenderer component is used together with the [[InteractiveCloth]] component, to visualize a cloth object in the scene. +CLASS ClothRenderer : Renderer + // Pause the cloth simulation, when the ClothRenderer is not currently visible. + AUTO_PROP bool pauseWhenNotVisible GetPauseWhenNotVisible SetPauseWhenNotVisible +END + +// A heightmap based collider. +CONDITIONAL ENABLE_TERRAIN && ENABLE_PHYSICS +CLASS TerrainCollider : Collider + + // The terrain that stores the heightmap + AUTO_PTR_PROP TerrainData terrainData GetTerrainData SetTerrainData + +END + +CSRAW +} +#endif diff --git a/Runtime/Dynamics/SkinnedCloth.cpp b/Runtime/Dynamics/SkinnedCloth.cpp new file mode 100644 index 0000000..65570c5 --- /dev/null +++ b/Runtime/Dynamics/SkinnedCloth.cpp @@ -0,0 +1,347 @@ +#include "UnityPrefix.h" +#include "SkinnedCloth.h" + +#if ENABLE_CLOTH + +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Filters/Mesh/MeshSkinning.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "Runtime/Input/TimeManager.h" +#include "PhysicsManager.h" +#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Math/Random/Random.h" + +extern Rand gClothRand; + +namespace Unity +{ + +SkinnedCloth::SkinnedCloth (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode), + m_UpdateNode(this) +{ + m_Fade = 0.0f; + m_TargetFade = 1.0f; + m_NeedsToReadVertices = true; + m_InterpolationTime = 0.1f; + m_WorldVelocityScale = 0.5f; + m_WorldAccelerationScale = 1.0f; +} + +SkinnedCloth::~SkinnedCloth () +{ +} + +void SkinnedCloth::Reset () +{ + Super::Reset(); + + m_WorldVelocityScale = 0.5f; + m_WorldAccelerationScale = 1.0f; +} + +#if ENABLE_CLOTH + +void SkinnedCloth::Create() +{ + Cleanup (); + + SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer); + Assert (smr != NULL); + if (smr->GetMesh() == NULL) + return; + + m_LastFrameWorldPosition = smr->GetActualRootBone().GetPosition(); + m_LastFrameVelocity = Vector3f::zero; + + smr->SetCloth (this); + m_Mesh = smr->GetMesh(); + +#if UNITY_PS3 + //-- we need to unify the streams for skinned cloth + int streamCount = 0; + VertexData& oldVertexData = m_Mesh->GetVertexData(); + if (oldVertexData.GetActiveStreamCount() > 2) + { + VertexStreamsLayout kHotColdSplit = {{ kShaderChannelsHot, kShaderChannelsCold, 0, 0 }}; + VertexData newVertexData(oldVertexData, m_Mesh->GetAvailableChannels(), kHotColdSplit); + swap (newVertexData, oldVertexData); + } +#endif + + SetupMeshData (false, true, 1); + + NxClothDesc clothDesc; + SetupClothDesc (clothDesc, false); + clothDesc.flags |= NX_CLF_DISABLE_COLLISION; + + m_ClothScene = GetPhysicsManager ().GetClothingScene(); + m_Cloth = m_ClothScene->createCloth(clothDesc); + + if (m_Cloth == NULL) + GetDynamicsSDK().releaseClothMesh(*clothDesc.clothMesh); + + SetupCoefficients (); + + m_VertexBuffer = NULL; + m_NormalBuffer = NULL; + m_TangentBuffer = NULL; + m_VertexBufferStride = 0; +} + +void SkinnedCloth::Cleanup () +{ + Super::Cleanup (); + + if (GetGameObjectPtr() != NULL) + { + SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer); + if (smr != NULL) + smr->SetCloth (NULL); + } +} + +void SkinnedCloth::SetEnabled (bool enabled) { + + if (enabled != GetEnabled()) + { + Super::SetEnabled (enabled); + if (enabled) + { + m_Fade = 0.0f; + m_TargetFade = 1.0f; + m_InterpolationTime = 0.1f; + m_NeedsToReadVertices = true; + SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer); + smr->SetCloth (this); + m_LastFrameWorldPosition = smr->GetActualRootBone().GetPosition(); + m_LastFrameVelocity = Vector3f::zero; + } + else + GetComponent(SkinnedMeshRenderer).SetCloth (NULL); + } +} + +void SkinnedCloth::SetEnabledFading (bool enabled, float interpolationTime) +{ + if (enabled && !GetEnabled()) + SetEnabled(true); + m_TargetFade = enabled ? 1.0f : 0.0f; + m_InterpolationTime = interpolationTime; +} + +void SkinnedCloth::LateUpdate () +{ + if (m_Cloth) + { + // calculate forces from world space movement + Vector3f externalAcceleration = m_ExternalAcceleration+RandomPointInsideCube (gClothRand, m_RandomAcceleration); + SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer); + Vector3f worldPosition = smr->GetActualRootBone().GetPosition(); + Vector3f velocity = (m_LastFrameWorldPosition - worldPosition) * GetInvDeltaTime(); + // Use squared velocity, as air friction is quadratic. + externalAcceleration += velocity * Magnitude(velocity) * m_WorldVelocityScale; + Vector3f acceleration = (m_LastFrameVelocity - velocity) * GetInvDeltaTime(); + externalAcceleration -= acceleration * m_WorldAccelerationScale; + + m_LastFrameWorldPosition = worldPosition; + m_LastFrameVelocity = velocity; + m_Cloth->setExternalAcceleration((const NxVec3&)externalAcceleration); + } + + if (m_Fade != m_TargetFade) + { + if (m_InterpolationTime == 0.0f) + m_Fade = m_TargetFade; + else if (m_Fade > m_TargetFade) + { + m_Fade -= GetDeltaTime() / m_InterpolationTime; + if (m_Fade < m_TargetFade) + m_Fade = m_TargetFade; + } + else if (m_Fade < m_TargetFade) + { + m_Fade += GetDeltaTime() / m_InterpolationTime; + if (m_Fade > m_TargetFade) + m_Fade = m_TargetFade; + } + SetupCoefficients (); + if (m_Fade == 0.0f && m_TargetFade == 0.0f) + SetEnabled (false); + } +} + +void SkinnedCloth::AddToManager () +{ + GetLateBehaviourManager().AddBehaviour (m_UpdateNode, -1); +} + +void SkinnedCloth::RemoveFromManager () +{ + GetLateBehaviourManager().RemoveBehaviour (m_UpdateNode); +} + +void SkinnedCloth::SetupCoefficients () +{ + m_Coefficients.resize (m_NumVertices); + if (m_Cloth) + { + if (m_Fade == 1.0) + m_Cloth->setConstrainCoefficients ((NxClothConstrainCoefficients*)&m_Coefficients[0]); + else + { + std::vector<ClothConstrainCoefficients> coefficients = m_Coefficients; + for (std::vector<ClothConstrainCoefficients>::iterator i = coefficients.begin(); i != coefficients.end(); i++) + i->maxDistance *= m_Fade; + m_Cloth->setConstrainCoefficients ((NxClothConstrainCoefficients*)&coefficients[0]); + } + } +} + +PROFILER_INFORMATION(gSetUpProfile, "SkinnedCloth.SetUpSkinnedBuffers", kProfilerPhysics) + +void SkinnedCloth::SetUpSkinnedBuffers (void *vertices, void *normals, void *tangents, size_t bufferStride) +{ + PROFILER_AUTO(gSetUpProfile, NULL) + + // If the mesh has changed, re-created skinning buffers to match the changed mesh + SkinnedMeshRenderer *smr = QueryComponent(SkinnedMeshRenderer); + if (smr->GetMesh() != m_Mesh || m_Mesh->GetVertexCount() != m_VertexTranslationTable.size()) + Create(); + + QuaternionToMatrix(Inverse(smr->GetActualRootBone().GetRotation()), m_WorldToLocalRotationMatrix); + + int numTotalVertices = m_VertexTranslationTable.size(); + if (normals) + { + for (int i=0; i<numTotalVertices; i++) + { + int index = m_VertexTranslationTable[i]; + m_Vertices[index] = *(Vector3f*)((char*)vertices+i*bufferStride); + m_Normals[index] = *(Vector3f*)((char*)normals+i*bufferStride); + } + } + else + { + for (int i=0; i<numTotalVertices; i++) + { + int index = m_VertexTranslationTable[i]; + m_Vertices[index] = *(Vector3f*)((char*)vertices+i*bufferStride); + } + } + + // If the component has just been enabled, read vertices for the first frame, + // so we don't suddenly jerk into place. + if (m_NeedsToReadVertices) + { + m_NeedsToReadVertices = false; + m_Cloth->setPositions (&m_Vertices[0], sizeof(Vector3f)); + } + + m_Cloth->setConstrainPositions (&m_Vertices[0], sizeof(Vector3f)); + if (normals) + m_Cloth->setConstrainNormals (&m_Normals[0], sizeof(Vector3f)); + + m_VertexBuffer = vertices; + m_NormalBuffer = normals; + m_TangentBuffer = tangents; + m_VertexBufferStride = bufferStride; +} + +void SkinnedCloth::ReadBackSkinnedBuffers () +{ + if (m_VertexBuffer != NULL) + { + TransformPoints3x3 (m_WorldToLocalRotationMatrix, &m_Vertices[0], &m_Vertices[0], m_NumVertices); + TransformPoints3x3 (m_WorldToLocalRotationMatrix, &m_Normals[0], &m_Normals[0], m_NumVertices); + + int numTotalVertices = m_VertexTranslationTable.size(); + if (m_NormalBuffer) + { + for (int i=0; i<numTotalVertices; i++) + { + int index = m_VertexTranslationTable[i]; + *(Vector3f*)((char*)m_VertexBuffer+i*m_VertexBufferStride) = m_Vertices[index]; + *(Vector3f*)((char*)m_NormalBuffer+i*m_VertexBufferStride) = m_Normals[index]; + } + } + else + { + for (int i=0; i<numTotalVertices; i++) + { + int index = m_VertexTranslationTable[i]; + *(Vector3f*)((char*)m_VertexBuffer+i*m_VertexBufferStride) = m_Vertices[index]; + } + } + + if (m_TangentBuffer != NULL) + { + // PhysX cloth will not output tangents. But we should at least + // transform them so things don't look completely wrong. + for (int i=0; i<numTotalVertices; i++) + { + Vector3f& v = *(Vector3f*)((char*)m_TangentBuffer+i*m_VertexBufferStride); + v = m_WorldToLocalRotationMatrix.MultiplyPoint3 (v); + } + } + } +} + +void SkinnedCloth::SetCoefficients(ClothConstrainCoefficients *coefficients) +{ + memcpy (&m_Coefficients[0], coefficients, sizeof(SkinnedCloth::ClothConstrainCoefficients) * m_Coefficients.size()); + SetupCoefficients(); + SetDirty(); +} + +#else //ENABLE_CLOTH + +void SkinnedCloth::Cleanup () {} +void SkinnedCloth::Create() {} +void SkinnedCloth::LateUpdate() {} +void SkinnedCloth::SetEnabled (bool) {} +void SkinnedCloth::SetEnabledFading (bool enabled, float interpolationTime) {} +void SkinnedCloth::AddToManager () {} +void SkinnedCloth::RemoveFromManager () {} +void SkinnedCloth::SetCoefficients(ClothConstrainCoefficients *coefficients) {} +#endif //ENABLE_CLOTH + +void SkinnedCloth::SetWorldVelocityScale (float value) +{ + m_WorldVelocityScale = value; + SetDirty(); +} + +void SkinnedCloth::SetWorldAccelerationScale (float value) +{ + m_WorldAccelerationScale = value; + SetDirty(); +} + +template<class TransferFunction> +void SkinnedCloth::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + TRANSFER (m_WorldVelocityScale); + TRANSFER (m_WorldAccelerationScale); + transfer.Transfer (m_Coefficients, "m_Coefficients", kHideInEditorMask); +} + +template<class TransferFunction> +void SkinnedCloth::ClothConstrainCoefficients::Transfer (TransferFunction& transfer) +{ + TRANSFER (maxDistance); + TRANSFER (maxDistanceBias); + TRANSFER (collisionSphereRadius); + TRANSFER (collisionSphereDistance); +} + +} + +IMPLEMENT_CLASS (SkinnedCloth) +IMPLEMENT_OBJECT_SERIALIZE (SkinnedCloth) + +#endif // ENABLE_CLOTH diff --git a/Runtime/Dynamics/SkinnedCloth.h b/Runtime/Dynamics/SkinnedCloth.h new file mode 100644 index 0000000..5f5a72f --- /dev/null +++ b/Runtime/Dynamics/SkinnedCloth.h @@ -0,0 +1,82 @@ +#pragma once +#include "Configuration/UnityConfigure.h" + +#if ENABLE_CLOTH + +#include "DeformableMesh.h" +#include "Runtime/Math/Matrix3x3.h" + +namespace Unity +{ + + +class SkinnedCloth : public Cloth +{ +public: + REGISTER_DERIVED_CLASS (SkinnedCloth, Cloth) + DECLARE_OBJECT_SERIALIZE (SkinnedCloth) + + SkinnedCloth (MemLabelId label, ObjectCreationMode mode); + + void SetUpSkinnedBuffers (void *vertices, void *normals, void *tangents, size_t bufferStride); + void ReadBackSkinnedBuffers (); + + virtual void Reset (); + virtual void SetEnabled (bool enabled); + virtual void LateUpdate (); + virtual void AddToManager (); + virtual void RemoveFromManager (); + + struct ClothConstrainCoefficients { + float maxDistance; + float maxDistanceBias; + float collisionSphereRadius; + float collisionSphereDistance; + DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (ClothConstrainCoefficients) + + ClothConstrainCoefficients () : maxDistance(0.05f), maxDistanceBias(0), collisionSphereRadius(0.5f), collisionSphereDistance(0) {} + }; + + std::vector<ClothConstrainCoefficients> &GetCoefficients() {return m_Coefficients;} + void SetCoefficients(ClothConstrainCoefficients *coefficients); + + float GetWorldVelocityScale () const { return m_WorldVelocityScale; } + void SetWorldVelocityScale (float value); + + float GetWorldAccelerationScale () const { return m_WorldAccelerationScale; } + void SetWorldAccelerationScale (float value); + + void SetEnabledFading (bool enabled, float interpolationTime); + +protected: + + virtual void Create (); + virtual void Cleanup (); + + void SetupCoefficients(); + + float m_Fade; + float m_TargetFade; + float m_InterpolationTime; + + bool m_NeedsToReadVertices; + void *m_VertexBuffer; + void *m_NormalBuffer; + void *m_TangentBuffer; + Matrix3x3f m_WorldToLocalRotationMatrix; + size_t m_VertexBufferStride; + + Vector3f m_LastFrameWorldPosition; + Vector3f m_LastFrameVelocity; + + float m_WorldVelocityScale; ///<How much world-space movement of the character will affect cloth vertices. + float m_WorldAccelerationScale; ///<How much world-space acceleration of the character will affect cloth vertices. + + BehaviourListNode m_UpdateNode; + + std::vector<ClothConstrainCoefficients> m_Coefficients; +}; + +} + +#endif // ENABLE_CLOTH diff --git a/Runtime/Dynamics/SphereCollider.cpp b/Runtime/Dynamics/SphereCollider.cpp new file mode 100644 index 0000000..9bd05cb --- /dev/null +++ b/Runtime/Dynamics/SphereCollider.cpp @@ -0,0 +1,272 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "SphereCollider.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 NxSphereShape*)m_Shape) + +using namespace std; + +SphereCollider::SphereCollider (MemLabelId label, ObjectCreationMode mode) + : Super(label, mode) +{ + #if UNITY_EDITOR + fixupSphereColliderBackwardsCompatibility = false; + #endif +} + +SphereCollider::~SphereCollider () +{ +} + +void SphereCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if (m_Shape) + { + // Apply changed values + SetRadius (m_Radius); + SetCenter (m_Center); + } + + Super::AwakeFromLoad (awakeMode); + + #if UNITY_EDITOR + if (fixupSphereColliderBackwardsCompatibility) + { + float oldRadius = m_Radius; + m_Radius = 1.0F; + if (GetScaledRadius () > 0.001) + { + SetRadius (oldRadius / GetScaledRadius ()); + } + fixupSphereColliderBackwardsCompatibility = false; + } + #endif +} + +void SphereCollider::SmartReset () +{ + Super::Reset (); + AABB aabb; + if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb)) + { + Vector3f dist = aabb.GetExtent (); + float longestAxis = max (dist.x, dist.y); + longestAxis = max (longestAxis, dist.z); + SetRadius (longestAxis); + SetCenter (aabb.GetCenter ()); + } + else + { + SetRadius (0.5F); + SetCenter (Vector3f::zero); + } +} + +void SphereCollider::Reset () +{ + Super::Reset (); + m_Radius = 0.5F; + m_Center = Vector3f::zero; +} + +float SphereCollider::GetScaledRadius () const +{ + Vector3f scale = GetComponent (Transform).GetWorldScaleLossy (); + float absoluteRadius = max (max (Abs (scale.x), Abs (scale.y)), Abs (scale.z)) * m_Radius; + absoluteRadius = Abs (absoluteRadius); + absoluteRadius = max (absoluteRadius, 0.00001F); + return absoluteRadius; +} + +void SphereCollider::SetRadius (float radius) +{ + if (m_Radius != radius) + { + SetDirty (); + m_Radius = radius; + } + + PROFILE_MODIFY_STATIC_COLLIDER + + if (GET_SHAPE ()) + { + GET_SHAPE ()->setRadius (GetScaledRadius ()); + RigidbodyMassDistributionChanged (); + RefreshPhysicsInEditMode(); + UpdateCCDSkeleton (); + } +} + + +void SphereCollider::SetCenter (const Vector3f& center) +{ + if (m_Center != center) + { + m_Center = center; + SetDirty (); + } + + if (GET_SHAPE ()) + TransformChanged (Transform::kRotationChanged | Transform::kPositionChanged | kForceUpdateMass); +} + +Vector3f SphereCollider::GetGlobalCenter () const +{ + return GetComponent (Transform).TransformPoint (m_Center); +} + +void SphereCollider::Create (const Rigidbody* ignoreRigidbody) +{ + if (m_Shape) + Cleanup (); + + NxSphereShapeDesc shapeDesc; + shapeDesc.radius = GetScaledRadius (); + + FinalizeCreate (shapeDesc, true, ignoreRigidbody); +} + +void SphereCollider::FetchPoseFromTransform () +{ + FetchPoseFromTransformUtility (m_Center); +} + +bool SphereCollider::GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix) +{ + return GetRelativeToParentPositionAndRotationUtility (transform, anyParent, m_Center, matrix); +} + +void SphereCollider::ScaleChanged () +{ + GET_SHAPE ()->setRadius (GetScaledRadius ()); + UpdateCCDSkeleton (); +} + +void SphereCollider::TransformChanged (int changeMask) +{ + Super::TransformChanged (changeMask); + if (m_Shape) + { + if (!m_Shape->getActor ().userData) + { + PROFILER_AUTO(gStaticColliderMove, this) + FetchPoseFromTransformUtility (m_Center); + } + else + { + Rigidbody* body = (Rigidbody*)m_Shape->getActor ().userData; + Matrix4x4f matrix; + if (GetRelativeToParentPositionAndRotationUtility (GetComponent (Transform), body->GetComponent (Transform), m_Center, 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(); + } +} + +AABB SphereCollider::GetBounds () +{ + if (m_Shape) + { + // AABB reported by PhysX is inaccurate, as PhysX will just transform the local AABB. + // For Spheres it's very easy to do better. + float globalRadius = GetScaledRadius(); + return AABB(GetGlobalCenter(), Vector3f(globalRadius, globalRadius, globalRadius)); + } + else + return Super::GetBounds (); +} + +NxCCDSkeleton* SphereCollider::CreateCCDSkeleton(float scale) +{ + // This is a very simple "approximation" of a sphere, of only 6 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 = scale; + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + radius *= GetScaledRadius(); + else + // Prior to 4.0 we incorrectly ignored object scale here. Keep this behaviour for backwards compatibility. + radius *= m_Radius; + + NxU32 triangles[3 * 8] = { + 0,1,2, + 0,2,3, + 0,3,4, + 0,4,1, + 5,2,1, + 5,3,2, + 5,4,3, + 5,1,4, + }; + + NxVec3 points[6]; + + // Static mesh + points[0].set( 0, -radius, 0); + points[1].set( -radius, 0, 0); + points[2].set( 0, 0, -radius); + points[3].set( radius, 0, 0); + points[4].set( 0, 0, radius); + points[5].set( 0, radius, 0); + + if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1)) + { + // This is wrong, as the m_Center transformation is already applied at the shape matrix. + // Keep for backward compatibility. + NxVec3 center(m_Center.x, m_Center.y, m_Center.z); + for (int i=0;i<6;i++) + points[i] += center; + } + + NxSimpleTriangleMesh stm; + stm.numVertices = 6; + stm.numTriangles = 8; + stm.pointStrideBytes = sizeof(NxVec3); + stm.triangleStrideBytes = sizeof(NxU32)*3; + + stm.points = points; + stm.triangles = triangles; + return GetDynamicsSDK().createCCDSkeleton(stm); +} + +template<class TransferFunction> +void SphereCollider::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.SetVersion (2); + + transfer.Align(); + + #if UNITY_EDITOR + fixupSphereColliderBackwardsCompatibility = transfer.IsOldVersion (1); + #endif + + TRANSFER_SIMPLE (m_Radius); + TRANSFER (m_Center); +} + +IMPLEMENT_CLASS (SphereCollider) +IMPLEMENT_OBJECT_SERIALIZE (SphereCollider) + +#undef GET_SHAPE +#endif //ENABLE_PHYSICS
\ No newline at end of file diff --git a/Runtime/Dynamics/SphereCollider.h b/Runtime/Dynamics/SphereCollider.h new file mode 100644 index 0000000..1a3f779 --- /dev/null +++ b/Runtime/Dynamics/SphereCollider.h @@ -0,0 +1,56 @@ +#ifndef SPHERECOLLIDER_H +#define SPHERECOLLIDER_H + +#include "Collider.h" +#include "Runtime/Math/Vector3.h" + + +class SphereCollider : public Collider +{ + public: + REGISTER_DERIVED_CLASS (SphereCollider, Collider) + DECLARE_OBJECT_SERIALIZE (SphereCollider) + + SphereCollider (MemLabelId label, ObjectCreationMode mode); + + virtual void Reset (); + virtual void SmartReset (); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + void SetRadius (float r); + float GetRadius () const { return m_Radius; } + + float GetScaledRadius () const; + + void SetCenter (const Vector3f& center); + Vector3f GetCenter () { return m_Center; } + + Vector3f GetGlobalCenter () const; + + void TransformChanged (int changeMask); + + virtual AABB GetBounds (); + + protected: + + virtual void Create (const Rigidbody* ignoreAttachRigidbody); + virtual void FetchPoseFromTransform (); + virtual bool GetRelativeToParentPositionAndRotation (Transform& transform, Transform& anyParent, Matrix4x4f& matrix); + + virtual NxCCDSkeleton* CreateCCDSkeleton(float scale); + + void ScaleChanged (); + + /// The radius of the sphere. range { 0.00001, infinity } + float m_Radius; + Vector3f m_Center; + + #if UNITY_EDITOR + /// In unity version 1.0 sphere radius did not change with scale. + /// This was fixed with version 1.1 + /// In the transfer function we check if we should up to account for the now introduced scale. + bool fixupSphereColliderBackwardsCompatibility; + #endif +}; + +#endif diff --git a/Runtime/Dynamics/SpringJoint.cpp b/Runtime/Dynamics/SpringJoint.cpp new file mode 100644 index 0000000..440d8f8 --- /dev/null +++ b/Runtime/Dynamics/SpringJoint.cpp @@ -0,0 +1,161 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "SpringJoint.h" +#include "PhysicsManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" + +using namespace std; + +namespace Unity +{ + +#define GET_JOINT() static_cast<NxDistanceJoint*> (m_Joint) + +/* +- We awake the hingejoint only once. (AwakeFromLoad) + At this point we setup the axes. They are never changed afterwards + -> The perfect solution remembers the old position/rotation of the rigid bodies. + Then when changing axis/anchor is changed it generates axes that are rleative to the old position/rotation state! +*/ + +SpringJoint::SpringJoint (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +SpringJoint::~SpringJoint () +{ +} + +void SpringJoint::Reset() +{ + Super::Reset(); + m_MinDistance = 0.0F; + m_MaxDistance = 0.5F; + m_Spring = 2; + m_Damper = 0.2f; +} + +void SpringJoint::Create () +{ + AssertIf (!IsActive ()); + + NxDistanceJointDesc desc; + + if (m_Joint && m_Joint->getState () == NX_JS_SIMULATING) + GET_JOINT()->saveToDesc (desc); + + desc.spring.spring = m_Spring; + desc.spring.damper = m_Damper; + + if (desc.minDistance < m_MaxDistance) + { + desc.minDistance = m_MinDistance; + desc.maxDistance = m_MaxDistance; + } + else + { + desc.minDistance = m_MaxDistance; + desc.maxDistance = m_MinDistance; + } + + desc.flags |= NX_DJF_SPRING_ENABLED | NX_DJF_MAX_DISTANCE_ENABLED | NX_DJF_MIN_DISTANCE_ENABLED; + + FINALIZE_CREATE (desc, NxDistanceJointDesc); +} +void SpringJoint::SetSpring (float spring) +{ + m_Spring = spring; + SetDirty(); + + if (GET_JOINT() != NULL) + { + NxDistanceJointDesc desc; + GET_JOINT()->saveToDesc (desc); + desc.spring.spring = m_Spring; + GET_JOINT()->loadFromDesc (desc); + } +} + +void SpringJoint::SetDamper(float damper) +{ + m_Damper = damper; + SetDirty(); + + if (GET_JOINT() != NULL) + { + NxDistanceJointDesc desc; + GET_JOINT()->saveToDesc (desc); + desc.spring.damper = m_Damper; + GET_JOINT()->loadFromDesc (desc); + } +} + +void SpringJoint::SetMinDistance(float distance) +{ + m_MinDistance = distance; + SetDirty(); + + if (GET_JOINT() != NULL) + { + NxDistanceJointDesc desc; + GET_JOINT()->saveToDesc (desc); + if (desc.minDistance < m_MaxDistance) + { + desc.minDistance = m_MinDistance; + desc.maxDistance = m_MaxDistance; + } + else + { + desc.minDistance = m_MaxDistance; + desc.maxDistance = m_MinDistance; + } + GET_JOINT()->loadFromDesc (desc); + } +} + +void SpringJoint::SetMaxDistance(float distance) +{ + m_MaxDistance = distance; + SetDirty(); + + if (GET_JOINT() != NULL) + { + NxDistanceJointDesc desc; + GET_JOINT()->saveToDesc (desc); + if (desc.minDistance < m_MaxDistance) + { + desc.minDistance = m_MinDistance; + desc.maxDistance = m_MaxDistance; + } + else + { + desc.minDistance = m_MaxDistance; + desc.maxDistance = m_MinDistance; + } + GET_JOINT()->loadFromDesc (desc); + } +} + +IMPLEMENT_AXIS_ANCHOR(SpringJoint,NxDistanceJointDesc) + +template<class TransferFunction> +void SpringJoint::Transfer (TransferFunction& transfer) +{ + JointTransferPreNoAxis(transfer); + TRANSFER_SIMPLE (m_Spring); + TRANSFER_SIMPLE (m_Damper); + TRANSFER_SIMPLE (m_MinDistance); + TRANSFER_SIMPLE (m_MaxDistance); + JointTransferPost (transfer); +} + +#undef GET_JOINT + +} + +IMPLEMENT_CLASS (SpringJoint) +IMPLEMENT_OBJECT_SERIALIZE (SpringJoint) + +#endif //ENABLE_PHYSICS
\ No newline at end of file diff --git a/Runtime/Dynamics/SpringJoint.h b/Runtime/Dynamics/SpringJoint.h new file mode 100644 index 0000000..99288c9 --- /dev/null +++ b/Runtime/Dynamics/SpringJoint.h @@ -0,0 +1,51 @@ +#ifndef SPRINGJOINT_H +#define SPRINGJOINT_H + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Math/Vector3.h" +#include "JointDescriptions.h" +#include "Joint.h" +class Rigidbody; +class NxJointDesc; + +namespace Unity +{ + + +class SpringJoint : public Joint +{ + public: + + REGISTER_DERIVED_CLASS (SpringJoint, Joint) + DECLARE_OBJECT_SERIALIZE (SpringJoint) + + SpringJoint (MemLabelId label, ObjectCreationMode mode); + void Reset(); + + float GetSpring () const { return m_Spring; } + void SetSpring (float spring); + + float GetDamper () { return m_Damper; } + void SetDamper(float damper); + + float GetMinDistance() { return m_MinDistance; } + void SetMinDistance(float distance); + + float GetMaxDistance() { return m_MaxDistance; } + void SetMaxDistance(float distance); + + private: + + virtual void ApplySetupAxesToDesc (int option); + virtual void Create (); + + float m_MinDistance; ///< range { 0, infinity } + float m_MaxDistance; ///< range { 0, infinity } + float m_Spring; ///< range { 0, infinity } + float m_Damper; ///< range { 0, infinity } +}; + +} + +#endif // SPRINGJOINT_H + diff --git a/Runtime/Dynamics/TerrainCollider.cpp b/Runtime/Dynamics/TerrainCollider.cpp new file mode 100644 index 0000000..67a82a0 --- /dev/null +++ b/Runtime/Dynamics/TerrainCollider.cpp @@ -0,0 +1,238 @@ +#include "UnityPrefix.h" + +#if ENABLE_TERRAIN && ENABLE_PHYSICS +#include "TerrainCollider.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Dynamics/PhysicsManager.h" + +///@TODO: THis should really only be ITerrain +#include "Runtime/Terrain/TerrainData.h" +#include "Runtime/Terrain/Heightmap.h" +#include "Runtime/Interfaces/ITerrainManager.h" + +#include "Runtime/Dynamics/CapsuleCollider.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/BaseClasses/SupportedMessageOptimization.h" +#include "Runtime/Dynamics/NxWrapperUtility.h" + +inline UInt32 GetLowMaterialIndex (UInt32 index) +{ + return index & 0x7F; +} + +inline UInt32 GetHighMaterialIndex (UInt32 index) +{ + return index >> 7; +} + + +IMPLEMENT_CLASS_HAS_INIT(TerrainCollider) +IMPLEMENT_OBJECT_SERIALIZE(TerrainCollider) + +TerrainCollider::TerrainCollider (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +, m_Node (this) +{ + m_CreateTreeColliders = true; + m_CachedInvSize = Vector3f(1.0F, 1.0F, 1.0F); +} + +TerrainCollider::~TerrainCollider () +{ +} + +void TerrainCollider::SetTerrainData (PPtr<TerrainData> map) +{ + if (m_TerrainData != map) + { + m_TerrainData = map; + Create(NULL); + SetDirty(); + } +} + +TerrainData* TerrainCollider::GetTerrainData () +{ + return m_TerrainData; +} + +void TerrainCollider::Cleanup () +{ + Super::Cleanup(); + + for (int i=0;i<m_TreeColliders.size();i++) + GetDynamicsScene ().releaseActor (m_TreeColliders[i]->getActor()); + m_TreeColliders.clear(); + + m_Node.RemoveFromList(); +} + +void TerrainCollider::Create (const Rigidbody* ignoreAttachRigidbody) +{ + Cleanup (); + + if (!GetTerrainData()) + return; + + ITerrainManager* terrainManager = GetITerrainManager(); + + //////@TODO: Make this more directly just return the physx representation... + + Heightmap* map = &GetTerrainData()->GetHeightmap(); + if (map == NULL || terrainManager->Heightmap_GetNxHeightField(*map) == NULL) + return; + + NxHeightFieldShapeDesc desc; + desc.heightField = terrainManager->Heightmap_GetNxHeightField(*map); + + if (desc.heightField == NULL) + return; + + m_CachedInvSize = Inverse(terrainManager->Heightmap_GetSize(*map)); + Vector3f mapScale = map->GetScale (); + desc.heightScale = mapScale.y / (float)(Heightmap::kMaxHeight); + desc.columnScale = mapScale.z; + desc.rowScale = mapScale.x; + desc.holeMaterial = 1; + + // Smooth sphere collisions on terrains are much better! + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1)) + desc.meshFlags = NX_MESH_SMOOTH_SPHERE_COLLISIONS; + + desc.materialIndexHighBits = GetHighMaterialIndex(terrainManager->Heightmap_GetMaterialIndex(*map)); + FinalizeCreate(desc, true, ignoreAttachRigidbody); + + if (m_Shape) + { + // Insert into front of terrain colliders. Create() may be called from inside + // of RecreateColliders, and inserting to back will result in eternal loop. + Heightmap::TerrainColliderList& colliders = map->GetTerrainColliders(); + colliders.insert(colliders.begin(), m_Node); + } + + // Dont create trees in edit mode,its too slow to recreate all those trees when adding new trees + if (m_CreateTreeColliders && IsWorldPlaying()) + CreateTrees(); +} + +void TerrainCollider::CreateTrees () +{ + const float kMinSize = 0.00001F; + int prototypeCount = m_TerrainData->GetTreeDatabase().GetTreePrototypes().size(); + const TreePrototype* prototypes = prototypeCount > 0 ? &m_TerrainData->GetTreeDatabase().GetTreePrototypes()[0] : NULL; + + int instanceCount = m_TerrainData->GetTreeDatabase().GetInstances().size(); + const TreeInstance* instances = instanceCount > 0 ? &m_TerrainData->GetTreeDatabase().GetInstances()[0] : NULL; + + + int supportedMessages = GetGameObject ().GetSupportedMessages (); + + NxActorDesc actorDesc; + NxCapsuleShapeDesc shapeDesc; + actorDesc.userData = NULL; + actorDesc.shapes.push_back (&shapeDesc); + shapeDesc.materialIndex = GetMaterialIndex (); + + if (supportedMessages & kHasCollisionStay) + actorDesc.group = kContactTouchGroup; + else if (supportedMessages & (kHasCollisionStay | kHasCollisionEnterExit)) + actorDesc.group = kContactEnterExitGroup; + else + actorDesc.group = kContactNothingGroup; + shapeDesc.userData = this; + shapeDesc.group = GetGameObject ().GetLayer (); + + Vector3f terrainPositionOffset = GetComponent(Transform).GetPosition(); + + Vector3f scale = GetITerrainManager()->Heightmap_GetSize(m_TerrainData->GetHeightmap()); + NxMat33 id33; + id33.id(); + for (int i=0;i<instanceCount;i++) + { + const TreeInstance& instance = instances[i]; + + if (instance.index >= prototypeCount) + { + ErrorString("Prototype for tree missing."); + continue; + } + + const TreePrototype& proto = prototypes[instance.index]; + Vector3f pos = Scale(scale, instance.position) + terrainPositionOffset;; + GameObject* prefab = proto.prefab; + if (prefab == NULL) + continue; + CapsuleCollider* capsule = prefab->QueryComponent(CapsuleCollider); + if (capsule == NULL) + continue; + + float absoluteHeight = max (Abs (capsule->GetHeight() * instance.heightScale), kMinSize); + float absoluteRadius = Abs (instance.widthScale) * capsule->GetRadius(); + + float height = absoluteHeight - absoluteRadius * 2.0F; + + height = max (height, kMinSize); + absoluteRadius = max (absoluteRadius, kMinSize); + + shapeDesc.height = height; + shapeDesc.radius = absoluteRadius; + shapeDesc.group = prefab->GetLayer(); + + pos += Scale(capsule->GetCenter(), Vector3f(instance.widthScale, instance.heightScale, instance.widthScale)); + + actorDesc.globalPose = NxMat34(id33, Vec3ToNx(pos )); + + NxActor* actor = GetDynamicsScene ().createActor (actorDesc); + if (actor == NULL) + { + ErrorString ("Could not create tree colliders. Maybe there are more Trees then PhysX can handle?"); + for (int i=0;i<m_TreeColliders.size();i++) + GetDynamicsScene ().releaseActor (m_TreeColliders[i]->getActor()); + m_TreeColliders.clear(); + return; + } + NxShape* shape = actor->getShapes ()[0]; + + m_TreeColliders.push_back(shape); + } +} + +template<class TransferFunc> +void TerrainCollider::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + transfer.Align(); + TRANSFER(m_TerrainData); + TRANSFER(m_CreateTreeColliders); +} + +void TerrainCollider::FetchPoseFromTransform () +{ + NxQuat quat; quat.id(); + Vector3f pos = GetComponent(Transform).GetPosition(); + NxMat34 shapeMatrix (quat, (const NxVec3&)pos); + + m_Shape->setGlobalPose(shapeMatrix); +} + +void TerrainCollider::InitializeClass () +{ + REGISTER_MESSAGE (TerrainCollider, kTerrainChanged, TerrainChanged, int); +} + +void TerrainCollider::TransformChanged (int changeMask) +{ + Super::TransformChanged(changeMask); + + if (m_Shape) + FetchPoseFromTransform (); +} + +void TerrainCollider::TerrainChanged (int changeMask) +{ + if (changeMask == TerrainData::kWillBeDestroyed) + Cleanup(); +} +#endif diff --git a/Runtime/Dynamics/TerrainCollider.h b/Runtime/Dynamics/TerrainCollider.h new file mode 100644 index 0000000..1d01f41 --- /dev/null +++ b/Runtime/Dynamics/TerrainCollider.h @@ -0,0 +1,46 @@ +#pragma once + +#if ENABLE_TERRAIN && ENABLE_PHYSICS + +#include "Runtime/Math/Vector3.h" +#include "Runtime/Utilities/LinkedList.h" +#include "Runtime/Dynamics/Collider.h" + +class TerrainData; + +class TerrainCollider : public Collider +{ + PPtr<TerrainData> m_TerrainData; + bool m_CreateTreeColliders; + ListNode<TerrainCollider> m_Node; + std::vector<NxShape*> m_TreeColliders; + Vector3f m_CachedInvSize; + + public: + + TerrainCollider (MemLabelId label, ObjectCreationMode mode); + + REGISTER_DERIVED_CLASS(TerrainCollider, Collider) + DECLARE_OBJECT_SERIALIZE(TerrainCollider) + + void ScaleChanged (){} + void FetchPoseFromTransform (); + + Vector3f GetCachedInvSize () { return m_CachedInvSize; } + + void SetTerrainData (PPtr<TerrainData> map); + TerrainData* GetTerrainData (); + + void CreateTrees (); + virtual void TransformChanged (int changeMask); + void TerrainChanged (int changeMask); + virtual void Create (const Rigidbody* ignoreAttachRigidbody); + virtual void Cleanup (); + static void InitializeClass (); + static void CleanupClass () {} + + bool HasShape() const { return m_Shape != NULL; } + virtual bool SupportsMaterial () const { return false; } +}; + +#endif
\ No newline at end of file diff --git a/Runtime/Dynamics/WheelCollider.cpp b/Runtime/Dynamics/WheelCollider.cpp new file mode 100644 index 0000000..a3cec0f --- /dev/null +++ b/Runtime/Dynamics/WheelCollider.cpp @@ -0,0 +1,464 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include "WheelCollider.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/BaseClasses/IsPlaying.h" + + +#define GET_SHAPE() static_cast<NxWheelShape*>(m_Shape) + + +const float kMinSize = 0.00001F; +const float kMinMass = 0.00001F; + +// we can't just reinterpret_cast one to another because NxTireFunctionDesc +// has a vtable (for apparently no good reason) +static inline void FrictionCurveToNovodexTireFunc( const WheelFrictionCurve& src, NxTireFunctionDesc& dst ) +{ + dst.extremumSlip = src.extremumSlip; + dst.extremumValue = src.extremumValue; + dst.asymptoteSlip = src.asymptoteSlip; + dst.asymptoteValue = src.asymptoteValue; + dst.stiffnessFactor = src.stiffnessFactor; +} +static inline void NovodexTireFuncToFrictionCurve( const NxTireFunctionDesc& src, WheelFrictionCurve& dst ) +{ + dst.extremumSlip = src.extremumSlip; + dst.extremumValue = src.extremumValue; + dst.asymptoteSlip = src.asymptoteSlip; + dst.asymptoteValue = src.asymptoteValue; + dst.stiffnessFactor = src.stiffnessFactor; +} + + +WheelCollider::WheelCollider(MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + // set default parameters for tire curves + NxTireFunctionDesc func; + NovodexTireFuncToFrictionCurve( func, m_SidewaysFriction ); + NovodexTireFuncToFrictionCurve( func, m_ForwardFriction ); +} + +WheelCollider::~WheelCollider () +{ +} + +void WheelCollider::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + if (m_Shape) + { + // Apply changed values + SetCenter (m_Center); + SetRadius (m_Radius); + SetSuspensionDistance (m_SuspensionDistance); + SetSuspensionSpring (m_SuspensionSpring); + SetForwardFriction(m_ForwardFriction); + SetSidewaysFriction(m_SidewaysFriction); + SetMass(m_Mass); + } + + Super::AwakeFromLoad (awakeMode); +} + +void WheelCollider::SmartReset() +{ + Super::SmartReset(); + AABB aabb; + if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb)) + { + SetCenter( aabb.GetCenter() ); + SetRadius( aabb.GetExtent ().y ); + SetSuspensionDistance( aabb.GetExtent ().y * 0.1f ); + } + else + { + SetCenter( Vector3f::zero ); + SetRadius( 1.0F ); + SetSuspensionDistance( 0.1f ); + } + + // setup default friction curves + WheelFrictionCurve curve; + curve.extremumSlip = 1.0f; + curve.extremumValue = 20000.0f; + curve.asymptoteSlip = 2.0f; + curve.asymptoteValue = 10000.0f; + curve.stiffnessFactor = 1.0f; + + SetForwardFriction( curve ); + SetSidewaysFriction( curve ); +} + +void WheelCollider::Reset() +{ + Super::Reset(); + + m_Center = Vector3f::zero; + m_Radius = 1.0F; + m_SuspensionDistance = 0.1F; + + m_SuspensionSpring.spring = 1.0f; + m_SuspensionSpring.damper = 0.0f; + m_SuspensionSpring.targetPosition = 0.0f; + m_Mass = 1.0F; + + // setup default friction curves + WheelFrictionCurve curve; + curve.extremumSlip = 1.0f; + curve.extremumValue = 20000.0f; + curve.asymptoteSlip = 2.0f; + curve.asymptoteValue = 10000.0f; + curve.stiffnessFactor = 1.0f; + + m_ForwardFriction = curve; + m_SidewaysFriction = curve; +} + +Vector3f WheelCollider::GetGlobalCenter() const +{ + return GetComponent(Transform).TransformPoint( m_Center ); +} + +float WheelCollider::GetGlobalRadius() const +{ + Vector3f scale = GetComponent(Transform).GetWorldScaleLossy(); + return std::max( Abs(m_Radius*scale.y), kMinSize ); +} + +float WheelCollider::GetGlobalSuspensionDistance() const +{ + Vector3f scale = GetComponent(Transform).GetWorldScaleLossy(); + return std::max( Abs(m_SuspensionDistance*scale.y), kMinSize ); +} + +void WheelCollider::Create(const Rigidbody* ignoreAttachRigidbody) +{ + if( m_Shape ) + Cleanup(); + + NxWheelShapeDesc shapeDesc; + shapeDesc.radius = GetGlobalRadius(); + shapeDesc.suspensionTravel = GetGlobalSuspensionDistance(); + shapeDesc.suspension = (NxSpringDesc&)m_SuspensionSpring; + shapeDesc.inverseWheelMass = 1.0F / std::max( m_Mass, kMinMass ); + shapeDesc.wheelFlags = NX_WF_INPUT_LNG_SLIPVELOCITY | NX_WF_INPUT_LAT_SLIPVELOCITY; + shapeDesc.materialIndex = 2; // use default wheel material setup in CreateScene + FrictionCurveToNovodexTireFunc( m_ForwardFriction, shapeDesc.longitudalTireForceFunction ); + FrictionCurveToNovodexTireFunc( m_SidewaysFriction, shapeDesc.lateralTireForceFunction ); + + m_IsTrigger = false; // TBD: make something nicer! + + Rigidbody* body = FindNewAttachedRigidbody (ignoreAttachRigidbody); + // WheelCollider requires a Rigidbody, or PhysX will give an error. + if (body != NULL) + FinalizeCreate( shapeDesc, false, ignoreAttachRigidbody ); + // Don't show error in Editor, since during setup people may create WheelColliders on empty GameObjects, + // and later move to a Rigidbody. We don't want to spam the console with errors in that case. + else +#if UNITY_EDITOR + if (IsWorldPlaying()) +#endif + ErrorStringObject("WheelCollider requires an attached Rigidbody to function.", this); +} + +void WheelCollider::Cleanup() +{ + Super::Cleanup(); +} + +void WheelCollider::SetCenter( const Vector3f& center ) +{ + if (m_Center != center) + { + m_Center = center; + SetDirty(); + } + + if( GET_SHAPE () ) + { + TransformChanged( Transform::kRotationChanged | Transform::kPositionChanged ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + RefreshPhysicsInEditMode(); + } +} + +void WheelCollider::SetRadius( float f ) +{ + if (m_Radius != f) + { + SetDirty(); + m_Radius = f; + } + + PROFILE_MODIFY_STATIC_COLLIDER + + if( GET_SHAPE () ) + { + GET_SHAPE()->setRadius( GetGlobalRadius() ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + RefreshPhysicsInEditMode(); + } +} + +void WheelCollider::SetSuspensionDistance( float f ) +{ + if (m_SuspensionDistance != f) + { + SetDirty(); + m_SuspensionDistance = f; + } + + if( GET_SHAPE () ) + { + GET_SHAPE()->setSuspensionTravel( GetGlobalSuspensionDistance() ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +void WheelCollider::SetSuspensionSpring( const JointSpring& spring ) +{ + if (m_SuspensionSpring != spring) + { + SetDirty(); + m_SuspensionSpring = spring; + } + + if( GET_SHAPE () ) + { + GET_SHAPE()->setSuspension( (NxSpringDesc&)spring ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +void WheelCollider::SetSidewaysFriction( const WheelFrictionCurve& curve ) +{ + if (m_SidewaysFriction != curve) + { + SetDirty(); + m_SidewaysFriction = curve; + } + + if( GET_SHAPE () ) + { + NxTireFunctionDesc func; + FrictionCurveToNovodexTireFunc( curve, func ); + GET_SHAPE()->setLateralTireForceFunction( func ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +void WheelCollider::SetForwardFriction( const WheelFrictionCurve& curve ) +{ + if (m_ForwardFriction != curve) + { + SetDirty(); + m_ForwardFriction = curve; + } + + if( GET_SHAPE () ) + { + NxTireFunctionDesc func; + FrictionCurveToNovodexTireFunc( curve, func ); + GET_SHAPE()->setLongitudalTireForceFunction( func ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +void WheelCollider::SetMass( float f ) +{ + if (m_Mass != f) + { + SetDirty(); + m_Mass = std::max( f, kMinMass ); + } + + if( GET_SHAPE() ) + { + GET_SHAPE()->setInverseWheelMass( 1.0F / m_Mass ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +void WheelCollider::SetMotorTorque( float f ) +{ + if( m_Shape ) + { + GET_SHAPE()->setMotorTorque( f ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +float WheelCollider::GetMotorTorque() const +{ + if( m_Shape ) + return GET_SHAPE()->getMotorTorque(); + else + return 0.0F; +} + +void WheelCollider::SetBrakeTorque( float f ) +{ + if( m_Shape ) + { + GET_SHAPE()->setBrakeTorque( f ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +float WheelCollider::GetBrakeTorque() const +{ + if( m_Shape ) + return GET_SHAPE()->getBrakeTorque(); + else + return 0.0F; +} + +void WheelCollider::SetSteerAngle( float f ) +{ + if( m_Shape ) + { + GET_SHAPE()->setSteerAngle( Deg2Rad(f) ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + } +} + +float WheelCollider::GetSteerAngle() const +{ + if( m_Shape ) + return Rad2Deg( GET_SHAPE()->getSteerAngle() ); + else + return 0.0F; +} + +bool WheelCollider::IsGrounded() const +{ + if( m_Shape ) + { + NxWheelContactData contactData; + NxShape* otherShape = GET_SHAPE()->getContact( contactData ); + return ( otherShape != NULL ); + } + else + { + return false; + } +} + +bool WheelCollider::GetGroundHit( WheelHit& hit ) const +{ + if( m_Shape ) + { + NxWheelContactData contactData; + NxShape* otherShape = GET_SHAPE()->getContact( contactData ); + if( otherShape != NULL ) + { + hit.point = reinterpret_cast<Vector3f&>( contactData.contactPoint ); + hit.normal = reinterpret_cast<Vector3f&>( contactData.contactNormal ); + hit.forwardDir = reinterpret_cast<Vector3f&>( contactData.longitudalDirection ); + hit.sidewaysDir = reinterpret_cast<Vector3f&>( contactData.lateralDirection ); + hit.force = contactData.contactForce; + hit.forwardSlip = contactData.longitudalSlip; + hit.sidewaysSlip = contactData.lateralSlip; + hit.collider = reinterpret_cast<Collider*>( otherShape->userData ); + return true; + } + return false; + } + else + { + return false; + } +} + +float WheelCollider::GetRpm() const +{ + if( m_Shape ) + return GET_SHAPE()->getAxleSpeed() / (kPI*2.0) * 60.0; + else + return 0.0F; +} + + +void WheelCollider::ScaleChanged() +{ + NxWheelShape* shape = GET_SHAPE(); + shape->setRadius( GetGlobalRadius() ); + shape->setSuspensionTravel( GetGlobalSuspensionDistance() ); + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + RefreshPhysicsInEditMode(); +} + +void WheelCollider::FetchPoseFromTransform() +{ + FetchPoseFromTransformUtility( m_Center ); +} + +bool WheelCollider::GetRelativeToParentPositionAndRotation( Transform& transform, Transform& anyParent, Matrix4x4f& matrix ) +{ + return GetRelativeToParentPositionAndRotationUtility( transform, anyParent, m_Center, matrix ); +} + +void WheelCollider::TransformChanged( int 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( changeMask & Transform::kScaleChanged ) + ScaleChanged(); + + m_Shape->getActor().wakeUp(); // wake actor up in case it was sleeping + RefreshPhysicsInEditMode(); + } +} + +void WheelCollider::InitializeClass () +{ + REGISTER_MESSAGE( WheelCollider, kTransformChanged, TransformChanged, int ); +} + +template<class TransferFunction> +void WheelCollider::Transfer (TransferFunction& transfer) +{ + // NOTE: bypass Collider transfer, so that material and trigger flag are not + // serialized. + + Super::Super::Transfer( transfer ); + TRANSFER( m_Center ); + TRANSFER_SIMPLE( m_Radius ); + TRANSFER_SIMPLE( m_SuspensionDistance ); + TRANSFER_SIMPLE( m_SuspensionSpring ); + TRANSFER_SIMPLE( m_Mass ); + + TRANSFER( m_ForwardFriction ); + TRANSFER( m_SidewaysFriction ); + + // Since we bypass Collider transfer, we need to transfer m_Enabled ourselves + transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask | kEditorDisplaysCheckBoxMask); + transfer.Align(); +} + +IMPLEMENT_CLASS_HAS_INIT( WheelCollider ) +IMPLEMENT_OBJECT_SERIALIZE( WheelCollider ) +#endif //ENABLE_PHYSICS
\ No newline at end of file diff --git a/Runtime/Dynamics/WheelCollider.h b/Runtime/Dynamics/WheelCollider.h new file mode 100644 index 0000000..53ddcaf --- /dev/null +++ b/Runtime/Dynamics/WheelCollider.h @@ -0,0 +1,142 @@ +#ifndef WHEELCOLLIDER_H +#define WHEELCOLLIDER_H + +#include "Collider.h" +#include "Runtime/Math/Vector3.h" +#include "JointDescriptions.h" + +class NxMaterial; + +// ---------------------------------------------------------------------- + +struct WheelFrictionCurve +{ + float extremumSlip; ///<Extremum Slip. range { 0.001, infinity } + float extremumValue; ///<Extremum Value. range { 0.001, infinity } + float asymptoteSlip; ///<Asymptote Slip. range { 0.001, infinity } + float asymptoteValue; ///<Asymptote Value. range { 0.001, infinity } + float stiffnessFactor; ///<Stiffness Factor. range { 0, infinity } + + bool operator != (const WheelFrictionCurve& c)const + { + return extremumSlip != c.extremumSlip + || extremumValue != c.extremumValue + || asymptoteSlip != c.asymptoteSlip + || asymptoteValue != c.asymptoteValue + || stiffnessFactor != c.stiffnessFactor + ; + } + + DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (WheelFrictionCurve) +}; + + +template<class TransferFunction> +void WheelFrictionCurve::Transfer (TransferFunction& transfer) +{ + TRANSFER_SIMPLE( extremumSlip ); + TRANSFER_SIMPLE( extremumValue ); + TRANSFER_SIMPLE( asymptoteSlip ); + TRANSFER_SIMPLE( asymptoteValue ); + TRANSFER_SIMPLE( stiffnessFactor ); +} + +// ---------------------------------------------------------------------- + +struct WheelHit +{ + Vector3f point; + Vector3f normal; + Vector3f forwardDir; + Vector3f sidewaysDir; + float force; + float forwardSlip; + float sidewaysSlip; + Collider* collider; +}; + + +// ---------------------------------------------------------------------- + + +class WheelCollider : public Collider +{ +public: + REGISTER_DERIVED_CLASS (WheelCollider, Collider) + DECLARE_OBJECT_SERIALIZE (WheelCollider) + + WheelCollider(MemLabelId label, ObjectCreationMode mode); + + virtual void Reset(); + virtual void SmartReset(); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + Vector3f GetCenter() const { return m_Center; } + void SetCenter( const Vector3f& center ); + + float GetRadius() const { return m_Radius; } + void SetRadius( float f ); + + float GetSuspensionDistance() const { return m_SuspensionDistance; } + void SetSuspensionDistance( float f ); + + void SetSuspensionSpring( const JointSpring& spring ); + const JointSpring& GetSuspensionSpring() const { return m_SuspensionSpring; } + + void SetForwardFriction( const WheelFrictionCurve& curve ); + const WheelFrictionCurve& GetForwardFriction() const { return m_ForwardFriction; } + + void SetSidewaysFriction( const WheelFrictionCurve& curve ); + const WheelFrictionCurve& GetSidewaysFriction() const { return m_SidewaysFriction; } + + float GetMass() const { return m_Mass; } + void SetMass( float f ); + + float GetMotorTorque() const; + void SetMotorTorque( float f ); + + float GetBrakeTorque() const; + void SetBrakeTorque( float f ); + + float GetSteerAngle() const; + void SetSteerAngle( float f ); + + bool IsGrounded() const; + bool GetGroundHit( WheelHit& hit ) const; + + float GetRpm() const; + + void TransformChanged( int changeMask ); + + static void InitializeClass(); + static void CleanupClass() { } + + Vector3f GetGlobalCenter() const; + float GetGlobalRadius() const; + float GetGlobalSuspensionDistance() const; + Matrix4x4f CalculateTransform() const; + + virtual bool SupportsMaterial () const { return false; } + +protected: + virtual void FetchPoseFromTransform(); + virtual bool GetRelativeToParentPositionAndRotation( Transform& transform, Transform& anyParent, Matrix4x4f& matrix ); + + virtual void Create(const Rigidbody* ignoreRigidbody); + virtual void ScaleChanged(); + virtual void Cleanup(); + +private: + Vector3f m_Center; + JointSpring m_SuspensionSpring; + WheelFrictionCurve m_ForwardFriction; + WheelFrictionCurve m_SidewaysFriction; + float m_Radius; ///< range { 0, infinity } + float m_SuspensionDistance; ///< range { 0, infinity } + float m_Mass; ///< range { 0.0001, infinity } + + // the wheel material is shared by all instances + static NxMaterial* m_WheelMaterial; +}; + +#endif diff --git a/Runtime/Dynamics/nxmemorystream.cpp b/Runtime/Dynamics/nxmemorystream.cpp new file mode 100644 index 0000000..eab492f --- /dev/null +++ b/Runtime/Dynamics/nxmemorystream.cpp @@ -0,0 +1,134 @@ +#include "UnityPrefix.h" +#if ENABLE_PHYSICS +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include "nxmemorystream.h" +#include "Runtime/Allocator/MemoryMacros.h" + + +#define BLOCKSIZE 4096 + +MemoryStream::MemoryStream(NxU8* data,NxU32 len) +{ + if ( data ) + { + mBuffer = data; + mMyAlloc = false; + } + else + { + mBuffer = (NxU8 *) UNITY_MALLOC(kMemFile, sizeof(NxU8)*len); + mMyAlloc = true; + } + + mReadLoc = 0; + mWriteLoc = 0; + mLen = len; +} + +MemoryStream::~MemoryStream(void) +{ + if ( mMyAlloc ) + { + UNITY_FREE(kMemFile, mBuffer); + } +} + +NxU8 MemoryStream::readByte(void) const +{ + NxU8 ret = 0; + readBuffer(&ret, sizeof(NxU8) ); + return ret; +} + +NxU16 MemoryStream::readWord(void) const +{ + NxU16 ret = 0; + readBuffer(&ret,sizeof(NxU16)); + return ret; +} + +NxU32 MemoryStream::readDword(void) const +{ + NxU32 ret = 0; + readBuffer(&ret,sizeof(NxU32)); + return ret; +} + +float MemoryStream::readFloat(void) const +{ + float ret = 0; + readBuffer(&ret,sizeof(float)); + return ret; +} + +double MemoryStream::readDouble(void) const +{ + double ret = 0; + readBuffer(&ret,sizeof(double)); + return ret; +} + +void MemoryStream::readBuffer(void* buffer, NxU32 size) const +{ + if ( (mReadLoc+size) <= mLen ) + { + memcpy( buffer, &mBuffer[mReadLoc], size ); + mReadLoc+=size; + } +} + +NxStream& MemoryStream::storeByte(NxU8 b) +{ + storeBuffer(&b, sizeof(NxU8) ); + return *this; +} + +NxStream& MemoryStream::storeWord(NxU16 w) +{ + storeBuffer(&w,sizeof(NxU16)); + return *this; +} + +NxStream& MemoryStream::storeDword(NxU32 d) +{ + storeBuffer(&d,sizeof(NxU32)); + return *this; +} + +NxStream& MemoryStream::storeFloat(NxReal f) +{ + storeBuffer(&f,sizeof(NxReal)); + return *this; +} + +NxStream& MemoryStream::storeDouble(NxF64 f) +{ + storeBuffer(&f,sizeof(NxF64)); + return *this; +} + +NxStream& MemoryStream::storeBuffer(const void* buffer, NxU32 size) +{ + + if ( (mWriteLoc+size) >= mLen ) + { + unsigned int blocksize = BLOCKSIZE; + + if ( size > BLOCKSIZE ) blocksize = size*2; + + NxU8 *buf = (NxU8 *)UNITY_MALLOC(kMemFile, mLen+blocksize); + memcpy(buf,mBuffer,mWriteLoc); + UNITY_FREE(kMemFile, mBuffer); + mBuffer = buf; + mLen+=blocksize; + + } + memcpy(&mBuffer[mWriteLoc], buffer, size ); + mWriteLoc+=size; + + return *this; +} + +#endif //ENABLE_PHYSICS
\ No newline at end of file diff --git a/Runtime/Dynamics/nxmemorystream.h b/Runtime/Dynamics/nxmemorystream.h new file mode 100644 index 0000000..80f8563 --- /dev/null +++ b/Runtime/Dynamics/nxmemorystream.h @@ -0,0 +1,34 @@ +#ifndef MEMORY_STREAM_H +#define MEMORY_STREAM_H + +#include "External/PhysX/builds/SDKs/Physics/include/NxPhysics.h" +#include "External/PhysX/builds/SDKs/Foundation/include/NxStream.h" + +class MemoryStream : public NxStream + { + public: + MemoryStream(NxU8* data,NxU32 len); + virtual ~MemoryStream(); + + virtual NxU8 readByte() const; + virtual NxU16 readWord() const; + virtual NxU32 readDword() const; + virtual float readFloat() const; + virtual double readDouble() const; + virtual void readBuffer(void* buffer, NxU32 size) const; + + virtual NxStream& storeByte(NxU8 b); + virtual NxStream& storeWord(NxU16 w); + virtual NxStream& storeDword(NxU32 d); + virtual NxStream& storeFloat(NxReal f); + virtual NxStream& storeDouble(NxF64 f); + virtual NxStream& storeBuffer(const void* buffer, NxU32 size); + + bool mMyAlloc; + mutable NxU32 mReadLoc; + NxU32 mWriteLoc; + NxU32 mLen; + NxU8* mBuffer; // buffer + }; + +#endif |