summaryrefslogtreecommitdiff
path: root/Runtime/Dynamics
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Dynamics
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Dynamics')
-rw-r--r--Runtime/Dynamics/BoxCollider.cpp256
-rw-r--r--Runtime/Dynamics/BoxCollider.h45
-rw-r--r--Runtime/Dynamics/CapsuleCollider.cpp369
-rw-r--r--Runtime/Dynamics/CapsuleCollider.h59
-rw-r--r--Runtime/Dynamics/CharacterController.cpp567
-rw-r--r--Runtime/Dynamics/CharacterController.h112
-rw-r--r--Runtime/Dynamics/CharacterJoint.cpp340
-rw-r--r--Runtime/Dynamics/CharacterJoint.h83
-rw-r--r--Runtime/Dynamics/Cloth.cpp402
-rw-r--r--Runtime/Dynamics/Cloth.h101
-rw-r--r--Runtime/Dynamics/ClothRenderer.cpp273
-rw-r--r--Runtime/Dynamics/ClothRenderer.h54
-rw-r--r--Runtime/Dynamics/Collider.cpp598
-rw-r--r--Runtime/Dynamics/Collider.h168
-rw-r--r--Runtime/Dynamics/CollisionMeshData.cpp121
-rw-r--r--Runtime/Dynamics/CollisionMeshData.h87
-rw-r--r--Runtime/Dynamics/ConfigurableJoint.cpp479
-rw-r--r--Runtime/Dynamics/ConfigurableJoint.h180
-rw-r--r--Runtime/Dynamics/ConstantForce.cpp66
-rw-r--r--Runtime/Dynamics/ConstantForce.h30
-rw-r--r--Runtime/Dynamics/DeformableMesh.cpp721
-rw-r--r--Runtime/Dynamics/DeformableMesh.h146
-rw-r--r--Runtime/Dynamics/ExtractDataFromMesh.cpp56
-rw-r--r--Runtime/Dynamics/ExtractDataFromMesh.h8
-rw-r--r--Runtime/Dynamics/FixedJoint.cpp61
-rw-r--r--Runtime/Dynamics/FixedJoint.h32
-rw-r--r--Runtime/Dynamics/HingeJoint.cpp280
-rw-r--r--Runtime/Dynamics/HingeJoint.h69
-rw-r--r--Runtime/Dynamics/Joint.cpp325
-rw-r--r--Runtime/Dynamics/Joint.h148
-rw-r--r--Runtime/Dynamics/JointDescriptions.h215
-rw-r--r--Runtime/Dynamics/Joints.h6
-rw-r--r--Runtime/Dynamics/MeshCollider.cpp326
-rw-r--r--Runtime/Dynamics/MeshCollider.h63
-rw-r--r--Runtime/Dynamics/NxMeshCreation.cpp115
-rw-r--r--Runtime/Dynamics/NxMeshCreation.h11
-rw-r--r--Runtime/Dynamics/NxWrapperUtility.h14
-rw-r--r--Runtime/Dynamics/PhysXRaycast.cpp195
-rw-r--r--Runtime/Dynamics/PhysXRaycast.h42
-rw-r--r--Runtime/Dynamics/PhysicMaterial.cpp340
-rw-r--r--Runtime/Dynamics/PhysicMaterial.h99
-rw-r--r--Runtime/Dynamics/PhysicsManager.cpp1508
-rw-r--r--Runtime/Dynamics/PhysicsManager.h235
-rw-r--r--Runtime/Dynamics/PhysicsModule.cpp141
-rw-r--r--Runtime/Dynamics/PhysicsModule.h3
-rw-r--r--Runtime/Dynamics/PhysicsModule.jam108
-rw-r--r--Runtime/Dynamics/PhysicsModuleRegistration.cpp67
-rw-r--r--Runtime/Dynamics/PhysicsTest.cpp93
-rw-r--r--Runtime/Dynamics/PrimitiveCollider.h7
-rw-r--r--Runtime/Dynamics/RaycastCollider.cpp212
-rw-r--r--Runtime/Dynamics/RaycastCollider.h49
-rw-r--r--Runtime/Dynamics/RaycastHit.cpp48
-rw-r--r--Runtime/Dynamics/RaycastHit.h6
-rw-r--r--Runtime/Dynamics/RigidBody.h266
-rw-r--r--Runtime/Dynamics/Rigidbody.cpp1097
-rw-r--r--Runtime/Dynamics/ScriptBindings/NewDynamics.txt1797
-rw-r--r--Runtime/Dynamics/SkinnedCloth.cpp347
-rw-r--r--Runtime/Dynamics/SkinnedCloth.h82
-rw-r--r--Runtime/Dynamics/SphereCollider.cpp272
-rw-r--r--Runtime/Dynamics/SphereCollider.h56
-rw-r--r--Runtime/Dynamics/SpringJoint.cpp161
-rw-r--r--Runtime/Dynamics/SpringJoint.h51
-rw-r--r--Runtime/Dynamics/TerrainCollider.cpp238
-rw-r--r--Runtime/Dynamics/TerrainCollider.h46
-rw-r--r--Runtime/Dynamics/WheelCollider.cpp464
-rw-r--r--Runtime/Dynamics/WheelCollider.h142
-rw-r--r--Runtime/Dynamics/nxmemorystream.cpp134
-rw-r--r--Runtime/Dynamics/nxmemorystream.h34
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