summaryrefslogtreecommitdiff
path: root/Runtime/Physics2D
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Physics2D')
-rw-r--r--Runtime/Physics2D/BoxCollider2D.cpp167
-rw-r--r--Runtime/Physics2D/BoxCollider2D.h38
-rw-r--r--Runtime/Physics2D/CircleCollider2D.cpp157
-rw-r--r--Runtime/Physics2D/CircleCollider2D.h39
-rw-r--r--Runtime/Physics2D/Collider2D.cpp441
-rw-r--r--Runtime/Physics2D/Collider2D.h90
-rw-r--r--Runtime/Physics2D/CollisionListener2D.cpp400
-rw-r--r--Runtime/Physics2D/CollisionListener2D.h123
-rw-r--r--Runtime/Physics2D/DistanceJoint2D.cpp161
-rw-r--r--Runtime/Physics2D/DistanceJoint2D.h46
-rw-r--r--Runtime/Physics2D/EdgeCollider2D.cpp176
-rw-r--r--Runtime/Physics2D/EdgeCollider2D.h45
-rw-r--r--Runtime/Physics2D/HingeJoint2D.cpp211
-rw-r--r--Runtime/Physics2D/HingeJoint2D.h61
-rw-r--r--Runtime/Physics2D/Joint2D.cpp201
-rw-r--r--Runtime/Physics2D/Joint2D.h59
-rw-r--r--Runtime/Physics2D/JointDescriptions2D.h101
-rw-r--r--Runtime/Physics2D/Physics2DManager.cpp1228
-rw-r--r--Runtime/Physics2D/Physics2DManager.h113
-rw-r--r--Runtime/Physics2D/Physics2DMaterial.cpp65
-rw-r--r--Runtime/Physics2D/Physics2DMaterial.h36
-rw-r--r--Runtime/Physics2D/Physics2DModule.jam178
-rw-r--r--Runtime/Physics2D/Physics2DModuleRegistration.cpp53
-rw-r--r--Runtime/Physics2D/Physics2DSettings.cpp167
-rw-r--r--Runtime/Physics2D/Physics2DSettings.h62
-rw-r--r--Runtime/Physics2D/PolygonCollider2D.cpp134
-rw-r--r--Runtime/Physics2D/PolygonCollider2D.h36
-rw-r--r--Runtime/Physics2D/PolygonColliderBase2D.cpp336
-rw-r--r--Runtime/Physics2D/PolygonColliderBase2D.h33
-rw-r--r--Runtime/Physics2D/RigidBody2D.h125
-rw-r--r--Runtime/Physics2D/Rigidbody2D.cpp700
-rw-r--r--Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt820
-rw-r--r--Runtime/Physics2D/SliderJoint2D.cpp231
-rw-r--r--Runtime/Physics2D/SliderJoint2D.h65
-rw-r--r--Runtime/Physics2D/SpringJoint2D.cpp161
-rw-r--r--Runtime/Physics2D/SpringJoint2D.h54
-rw-r--r--Runtime/Physics2D/SpriteCollider2D.cpp91
-rw-r--r--Runtime/Physics2D/SpriteCollider2D.h37
38 files changed, 7241 insertions, 0 deletions
diff --git a/Runtime/Physics2D/BoxCollider2D.cpp b/Runtime/Physics2D/BoxCollider2D.cpp
new file mode 100644
index 0000000..7b32134
--- /dev/null
+++ b/Runtime/Physics2D/BoxCollider2D.cpp
@@ -0,0 +1,167 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/BoxCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileBoxColliderCreate, "Physics2D.BoxColliderCreate", kProfilerPhysics)
+
+IMPLEMENT_CLASS (BoxCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (BoxCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+BoxCollider2D::BoxCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+BoxCollider2D::~BoxCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void BoxCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Size);
+ TRANSFER (m_Center);
+}
+
+
+void BoxCollider2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ if (IsFinite (m_Size))
+ {
+ m_Size.x = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, m_Size.x);
+ m_Size.y = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, m_Size.y);
+ }
+ else
+ {
+ m_Size.Set (1.0f, 1.0f);
+ }
+
+ if (!IsFinite (m_Center))
+ m_Center = Vector2f::zero;
+}
+
+
+void BoxCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Size.Set (1.0f, 1.0f);
+ m_Center = Vector2f::zero;
+}
+
+
+void BoxCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ m_Size.x = aabb.GetExtent().x * 2.0f;
+ m_Size.y = aabb.GetExtent().y * 2.0f;
+ m_Center.x = aabb.GetCenter().x;
+ m_Center.y = aabb.GetCenter().y;
+ return;
+ }
+
+ m_Size.Set (1.0f, 1.0f);
+ m_Center = Vector2f::zero;
+}
+
+
+void BoxCollider2D::SetSize (const Vector2f& size)
+{
+ ABORT_INVALID_VECTOR2 (size, size, BoxCollider2D);
+
+ // Finish if no change.
+ if (m_Size == size)
+ return;
+
+ m_Size.x = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, size.x);
+ m_Size.y = std::max (PHYSICS_2D_SMALL_RANGE_CLAMP, size.y);
+
+ SetDirty ();
+
+ Create();
+}
+
+
+void BoxCollider2D::SetCenter (const Vector2f& center)
+{
+ ABORT_INVALID_VECTOR2 (center, center, BoxCollider2D);
+
+ // Finish if no change.
+ if (m_Center == center)
+ return;
+
+ m_Center = center;
+ SetDirty ();
+
+ Create();
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void BoxCollider2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfileBoxColliderCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ // Calculate collider center.
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+ Vector3f center = relativeTransform.MultiplyPoint3 (Vector3f(m_Center.x * scale.x, m_Center.y * scale.y, 0.0f));
+
+ // Calculate rotation.
+ Quaternionf quat;
+ MatrixToQuaternion(relativeTransform, quat);
+ const Vector3f euler = QuaternionToEuler(quat);
+
+ // Calculate scaled size.
+ Vector3f size = GetComponent (Transform).GetWorldScaleLossy ();
+ size.x = max(Abs(size.x * m_Size.x), PHYSICS_2D_SMALL_RANGE_CLAMP);
+ size.y = max(Abs(size.y * m_Size.y), PHYSICS_2D_SMALL_RANGE_CLAMP);
+
+ // Create the shape.
+ b2PolygonShape shape;
+ shape.SetAsBox (size.x*0.5f, size.y*0.5f, b2Vec2(center.x, center.y), euler.z);
+ b2FixtureDef def;
+ def.shape = &shape;
+
+ // Finalize the creation.
+ FinalizeCreate (def, body);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/BoxCollider2D.h b/Runtime/Physics2D/BoxCollider2D.h
new file mode 100644
index 0000000..2c40727
--- /dev/null
+++ b/Runtime/Physics2D/BoxCollider2D.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Math/Vector2.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class BoxCollider2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_CLASS (BoxCollider2D, Collider2D)
+ DECLARE_OBJECT_SERIALIZE (BoxCollider2D)
+
+ BoxCollider2D (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ void SetSize (const Vector2f& size);
+ const Vector2f& GetSize () const { return m_Size; }
+
+ void SetCenter (const Vector2f& center);
+ const Vector2f& GetCenter() const { return m_Center; }
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ Vector2f m_Size; ///< The size of the box.
+ Vector2f m_Center; ///< The offset of the box.
+};
+
+#endif
diff --git a/Runtime/Physics2D/CircleCollider2D.cpp b/Runtime/Physics2D/CircleCollider2D.cpp
new file mode 100644
index 0000000..a34a94b
--- /dev/null
+++ b/Runtime/Physics2D/CircleCollider2D.cpp
@@ -0,0 +1,157 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/CircleCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileCircleColliderCreate, "Physics2D.CircleColliderCreate", kProfilerPhysics)
+
+
+IMPLEMENT_CLASS (CircleCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (CircleCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+CircleCollider2D::CircleCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+CircleCollider2D::~CircleCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void CircleCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Radius);
+ TRANSFER (m_Center);
+}
+
+
+void CircleCollider2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Radius = clamp<float> (m_Radius, PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (!IsFinite (m_Center))
+ m_Center = Vector2f::zero;
+}
+
+
+void CircleCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Radius = 0.5f;
+ m_Center = Vector2f::zero;
+}
+
+
+void CircleCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f dist = aabb.GetExtent ();
+ m_Radius = clamp<float> (std::max(dist.x, dist.y), PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_Center.x = aabb.GetCenter().x;
+ m_Center.y = aabb.GetCenter().y;
+ return;
+ }
+
+ m_Radius = 0.5f;
+ m_Center = Vector2f::zero;
+}
+
+
+void CircleCollider2D::SetRadius (float radius)
+{
+ ABORT_INVALID_FLOAT (radius, radius, CircleCollider2D);
+
+ // Finish if no change.
+ if (m_Radius == radius)
+ return;
+
+ m_Radius = clamp<float> (radius, PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (GetShape() == NULL)
+ return;
+
+ SetDirty ();
+ Create();
+}
+
+
+void CircleCollider2D::SetCenter (const Vector2f& center)
+{
+ ABORT_INVALID_VECTOR2 (center, center, CircleCollider2D);
+
+ // Finish if no change.
+ if (m_Center == center)
+ return;
+
+ m_Center = center;
+
+ SetDirty ();
+ Create();
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void CircleCollider2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfileCircleColliderCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ // Fetch scale.
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+
+ // Calculate collider center.
+ Vector3f center = relativeTransform.MultiplyPoint3(Vector3f(m_Center.x * scale.x, m_Center.y * scale.y, 0.0f));
+
+ // Calculate scaled radius.
+ const float scaledRadius = clamp<float> (max( PHYSICS_2D_SMALL_RANGE_CLAMP, max (Abs (scale.x), Abs (scale.y)) * m_Radius), PHYSICS_2D_SMALL_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ // Create the shape.
+ b2CircleShape shape;
+ shape.m_p.Set(center.x, center.y);
+ shape.m_radius = scaledRadius;
+ b2FixtureDef def;
+ def.shape = &shape;
+
+ // Finalize the creation.
+ FinalizeCreate (def, body);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/CircleCollider2D.h b/Runtime/Physics2D/CircleCollider2D.h
new file mode 100644
index 0000000..f30b733
--- /dev/null
+++ b/Runtime/Physics2D/CircleCollider2D.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Math/Vector2.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class CircleCollider2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_CLASS (CircleCollider2D, Collider2D)
+ DECLARE_OBJECT_SERIALIZE (CircleCollider2D)
+
+ CircleCollider2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~CircleCollider2D (); declared-by-macro
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ void SetRadius (float radius);
+ float GetRadius () const { return m_Radius; }
+
+ void SetCenter (const Vector2f& center);
+ const Vector2f& GetCenter() const { return m_Center; }
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ float m_Radius; ///< The radius of the circle. range { 0.0001, 1000000 }
+ Vector2f m_Center; ///< The offset of the circle.
+};
+
+#endif
diff --git a/Runtime/Physics2D/Collider2D.cpp b/Runtime/Physics2D/Collider2D.cpp
new file mode 100644
index 0000000..9e34e60
--- /dev/null
+++ b/Runtime/Physics2D/Collider2D.cpp
@@ -0,0 +1,441 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Utilities/Utility.h"
+
+
+PROFILER_INFORMATION(gPhysics2DProfileColliderCleanup, "Physics2D.ColliderCleanup", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderShapeGeneration, "Physics2D.ColliderShapeGeneration", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformChanged, "Physics2D.ColliderTransformChanged", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformParentChanged, "Physics2D.ColliderTransformParentChanged", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformScaleChanged, "Physics2D.ColliderTransformScaleChanged", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileColliderTransformPositionRotationChanged, "Physics2D.ColliderTransformPositionRotationChanged", kProfilerPhysics)
+
+
+IMPLEMENT_CLASS_HAS_INIT (Collider2D)
+IMPLEMENT_OBJECT_SERIALIZE (Collider2D)
+INSTANTIATE_TEMPLATE_TRANSFER (Collider2D)
+
+// --------------------------------------------------------------------------
+
+
+Collider2D::Collider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_IsTrigger(false)
+, m_IsStaticBody(false)
+, m_LocalTransformInitialized(false)
+{
+}
+
+
+Collider2D::~Collider2D ()
+{
+ Cleanup ();
+}
+
+
+void Collider2D::InitializeClass ()
+{
+ REGISTER_MESSAGE (Collider2D, kTransformChanged, TransformChanged, int);
+}
+
+
+template<class TransferFunction>
+void Collider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER(m_Material);
+ transfer.Transfer (m_IsTrigger, "m_IsTrigger");
+ transfer.Align();
+}
+
+
+void Collider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Material = 0;
+ m_IsTrigger = false;
+}
+
+
+void Collider2D::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ // (Re)create collider if appropriate.
+ Create ();
+}
+
+
+void Collider2D::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate (operation);
+ Cleanup ();
+
+ // Destroy collider collisions immediately.
+ GetPhysics2DManager().DestroyColliderCollisions (this);
+}
+
+
+void Collider2D::Cleanup ()
+{
+ PROFILER_AUTO(gPhysics2DProfileColliderCleanup, NULL)
+
+ // Process any shapes.
+ if (m_Shapes.size() > 0)
+ {
+ if (m_IsStaticBody)
+ {
+ // Destroy the fixtures
+ b2Body* groundBody = GetPhysicsGroundBody ();
+ for (int i = 0; i < m_Shapes.size(); ++i)
+ groundBody->DestroyFixture(m_Shapes[i]);
+ }
+ else
+ {
+ Rigidbody2D* rigidBody = GetRigidbody();
+ if (rigidBody)
+ {
+ // Destroy the fixtures.
+ b2Body* body = rigidBody->GetBody ();
+ for (int i = 0; i < m_Shapes.size(); ++i)
+ body->DestroyFixture(m_Shapes[i]);
+
+ // Recalculate the collider body-mass.
+ rigidBody->CalculateColliderBodyMass ();
+ }
+ }
+
+ m_Shapes.clear();
+
+ // Invalidate any persisted contact information.
+ GetPhysics2DManager().InvalidateColliderCollisions (this);
+ }
+
+ m_IsStaticBody = false;
+}
+
+
+void Collider2D::RecreateCollider (const Rigidbody2D* ignoreRigidbody)
+{
+ if (IsActive () && GetEnabled())
+ Create (ignoreRigidbody);
+ else
+ Cleanup ();
+}
+
+
+void Collider2D::AddToManager ()
+{
+ // Create the collider if not already created.
+ if (m_Shapes.size() == 0)
+ Create ();
+}
+
+
+void Collider2D::RemoveFromManager ()
+{
+ // Destroy the collider.
+ Cleanup ();
+}
+
+
+void Collider2D::SetIsTrigger (bool trigger)
+{
+ // Finish if no change.
+ if (m_IsTrigger == trigger)
+ return;
+
+ m_IsTrigger = trigger;
+
+ SetDirty();
+
+ if (IsActive () && GetEnabled())
+ Create ();
+}
+
+
+PPtr<PhysicsMaterial2D> Collider2D::GetMaterial ()
+{
+ return m_Material;
+}
+
+
+void Collider2D::SetMaterial (PPtr<PhysicsMaterial2D> material)
+{
+ if (m_Material == material)
+ return;
+
+ SetDirty ();
+ m_Material = material;
+}
+
+
+Rigidbody2D* Collider2D::GetRigidbody ()
+{
+ // Finish if a static body or no shapes.
+ if (m_IsStaticBody || m_Shapes.size() == 0)
+ return NULL;
+
+ // Fetch body from first shape.
+ b2Body* body = m_Shapes[0]->GetBody();
+
+ if (!body)
+ return NULL;
+
+ return (Rigidbody2D*)body->GetUserData();
+}
+
+
+bool Collider2D::OverlapPoint (const Vector2f& point) const
+{
+ // Cannot perform query if not active.
+ if (!IsActive ())
+ return false;
+
+ const b2Vec2 testPoint(point.x, point.y);
+
+ // Test all fixtures for the point.
+ for (FixtureArray::const_iterator fixtureItr = m_Shapes.begin (); fixtureItr != m_Shapes.end (); ++fixtureItr)
+ {
+ if ((*fixtureItr)->TestPoint (testPoint))
+ return true;
+ }
+
+ return false;
+}
+
+
+void Collider2D::TransformChanged (int changeMask)
+{
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformChanged, NULL)
+
+ // Finish if transform message is disabled.
+ if (!GetPhysics2DManager().IsTransformMessageEnabled())
+ return;
+
+ // Recreate the collider if the parent body has changed.
+ if (changeMask & Transform::kParentingChanged)
+ {
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformParentChanged, NULL)
+
+ Rigidbody2D* currentBody = GetRigidbody();
+ Rigidbody2D* newBody = Rigidbody2D::FindRigidbody (GetGameObjectPtr());
+ if (newBody != currentBody)
+ {
+ Create();
+ return;
+ }
+ }
+
+ // Recreate the collider if the scale has changed.
+ if (changeMask & Transform::kScaleChanged)
+ {
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformScaleChanged, NULL)
+
+ Create();
+ return;
+ }
+
+ // Recreate the collider if the position or rotation has changed.
+ if (changeMask & (Transform::kPositionChanged | Transform::kRotationChanged))
+ {
+ PROFILER_AUTO(gPhysics2DProfileColliderTransformPositionRotationChanged, NULL)
+
+ // Always recreate static bodies but only recreate non-static bodies if the rigid-body exists
+ // on a parent GameObject i.e. this is a compound object.
+ if (!m_IsStaticBody && (QueryComponent (Rigidbody2D) != NULL || !HasLocalTransformChanged ()))
+ return;
+
+ Create();
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void Collider2D::FinalizeCreate(b2FixtureDef& def, b2Body* body, const dynamic_array<b2Shape*>* shapes)
+{
+ Assert (m_Shapes.size() == 0);
+
+ PROFILER_AUTO(gPhysics2DProfileColliderShapeGeneration, NULL)
+
+ // Don't create the collider if not active and enabled.
+ if (!(IsActive () && GetEnabled ()))
+ return;
+
+ // Initialize fixture definition.
+ if (m_Material.IsNull ())
+ {
+ PhysicsMaterial2D* defaultMaterial = GetPhysics2DSettings ().GetDefaultPhysicsMaterial ();
+ if (defaultMaterial != NULL )
+ {
+ def.friction = defaultMaterial->GetFriction ();
+ def.restitution = defaultMaterial->GetBounciness ();
+ }
+ else
+ {
+ def.friction = 0.4f;
+ def.restitution = 0.0f;
+ }
+ }
+ else
+ {
+ def.friction = m_Material->GetFriction();
+ def.restitution = m_Material->GetBounciness();
+ }
+ def.density = 1.0f; // Body mass is calculated based upon a mass calculation of each shape unit-density.
+ def.userData = this;
+ def.isSensor = m_IsTrigger;
+
+ // Fetch the associated rigid-body.
+ // NOTE: There won't be one if the body is the ground-body!
+ Rigidbody2D* rigidBody = (Rigidbody2D*)body->GetUserData ();
+
+ // Do we have any shapes?
+ if (shapes)
+ {
+ // Yes, so add shape to existing set.
+ const int shapeCount = shapes->size();
+
+ Assert(shapeCount > 0);
+ Assert(def.shape == 0);
+
+ m_Shapes.resize_uninitialized(shapeCount);
+ for (int i = 0; i < shapeCount; ++i)
+ {
+ b2FixtureDef prototype = def;
+ prototype.shape = (*shapes)[i];
+ m_Shapes[i] = body->CreateFixture(&prototype);
+ }
+ }
+ else
+ {
+ // No, so add new shape to set.
+ m_Shapes.resize_uninitialized(1);
+ m_Shapes[0] = body->CreateFixture(&def);
+ }
+
+ // Calculate the collider body mass.
+ if (rigidBody != NULL)
+ rigidBody->CalculateColliderBodyMass ();
+
+ // Update the local transform.
+ UpdateLocalTransform ();
+}
+
+
+void Collider2D::CalculateColliderTransformation (const Rigidbody2D* ignoreRigidbody, b2Body** attachedBody, Matrix4x4f& matrix)
+{
+ // Fetch the transform.
+ const Transform& transform = GetComponent(Transform);
+
+ // Do we have a rigid-body on the same game object?
+ Rigidbody2D* rigidBody = QueryComponent (Rigidbody2D);
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive () && rigidBody->GetBody () != NULL)
+ {
+ // Yes, so no transformation.
+ matrix.SetIdentity();
+
+ // Ensure the rigid-body is available.
+ Assert (rigidBody->GetBody() != NULL);
+
+ // Set attached body.
+ *attachedBody = rigidBody->GetBody();
+
+ // Flag as non-static.
+ m_IsStaticBody = false;
+
+ return;
+ }
+
+ // Find valid rigid-body parent transform.
+ Transform* parentTransform = transform.GetParent ();
+
+ while(parentTransform)
+ {
+ // Fetch the grandparent transform.
+ Transform* grandParentTransform = parentTransform->GetParent ();
+
+ // Fetch the parent game object.
+ GameObject* go = parentTransform->GetGameObjectPtr ();
+ if (go)
+ {
+ // Fetch any rigid-body.
+ rigidBody = go->QueryComponent (Rigidbody2D);
+
+ // Is the rigid-body valid or we're at the transform root?
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive () && rigidBody->GetBody () != NULL)
+ {
+ // Yes, so calculate its relative transformation.
+ Vector3f childPosition = transform.GetPosition ();
+ Quaternionf childRotation = transform.GetRotation ();
+ Matrix4x4f childMatrix, parentMatrix;
+ childMatrix.SetTR (childPosition, childRotation);
+ parentMatrix = parentTransform->GetWorldToLocalMatrixNoScale ();
+ MultiplyMatrices4x4 (&parentMatrix, &childMatrix, &matrix);
+
+ // Ensure the rigid-body is available.
+ Assert (rigidBody->GetBody() != NULL);
+
+ // Set attached body.
+ *attachedBody = rigidBody->GetBody();
+
+ // Flag as non-static.
+ m_IsStaticBody = false;
+
+ return;
+ }
+ }
+
+ // Move up hierarchy.
+ parentTransform = grandParentTransform;
+ }
+
+ // Fetch the transformation from the origin (ground-body position).
+ matrix = transform.GetLocalToWorldMatrixNoScale();
+
+ // Set attached body (ground-body).
+ *attachedBody = GetPhysicsGroundBody();
+
+ // Flag as static.
+ m_IsStaticBody = true;
+}
+
+
+bool Collider2D::HasLocalTransformChanged () const
+{
+ // Fetch the transform.
+ const Transform& transform = GetComponent(Transform);
+
+ return !m_LocalTransformInitialized || m_LocalPosition != transform.GetLocalPosition () || m_LocalRotation != transform.GetLocalRotation ();
+}
+
+
+void Collider2D::UpdateLocalTransform ()
+{
+ // Fetch the transform.
+ const Transform& transform = GetComponent(Transform);
+
+ m_LocalPosition = transform.GetLocalPosition ();
+ m_LocalRotation = transform.GetLocalRotation ();
+ m_LocalTransformInitialized = true;
+}
+
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Collider2D.h b/Runtime/Physics2D/Collider2D.h
new file mode 100644
index 0000000..8b62ce5
--- /dev/null
+++ b/Runtime/Physics2D/Collider2D.h
@@ -0,0 +1,90 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/GameCode/Behaviour.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Vector3.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "External/Box2D/Box2D/Box2D.h"
+
+class Rigidbody2D;
+class Transform;
+class Matrix4x4f;
+class PhysicsMaterial2D;
+
+
+// --------------------------------------------------------------------------
+
+
+class Collider2D : public Behaviour
+{
+public:
+ typedef dynamic_array<b2Fixture*> FixtureArray;
+
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Collider2D, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Collider2D)
+
+ Collider2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Collider2D (); declared-by-macro
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void Deactivate (DeactivateOperation operation);
+ void Cleanup ();
+
+ // Colliders are recreated when deleting a rigibody component from existing game object;
+ // since colliders now have to be attached to the "static body". Old RigidBody2D component
+ // is not destroyed at this point yet, and thus it should be ignored when finding which
+ // RB to attach colliders to.
+ void RecreateCollider (const Rigidbody2D* ignoreRigidbody);
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ void SetIsTrigger (bool trigger);
+ bool GetIsTrigger () const { return m_IsTrigger; }
+
+ PPtr<PhysicsMaterial2D> GetMaterial ();
+ void SetMaterial (PPtr<PhysicsMaterial2D> material);
+
+ bool OverlapPoint (const Vector2f& point) const;
+
+ // Shapes.
+ inline int GetShapeCount() const { return m_Shapes.size(); }
+ const b2Fixture* GetShape() const { return (m_Shapes.size() ? m_Shapes[0] : 0); }
+ b2Fixture* GetShape() { return (m_Shapes.size() ? m_Shapes[0] : 0); }
+ const FixtureArray& GetShapes() const { return m_Shapes; }
+ FixtureArray& GetShapes() { return m_Shapes; }
+
+ Rigidbody2D* GetRigidbody ();
+
+ void TransformChanged (int changeMask);
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL) = 0;
+ void FinalizeCreate (b2FixtureDef& def, b2Body* body, const dynamic_array<b2Shape*>* shapes = 0);
+ void CalculateColliderTransformation (const Rigidbody2D* ignoreRigidbody, b2Body** attachedBody, Matrix4x4f& matrix);
+
+ bool HasLocalTransformChanged () const;
+ void UpdateLocalTransform ();
+
+protected:
+ PPtr<PhysicsMaterial2D> m_Material;
+ bool m_IsTrigger;
+ bool m_IsStaticBody;
+ FixtureArray m_Shapes;
+
+private:
+ Vector3f m_LocalPosition;
+ Quaternionf m_LocalRotation;
+ bool m_LocalTransformInitialized;
+};
+
+#endif
diff --git a/Runtime/Physics2D/CollisionListener2D.cpp b/Runtime/Physics2D/CollisionListener2D.cpp
new file mode 100644
index 0000000..2e7d2b6
--- /dev/null
+++ b/Runtime/Physics2D/CollisionListener2D.cpp
@@ -0,0 +1,400 @@
+#include "UnityPrefix.h"
+#include "CollisionListener2D.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+#include "Runtime/Profiler/Profiler.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileContactPreSolveAcquire, "Physics2D.ContactPreSolveAcquire", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactBeginAcquire, "Physics2D.ContactBeginAcquire", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactEndAcquire, "Physics2D.ContactEndAcquire", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactReporting, "Physics2D.ContactReporting", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactReportTriggers, "Physics2D.ContactReportTriggers", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileContactReportCollisions, "Physics2D.ContactReportCollisions", kProfilerPhysics)
+
+
+// --------------------------------------------------------------------------
+
+
+inline void VerifyObjectPtr(Object* obj)
+{
+ #if !UNITY_RELEASE
+ if (obj == NULL)
+ return;
+ Assert(Object::IDToPointer(obj->GetInstanceID()) == obj);
+ #endif
+}
+
+
+CollisionListener2D::CollisionListener2D()
+ : m_ReportingCollisions(false)
+{
+ m_Collisions.set_empty_key(std::make_pair((Collider2D*)NULL,(Collider2D*)NULL));
+ m_Collisions.set_deleted_key(std::make_pair((Collider2D*)~0,(Collider2D*)~0));
+}
+
+
+void CollisionListener2D::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
+{
+ PROFILER_AUTO(gPhysics2DProfileContactPreSolveAcquire, NULL);
+ Assert (!m_ReportingCollisions);
+
+ // Fetch the fixtures and colliders.
+ b2Fixture* fixture = contact->GetFixtureA();
+ b2Fixture* otherFixture = contact->GetFixtureB();
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ VerifyObjectPtr (collider);
+ Collider2D* otherCollider = reinterpret_cast<Collider2D*>(otherFixture->GetUserData());
+ VerifyObjectPtr (otherCollider);
+
+ // Calculate the contact key.
+ Collider2D* firstCollider = collider;
+ Collider2D* secondCollider = otherCollider;
+ if (firstCollider->GetInstanceID() > secondCollider->GetInstanceID ())
+ std::swap (firstCollider, secondCollider);
+ const ColliderKey contactKey = std::make_pair (firstCollider, secondCollider);
+
+ // Find the contact.
+ ColliderMap::iterator colliderItr = m_Collisions.find (contactKey);
+ Assert (colliderItr != m_Collisions.end ());
+ Collision2D& collision = colliderItr->second;
+
+ // Ignore if the contact was not just added.
+ if (collision.m_ContactMode != Collision2D::ContactAdded)
+ return;
+
+ // Calculate the contacts.
+ collision.m_ContactCount = contact->GetManifold()->pointCount;
+ contact->GetWorldManifold( &collision.m_ContactManifold );
+
+ // Fetch the rigid-bodies.
+ Rigidbody2D* rigidbody = collider ? collider->GetRigidbody() : NULL;
+ VerifyObjectPtr (rigidbody);
+ Rigidbody2D* otherRigidbody = otherCollider ? otherCollider->GetRigidbody() : NULL;
+ VerifyObjectPtr (otherRigidbody);
+ b2Body* body = rigidbody != NULL ? rigidbody->GetBody () : GetPhysicsGroundBody ();
+ b2Body* otherBody = otherRigidbody != NULL ? otherRigidbody->GetBody () : GetPhysicsGroundBody ();
+
+ // Calculate relative velocity.
+ const b2Vec2& contactPoint = collision.m_ContactManifold.points[0];
+ const b2Vec2 bodyVelocity = body->GetLinearVelocityFromWorldPoint(contactPoint);
+ const b2Vec2 otherBodyVelocity = otherBody->GetLinearVelocityFromWorldPoint(contactPoint);
+ collision.m_RelativeVelocity.Set (otherBodyVelocity.x - bodyVelocity.x, otherBodyVelocity.y - bodyVelocity.y);
+}
+
+
+void CollisionListener2D::BeginContact(b2Contact* contact)
+{
+ PROFILER_AUTO(gPhysics2DProfileContactBeginAcquire, NULL);
+
+ Assert (!m_ReportingCollisions);
+ Assert (contact->IsTouching());
+
+ // Fetch the fixtures and colliders.
+ b2Fixture* fixture = contact->GetFixtureA();
+ b2Fixture* otherFixture = contact->GetFixtureB();
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ VerifyObjectPtr (collider);
+ Collider2D* otherCollider = reinterpret_cast<Collider2D*>(otherFixture->GetUserData());
+ VerifyObjectPtr (otherCollider);
+
+ // Calculate the contact key.
+ Collider2D* firstCollider = collider;
+ Collider2D* secondCollider = otherCollider;
+ if (firstCollider->GetInstanceID() > secondCollider->GetInstanceID ())
+ std::swap (firstCollider, secondCollider);
+ const ColliderKey contactKey = std::make_pair (firstCollider, secondCollider);
+
+ // Find the contact.
+ ColliderMap::iterator colliderItr = m_Collisions.find (contactKey);
+
+ // If we already have a contact added for this collider-pair then we'll just bump-up the contact references.
+ // We do this because each collider can have multiple fixtures resulting in multiple contacts generated.
+ if (colliderItr != m_Collisions.end ())
+ {
+ Collision2D& collision = colliderItr->second;
+
+ // Increase contact references.
+ collision.m_ContactReferences++;
+
+ // Contact mode should revert to stay if it was removed else it's just added.
+ collision.m_ContactMode = collision.m_ContactMode == Collision2D::ContactRemoved ? Collision2D::ContactStay : Collision2D::ContactAdded;
+
+ return;
+ }
+
+ // Add the contact.
+ Collision2D& collision = m_Collisions[contactKey];
+
+ // Fetch the rigid-bodies.
+ Rigidbody2D* rigidbody = collider ? collider->GetRigidbody() : NULL;
+ VerifyObjectPtr (rigidbody);
+ Rigidbody2D* otherRigidbody = otherCollider ? otherCollider->GetRigidbody() : NULL;
+ VerifyObjectPtr (otherRigidbody);
+
+ // Populate the collision entry.
+ collision.m_ContactReferences = 1;
+ collision.m_Rigidbody = rigidbody;
+ collision.m_OtherRigidbody = otherRigidbody;
+ collision.m_Collider = collider;
+ collision.m_OtherCollider = otherCollider;
+ collision.m_Flipped = false;
+ collision.m_ContactMode = Collision2D::ContactAdded;
+ collision.m_TriggerCollision = fixture->IsSensor() || otherFixture->IsSensor();
+ collision.m_ContactCount = 0;
+ collision.m_RelativeVelocity = Vector2f::zero;
+}
+
+
+void CollisionListener2D::EndContact(b2Contact* contact)
+{
+ if (m_ReportingCollisions)
+ return;
+
+ PROFILER_AUTO(gPhysics2DProfileContactEndAcquire, NULL);
+
+ // Fetch the fixtures and colliders.
+ b2Fixture* fixture = contact->GetFixtureA();
+ b2Fixture* otherFixture = contact->GetFixtureB();
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ VerifyObjectPtr (collider);
+ Collider2D* otherCollider = reinterpret_cast<Collider2D*>(otherFixture->GetUserData());
+ VerifyObjectPtr (otherCollider);
+
+ // Calculate the contact key.
+ Collider2D* firstCollider = collider;
+ Collider2D* secondCollider = otherCollider;
+ if (firstCollider->GetInstanceID() > secondCollider->GetInstanceID ())
+ std::swap (firstCollider, secondCollider);
+ const ColliderKey contactKey = std::make_pair (firstCollider, secondCollider);
+
+ // Find the contact.
+ ColliderMap::iterator colliderItr = m_Collisions.find (contactKey);
+ Assert (colliderItr != m_Collisions.end ());
+ Collision2D& collision = colliderItr->second;
+
+ // We need to reduce the contact references.
+ // We do this because each collider can have multiple fixtures resulting in multiple contacts generated.
+ UInt32& contactReferences = collision.m_ContactReferences;
+ contactReferences--;
+
+ // Finish if there still exists contact references.
+ if (contactReferences > 0)
+ return;
+
+ // Flag the contact as invalid if either collider is not active.
+ if (!collider->IsActive () || !collider->GetEnabled () || !otherCollider->IsActive() || !otherCollider->GetEnabled ())
+ {
+ collision.m_ContactMode = Collision2D::ContactInvalid;
+ return;
+ }
+
+ // Sanity!
+ VerifyObjectPtr (collision.m_Collider);
+ VerifyObjectPtr (collision.m_OtherCollider);
+ VerifyObjectPtr (collision.m_Rigidbody);
+ VerifyObjectPtr (collision.m_OtherRigidbody);
+
+ // Flag as just removed.
+ collision.m_ContactMode = Collision2D::ContactRemoved;
+}
+
+
+void CollisionListener2D::InvalidateColliderCollisions(Collider2D* collider)
+{
+ // Flag collider collision information for the specified collider as invalid.
+ // NOTE: Does not send any collision/trigger messages (matches what 3D physics is doing).
+ for (ColliderMap::iterator colliderItr = m_Collisions.begin(); colliderItr != m_Collisions.end(); ++colliderItr)
+ {
+ // Does this contact relate to this collider?
+ if (colliderItr->first.first == collider || colliderItr->first.second == collider)
+ {
+ // Yes, so flag it as a bad contact.
+ colliderItr->second.m_ContactMode = Collision2D::ContactInvalid;
+ }
+ }
+}
+
+
+void CollisionListener2D::DestroyColliderCollisions(Collider2D* collider)
+{
+ // Destroy collider collision information for the specified collider immediately.
+ for (ColliderMap::iterator colliderItr = m_Collisions.begin(); colliderItr != m_Collisions.end(); /**/)
+ {
+ // Fetch next collider.
+ ColliderMap::iterator nextColliderItr = colliderItr;
+ ++nextColliderItr;
+
+ // Does this contact relate to this collider?
+ if (colliderItr->first.first == collider || colliderItr->first.second == collider)
+ {
+ // Yes, so remove it.
+ m_Collisions.erase (colliderItr);
+ }
+
+ colliderItr = nextColliderItr;
+ }
+}
+
+
+void CollisionListener2D::ReportCollisions()
+{
+ PROFILER_AUTO(gPhysics2DProfileContactReporting, NULL);
+
+ Assert (!m_ReportingCollisions);
+ m_ReportingCollisions = true;
+
+ // Iterate all the active collider collisions.
+ for (ColliderMap::iterator colliderItr = m_Collisions.begin(); colliderItr != m_Collisions.end(); /**/)
+ {
+ // Fetch next collider.
+ ColliderMap::iterator nextColliderItr = colliderItr;
+ ++nextColliderItr;
+
+ // Fetch the collision.
+ Collision2D& collision = colliderItr->second;
+
+ // Fetch the contact mode.
+ Collision2D::ContactMode& contactMode = collision.m_ContactMode;
+
+ // Process the collision if it's not invalid.
+ if (contactMode != Collision2D::ContactInvalid)
+ {
+ // Further validate the collision.
+ VerifyObjectPtr (collision.m_Collider);
+ VerifyObjectPtr (collision.m_OtherCollider);
+ VerifyObjectPtr (collision.m_Rigidbody);
+ VerifyObjectPtr (collision.m_OtherRigidbody);
+
+ // Calculate the message targets.
+ Unity::Component* messageTarget = (Unity::Component*)collision.m_Collider;
+ Unity::Component* otherMessageTarget = (Unity::Component*)collision.m_OtherCollider;
+
+ // Reset the callback message.
+ const MessageIdentifier* callbackMessage = NULL;
+
+ // Is this a trigger collision?
+ if (collision.m_TriggerCollision)
+ {
+ PROFILER_AUTO(gPhysics2DProfileContactReportTriggers, NULL);
+
+ // Yes, so calculate the appropriate trigger callback message.
+ if (collision.m_ContactMode == Collision2D::ContactAdded)
+ callbackMessage = &kTriggerEnter2D;
+ else if (collision.m_ContactMode == Collision2D::ContactRemoved)
+ callbackMessage = &kTriggerExit2D;
+ else
+ callbackMessage = &kTriggerStay2D;
+
+ // Send trigger callbacks to both colliders
+ messageTarget->SendMessage (*callbackMessage, collision.m_OtherCollider, ClassID (Collider2D));
+ otherMessageTarget->SendMessage (*callbackMessage, collision.m_Collider, ClassID(Collider2D));
+ }
+ else
+ {
+ PROFILER_AUTO(gPhysics2DProfileContactReportCollisions, NULL);
+
+ // No, so calculate the appropriate collision callback message.
+ if (collision.m_ContactMode == Collision2D::ContactAdded)
+ callbackMessage = &kCollisionEnter2D;
+ else if (collision.m_ContactMode == Collision2D::ContactRemoved)
+ callbackMessage = &kCollisionExit2D;
+ else
+ callbackMessage = &kCollisionStay2D;
+
+ // Send collision callbacks to both colliders.
+ collision.m_Flipped = true;
+ messageTarget->SendMessage (*callbackMessage, &collision, ClassID (Collision2D));
+ collision.m_Flipped = false;
+ otherMessageTarget->SendMessage (*callbackMessage, &collision, ClassID (Collision2D));
+ }
+ }
+
+ // Remove contacts that are flagged as being removed.
+ if (contactMode == Collision2D::ContactRemoved || contactMode == Collision2D::ContactInvalid)
+ {
+ m_Collisions.erase (colliderItr);
+ }
+ else
+ {
+ // The collision is now at "stay" mode.
+ contactMode = Collision2D::ContactStay;
+ }
+
+ colliderItr = nextColliderItr;
+ }
+
+ m_ReportingCollisions = false;
+}
+
+
+#if ENABLE_SCRIPTING
+ScriptingObjectPtr ConvertCollision2DToScripting (Collision2D* input)
+{
+ Collision2D& collision = *reinterpret_cast<Collision2D*>(input);
+ ScriptingCollision2D scriptCollision;
+ ScriptingObjectPtr collider;
+ ScriptingObjectPtr otherCollider;
+
+ // Populate object targets.
+ if (collision.m_Flipped)
+ {
+ scriptCollision.rigidbody = Scripting::ScriptingWrapperFor (collision.m_OtherRigidbody);
+ collider = scriptCollision.collider = Scripting::ScriptingWrapperFor (collision.m_OtherCollider);
+ otherCollider = Scripting::ScriptingWrapperFor (collision.m_Collider);
+ scriptCollision.relativeVelocity = -collision.m_RelativeVelocity;
+ }
+ else
+ {
+ scriptCollision.rigidbody = Scripting::ScriptingWrapperFor (collision.m_Rigidbody);
+ collider = scriptCollision.collider = Scripting::ScriptingWrapperFor (collision.m_Collider);
+ otherCollider = Scripting::ScriptingWrapperFor (collision.m_OtherCollider);
+ scriptCollision.relativeVelocity = collision.m_RelativeVelocity;
+ }
+
+ // Populate contact array.
+ ScriptingArrayPtr contacts = CreateScriptingArray<ScriptingContactPoint2D>(GetScriptingManager ().GetCommonClasses ().contactPoint2D, collision.m_ContactCount);
+ scriptCollision.contacts = contacts;
+
+ // Fetch collision normal.
+ const b2Vec2& manifoldNormal = collision.m_Flipped ? -collision.m_ContactManifold.normal : collision.m_ContactManifold.normal;
+ const Vector2f collisionNormal (manifoldNormal.x, manifoldNormal.y);
+
+ // Populate contacts.
+ for (int index = 0; index < collision.m_ContactCount; ++index )
+ {
+#if UNITY_WINRT
+ ScriptingContactPoint2D contactPoint;
+#else
+ ScriptingContactPoint2D& contactPoint = Scripting::GetScriptingArrayElement<ScriptingContactPoint2D> (contacts, index);
+#endif
+ // Set contact point.
+ const b2Vec2& manifoldPoint = collision.m_ContactManifold.points[index];
+ contactPoint.point.Set (manifoldPoint.x, manifoldPoint.y);
+ contactPoint.normal = collisionNormal;
+
+ // Set colliders.
+ contactPoint.collider = collider;
+ contactPoint.otherCollider = otherCollider;
+
+#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 ScriptingContactPoint2D
+ Scripting::SetScriptingArrayElement(contacts, index, CreateScriptingObjectFromNativeStruct<ScriptingContactPoint2D>(GetScriptingManager ().GetCommonClasses ().contactPoint2D, contactPoint));
+#endif
+ }
+
+ return CreateScriptingObjectFromNativeStruct<ScriptingCollision2D>(GetScriptingManager ().GetCommonClasses ().collision2D, scriptCollision);
+}
+#endif
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/CollisionListener2D.h b/Runtime/Physics2D/CollisionListener2D.h
new file mode 100644
index 0000000..f87cecb
--- /dev/null
+++ b/Runtime/Physics2D/CollisionListener2D.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Utilities/dense_hash_map.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Box2D/Box2D.h"
+#include <list>
+
+class Rigidbody2D;
+class b2Contact;
+
+
+// --------------------------------------------------------------------------
+
+
+struct Collision2D
+{
+ Collision2D() :
+ m_Rigidbody(NULL),
+ m_OtherRigidbody(NULL),
+ m_Collider(NULL),
+ m_OtherCollider(NULL),
+ m_ContactMode(ContactInvalid),
+ m_Flipped(false),
+ m_TriggerCollision(false),
+ m_ContactCount(0),
+ m_ContactReferences(0) {}
+
+ Rigidbody2D* m_Rigidbody;
+ Rigidbody2D* m_OtherRigidbody;
+ Collider2D* m_Collider;
+ Collider2D* m_OtherCollider;
+
+ UInt32 m_ContactCount;
+ UInt32 m_ContactReferences;
+ b2WorldManifold m_ContactManifold;
+ Vector2f m_RelativeVelocity;
+
+ enum ContactMode
+ {
+ ContactInvalid,
+ ContactAdded,
+ ContactRemoved,
+ ContactStay
+ };
+
+ ContactMode m_ContactMode;
+ bool m_Flipped;
+ bool m_TriggerCollision;
+};
+
+
+// --------------------------------------------------------------------------
+
+#if ENABLE_SCRIPTING
+struct ScriptingContactPoint2D
+{
+ Vector2f point;
+ Vector2f normal;
+ ScriptingObjectPtr collider;
+ ScriptingObjectPtr otherCollider;
+};
+
+struct ScriptingCollision2D
+{
+ ScriptingObjectPtr rigidbody;
+ ScriptingObjectPtr collider;
+ ScriptingArrayPtr contacts;
+ Vector2f relativeVelocity;
+};
+
+ScriptingObjectPtr ConvertCollision2DToScripting (Collision2D* input);
+#endif
+
+
+// --------------------------------------------------------------------------
+
+
+class CollisionListener2D : public b2ContactListener
+{
+public:
+ CollisionListener2D();
+
+ // b2ContactListener interface
+ virtual void BeginContact(b2Contact* contact);
+ virtual void EndContact(b2Contact* contact);
+ virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold);
+
+ void ReportCollisions();
+ void InvalidateColliderCollisions(Collider2D* collider);
+ void DestroyColliderCollisions(Collider2D* collider);
+
+private:
+ // Collision information is stored for each collider pair (the key is sorted by instance IDs so it always identifies the pair).
+ // In the collision information, contact points etc. are stored.
+ typedef std::pair<Collider2D*,Collider2D*> ColliderKey;
+ struct ColliderKeyHashFunctor
+ {
+ inline size_t operator()(const ColliderKey& x) const
+ {
+ UInt32 xa = x.first->GetInstanceID();
+ UInt32 xb = x.second->GetInstanceID();
+ UInt32 a = xa;
+ a = (a+0x7ed55d16) + (a<<12);
+ a = (a^0xc761c23c) ^ (a>>19);
+ a ^= xb;
+ a = (a+0x165667b1) + (a<<5);
+ a = (a+0xd3a2646c) ^ (a<<9);
+ return a;
+ }
+ };
+ typedef std::pair<const ColliderKey, Collision2D> ColliderKeyToCollisionPair;
+ typedef dense_hash_map<ColliderKey, Collision2D, ColliderKeyHashFunctor, std::equal_to<ColliderKey>, STL_ALLOCATOR(kMemSTL, ColliderKeyToCollisionPair) > ColliderMap;
+
+private:
+ ColliderMap m_Collisions;
+ bool m_ReportingCollisions;
+};
+
+#endif
diff --git a/Runtime/Physics2D/DistanceJoint2D.cpp b/Runtime/Physics2D/DistanceJoint2D.cpp
new file mode 100644
index 0000000..85b932a
--- /dev/null
+++ b/Runtime/Physics2D/DistanceJoint2D.cpp
@@ -0,0 +1,161 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/DistanceJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (DistanceJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (DistanceJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+DistanceJoint2D::DistanceJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+DistanceJoint2D::~DistanceJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void DistanceJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_Distance);
+}
+
+
+void DistanceJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Distance = clamp<float> (m_Distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void DistanceJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Distance = 1.0f;
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void DistanceJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, DistanceJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void DistanceJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, DistanceJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void DistanceJoint2D::SetDistance (float distance)
+{
+ ABORT_INVALID_FLOAT (distance, distance, DistanceJoint2D);
+
+ m_Distance = clamp<float> (distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2RopeJoint*)m_Joint)->SetMaxLength (m_Distance);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void DistanceJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Configure the joint definition.
+ b2RopeJointDef jointDef;
+ jointDef.maxLength = m_Distance;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+void DistanceJoint2D::AutoCalculateDistance ()
+{
+ // Reset to default.
+ m_Distance = 1.0f;
+
+ if (m_ConnectedRigidBody.IsNull ())
+ return;
+
+ // Find the appropriate rigid body A.
+ Rigidbody2D* rigidBodyA = QueryComponent(Rigidbody2D);
+ Assert (rigidBodyA != NULL);
+
+ // Find the appropriate rigid body B.
+ Rigidbody2D* rigidBodyB = m_ConnectedRigidBody;
+
+ Transform* transformA = QueryComponent (Transform);
+ Vector3f pointA = transformA->TransformPoint(Vector3f(m_Anchor.x, m_Anchor.y, 0.0f));
+
+ if (rigidBodyB == NULL)
+ {
+ m_Distance = Magnitude(Vector2f(pointA.x, pointA.y));
+ return;
+ }
+
+ Transform* transformB = rigidBodyB->GetGameObjectPtr ()->QueryComponent (Transform);
+ Vector3f pointB = transformB->TransformPoint(Vector3f(m_ConnectedAnchor.x, m_ConnectedAnchor.y, 0.0f));
+
+ m_Distance = Magnitude(Vector2f(pointA.x-pointB.x, pointA.y-pointB.y));
+}
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/DistanceJoint2D.h b/Runtime/Physics2D/DistanceJoint2D.h
new file mode 100644
index 0000000..5eeedc2
--- /dev/null
+++ b/Runtime/Physics2D/DistanceJoint2D.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class DistanceJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (DistanceJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (DistanceJoint2D)
+
+ DistanceJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~DistanceJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ void SetDistance (float distance);
+ float GetDistance () const { return m_Distance; }
+
+protected:
+ virtual void Create ();
+ void AutoCalculateDistance ();
+
+protected:
+ float m_Distance; ///< The maximum distance which the joint should attempt to maintain between attached bodies. range { 0.005, 1000000 }
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+};
+
+#endif
diff --git a/Runtime/Physics2D/EdgeCollider2D.cpp b/Runtime/Physics2D/EdgeCollider2D.cpp
new file mode 100644
index 0000000..1c49f96
--- /dev/null
+++ b/Runtime/Physics2D/EdgeCollider2D.cpp
@@ -0,0 +1,176 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/EdgeCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Filters/AABBUtility.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "External/libtess2/libtess2/tesselator.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileEdgeColliderCreate, "Physics2D.EdgeColliderCreate", kProfilerPhysics)
+
+IMPLEMENT_CLASS (EdgeCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (EdgeCollider2D)
+
+#define EDGE_COLLIDER_2D_MIN_DISTANCE (b2_linearSlop * b2_linearSlop * 2.01f)
+
+// --------------------------------------------------------------------------
+
+
+EdgeCollider2D::EdgeCollider2D (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+{
+}
+
+
+EdgeCollider2D::~EdgeCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void EdgeCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Points);
+}
+
+
+void EdgeCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Points.clear ();
+ m_Points.push_back (Vector2f (-0.5f, 0.0f));
+ m_Points.push_back (Vector2f (0.5f, 0.0f));
+}
+
+
+void EdgeCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+
+ AABB aabb;
+ if (GetGameObjectPtr () && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ if (aabb.GetExtent ().x < EDGE_COLLIDER_2D_MIN_DISTANCE)
+ {
+ m_Points.clear ();
+ m_Points.push_back (Vector2f (-0.5f, 0.0f));
+ m_Points.push_back (Vector2f (0.5f, 0.0f));
+ return;
+ }
+
+ const Vector3f min = aabb.GetMin();
+ const Vector3f max = aabb.GetMax();
+ const float y = (min.y + max.y) * 0.5f;
+
+ Vector2f points[2] = { Vector2f(min.x, y), Vector2f(max.x, y) };
+ SetPoints (points, 2);
+ }
+}
+
+
+bool EdgeCollider2D::SetPoints (const Vector2f* points, size_t count)
+{
+ // Fail if point count is invalid.
+ if (count < 2)
+ return false;
+
+ m_Points.clear ();
+ while (count-- > 0)
+ {
+ m_Points.push_back (*points++);
+ }
+
+ // Create the points.
+ Create();
+
+ return true;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void EdgeCollider2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfileEdgeColliderCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive() || m_Points.size () < 2)
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ // Fetch the collider scale.
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+
+ // Transform the chain.
+ b2Vec2* transformedPoints;
+ ALLOC_TEMP(transformedPoints, b2Vec2, m_Points.size () + 1); // We add an extra one in-case we need to extend.
+ const int validPointCount = TransformPoints (relativeTransform, scale, transformedPoints);
+
+ // Invalid chain if a single edge doesn't exit.
+ if (validPointCount < 2)
+ return;
+
+ // Check vertex distances.
+ for (int i = 1; i < validPointCount; ++i)
+ if (b2DistanceSquared (transformedPoints[i-1], transformedPoints[i]) < EDGE_COLLIDER_2D_MIN_DISTANCE)
+ return;
+
+ // Generate the chain shape.
+ b2ChainShape chainShape;
+ chainShape.CreateChain (transformedPoints, validPointCount);
+
+ // Create the chain fixture.
+ dynamic_array<b2Shape*> chainShapes(kMemTempAlloc);
+ chainShapes.push_back (&chainShape);
+ b2FixtureDef def;
+ FinalizeCreate(def, body, &chainShapes);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+int EdgeCollider2D::TransformPoints(const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints)
+{
+ const size_t inPointCount = m_Points.size ();
+ const Vector2f* edgePoint = m_Points.data ();
+ int outCount = 0;
+ for (size_t i = 0; i <inPointCount; ++i, ++edgePoint)
+ {
+ // Calculate transform points.
+ const Vector3f vertex3D = relativeTransform.MultiplyPoint3 (Vector3f(edgePoint->x * scale.x, edgePoint->y * scale.y, 0.0f));
+ const b2Vec2 vertex2D(vertex3D.x, vertex3D.y);
+
+ // Skip point if they end up being too close. Box2d fires asserts if distance between neighbors is less than b2_linearSlop.
+ if (outCount > 0 && b2DistanceSquared(*(outPoints-1), vertex2D) <= EDGE_COLLIDER_2D_MIN_DISTANCE)
+ continue;
+
+ *outPoints++ = vertex2D;
+ ++outCount;
+ }
+
+ return outCount;
+}
+
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/EdgeCollider2D.h b/Runtime/Physics2D/EdgeCollider2D.h
new file mode 100644
index 0000000..f595640
--- /dev/null
+++ b/Runtime/Physics2D/EdgeCollider2D.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Graphics/Polygon2D.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+
+// --------------------------------------------------------------------------
+
+
+class EdgeCollider2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_CLASS (EdgeCollider2D, Collider2D)
+ DECLARE_OBJECT_SERIALIZE (EdgeCollider2D)
+
+ typedef dynamic_array<Vector2f> Points;
+
+ EdgeCollider2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~EdgeCollider2D (); declared-by-macro
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ bool SetPoints (const Vector2f* points, size_t count);
+ const Points& GetPoints() const { return m_Points; }
+ const Vector2f& GetPoint (int index) { Assert(index < m_Points.size ()); return m_Points[index]; }
+ size_t GetPointCount() const { return m_Points.size (); }
+ size_t GetEdgeCount() const { return m_Points.size () - 1; }
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ int TransformPoints(const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints);
+
+private:
+ Points m_Points;
+};
+
+#endif
diff --git a/Runtime/Physics2D/HingeJoint2D.cpp b/Runtime/Physics2D/HingeJoint2D.cpp
new file mode 100644
index 0000000..23e6429
--- /dev/null
+++ b/Runtime/Physics2D/HingeJoint2D.cpp
@@ -0,0 +1,211 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/HingeJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (HingeJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (HingeJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+HingeJoint2D::HingeJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_OldReferenceAngle (std::numeric_limits<float>::infinity ())
+{
+}
+
+
+HingeJoint2D::~HingeJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void HingeJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_UseMotor);
+ transfer.Align ();
+ TRANSFER (m_Motor);
+ TRANSFER (m_UseLimits);
+ transfer.Align ();
+ TRANSFER (m_AngleLimits);
+}
+
+
+void HingeJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Motor.CheckConsistency ();
+ m_AngleLimits.CheckConsistency ();
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void HingeJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_UseMotor = false;
+ m_UseLimits = false;
+ m_Motor.Initialize ();
+ m_AngleLimits.Initialize ();
+
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void HingeJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, HingeJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void HingeJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, HingeJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void HingeJoint2D::SetUseMotor (bool enable)
+{
+ m_UseMotor = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2RevoluteJoint*)m_Joint)->EnableMotor(m_UseMotor);
+}
+
+
+void HingeJoint2D::SetUseLimits (bool enable)
+{
+ m_UseLimits = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2RevoluteJoint*)m_Joint)->EnableLimit(m_UseLimits);
+}
+
+
+void HingeJoint2D::SetMotor (const JointMotor2D& motor)
+{
+ m_Motor = motor;
+ m_Motor.CheckConsistency ();
+ SetDirty();
+
+ // Motor is automatically enabled if motor is set.
+ SetUseMotor(true);
+
+ if (m_Joint != NULL)
+ {
+ b2RevoluteJoint* joint = (b2RevoluteJoint*)m_Joint;
+ joint->SetMotorSpeed (math::radians (m_Motor.m_MotorSpeed));
+ joint->SetMaxMotorTorque (m_Motor.m_MaximumMotorForce);
+ }
+}
+
+
+void HingeJoint2D::SetLimits (const JointAngleLimits2D& limits)
+{
+ m_AngleLimits = limits;
+ m_AngleLimits.CheckConsistency ();
+ SetDirty();
+
+ // Limits ares automatically enabled if limits are set.
+ SetUseLimits(true);
+
+ if (m_Joint != NULL)
+ {
+ b2RevoluteJoint* joint = (b2RevoluteJoint*)m_Joint;
+ joint->SetLimits(math::radians (m_AngleLimits.m_LowerAngle), math::radians (m_AngleLimits.m_UpperAngle));
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void HingeJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Configure the joint definition.
+ b2RevoluteJointDef jointDef;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+ jointDef.enableMotor = m_UseMotor;
+ jointDef.enableLimit = m_UseLimits;
+ jointDef.motorSpeed = math::radians (m_Motor.m_MotorSpeed);
+ jointDef.maxMotorTorque = m_Motor.m_MaximumMotorForce;
+ jointDef.lowerAngle = math::radians (m_AngleLimits.m_LowerAngle);
+ jointDef.upperAngle = math::radians (m_AngleLimits.m_UpperAngle);
+ if (jointDef.lowerAngle > jointDef.upperAngle)
+ std::swap(jointDef.lowerAngle, jointDef.upperAngle);
+ jointDef.referenceAngle = m_OldReferenceAngle == std::numeric_limits<float>::infinity () ? FetchBodyB()->GetAngle() - FetchBodyA()->GetAngle() : m_OldReferenceAngle;
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+void HingeJoint2D::ReCreate()
+{
+ // Do we have an existing joint and we're still active/enabled?
+ if (m_Joint != NULL && IsActive () && GetEnabled ())
+ {
+ // Yes, so keep reference angle.
+ m_OldReferenceAngle = ((b2RevoluteJoint*)m_Joint)->GetReferenceAngle ();
+ }
+ else
+ {
+ // No, so reset reference angle.
+ m_OldReferenceAngle = std::numeric_limits<float>::infinity ();
+ }
+
+ Super::ReCreate ();
+}
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/HingeJoint2D.h b/Runtime/Physics2D/HingeJoint2D.h
new file mode 100644
index 0000000..6da16b7
--- /dev/null
+++ b/Runtime/Physics2D/HingeJoint2D.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class HingeJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (HingeJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (HingeJoint2D)
+
+ HingeJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~HingeJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ bool GetUseMotor () const { return m_UseMotor; }
+ void SetUseMotor (bool enable);
+
+ bool GetUseLimits () const { return m_UseLimits; }
+ void SetUseLimits (bool enable);
+
+ JointMotor2D GetMotor () const { return m_Motor; }
+ void SetMotor (const JointMotor2D& motor);
+
+ JointAngleLimits2D GetLimits () const { return m_AngleLimits; }
+ void SetLimits (const JointAngleLimits2D& limits);
+
+protected:
+ virtual void Create ();
+ virtual void ReCreate();
+
+protected:
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+ JointMotor2D m_Motor; ///< The joint motor.
+ JointAngleLimits2D m_AngleLimits; ///< The joint angle limits.
+ bool m_UseMotor; ///< Whether to use the joint motor or not.
+ bool m_UseLimits; ///< Whether to use the angle limits or not.
+
+private:
+ float m_OldReferenceAngle;
+};
+
+#endif
diff --git a/Runtime/Physics2D/Joint2D.cpp b/Runtime/Physics2D/Joint2D.cpp
new file mode 100644
index 0000000..ed0de54
--- /dev/null
+++ b/Runtime/Physics2D/Joint2D.cpp
@@ -0,0 +1,201 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+#include "Runtime/Physics2D/Joint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/LogAssert.h"
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (Joint2D)
+IMPLEMENT_OBJECT_SERIALIZE (Joint2D)
+INSTANTIATE_TEMPLATE_TRANSFER (Joint2D)
+
+// --------------------------------------------------------------------------
+
+
+Joint2D::Joint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Joint(NULL)
+{
+}
+
+
+Joint2D::~Joint2D ()
+{
+ Cleanup ();
+}
+
+
+template<class TransferFunction>
+void Joint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_CollideConnected);
+ transfer.Align();
+ TRANSFER (m_ConnectedRigidBody);
+}
+
+
+void Joint2D::Reset ()
+{
+ Super::Reset ();
+
+ Cleanup ();
+
+ m_ConnectedRigidBody = NULL;
+ m_CollideConnected = false;
+}
+
+
+void Joint2D::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ // Recreate joint if appropriate.
+ // Most of Box2D joint properties are immutable once created
+ // thus causing us to regenerate the joint if a property changes in the editor.
+ if ((awakeMode == kDefaultAwakeFromLoad))
+ ReCreate ();
+}
+
+
+void Joint2D::Deactivate (DeactivateOperation operation)
+{
+ Cleanup ();
+ Super::Deactivate (operation);
+}
+
+
+void Joint2D::AddToManager ()
+{
+ // Create the joint.
+ ReCreate ();
+}
+
+
+void Joint2D::RemoveFromManager ()
+{
+ // Destroy the joint.
+ Cleanup ();
+}
+
+
+void Joint2D::RecreateJoint (const Rigidbody2D* ignoreRigidbody)
+{
+ if (IsActive () && GetEnabled())
+ ReCreate ();
+ else
+ Cleanup ();
+}
+
+
+void Joint2D::SetConnectedBody (PPtr<Rigidbody2D> rigidBody)
+{
+ m_ConnectedRigidBody = rigidBody;
+ SetDirty ();
+
+ ReCreate ();
+}
+
+
+void Joint2D::SetCollideConnected (bool collide)
+{
+ m_CollideConnected = collide;
+ SetDirty ();
+
+ ReCreate ();
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void Joint2D::ReCreate()
+{
+ Cleanup();
+
+ if (IsActive () && GetEnabled ())
+ Create();
+}
+
+
+void Joint2D::Cleanup ()
+{
+ // Finish if no joint to clean-up.
+ if (!m_Joint)
+ return;
+
+ // Destroy the joint.
+ GetPhysics2DWorld ()->DestroyJoint (m_Joint);
+ m_Joint = NULL;
+}
+
+
+b2Body* Joint2D::FetchBodyA () const
+{
+ // Find the rigid-body A.
+ Rigidbody2D* rigidBodyA = QueryComponent(Rigidbody2D);
+ Assert (rigidBodyA != NULL);
+
+ // Ensure the rigid-body (body) is available.
+ if ( rigidBodyA )
+ rigidBodyA->Create();
+
+ // Fetch the body A.
+ return rigidBodyA->GetBody();
+}
+
+
+b2Body* Joint2D::FetchBodyB () const
+{
+ // Find the appropriate rigid body B.
+ Rigidbody2D* rigidBodyB = m_ConnectedRigidBody;
+
+ // Ensure the rigid-body (body) is available.
+ if ( rigidBodyB )
+ rigidBodyB->Create();
+
+ // Fetch the appropriate body B.
+ return rigidBodyB != NULL ? rigidBodyB->GetBody() : GetPhysicsGroundBody();
+}
+
+
+void Joint2D::FinalizeCreateJoint (b2JointDef* jointDef)
+{
+ Assert (jointDef != NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch the appropriate body A.
+ b2Body* bodyA = FetchBodyA();
+
+ // Fetch the appropriate body B.
+ b2Body* bodyB = FetchBodyB();
+
+ // Finish if the same body is being used.
+ if ( bodyA == bodyB )
+ {
+ WarningStringObject(Format("Cannot create 2D joint on '%s' as it connects to itself.\n",
+ GetGameObjectPtr()->GetName()), this);
+ return;
+ }
+
+ // Populate the basic joint definition information.
+ jointDef->bodyA = bodyA;
+ jointDef->bodyB = bodyB;
+ jointDef->collideConnected = m_CollideConnected;
+ jointDef->userData = this;
+
+ // Create the joint.
+ m_Joint = GetPhysics2DWorld ()->CreateJoint (jointDef);
+}
+
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Joint2D.h b/Runtime/Physics2D/Joint2D.h
new file mode 100644
index 0000000..a624709
--- /dev/null
+++ b/Runtime/Physics2D/Joint2D.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/JointDescriptions2D.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/GameCode/Behaviour.h"
+
+class Rigidbody2D;
+class b2Joint;
+class b2Body;
+struct b2JointDef;
+
+// --------------------------------------------------------------------------
+
+
+class Joint2D : public Behaviour
+{
+ friend class Rigidbody2D;
+
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (Joint2D, Behaviour)
+ DECLARE_OBJECT_SERIALIZE (Joint2D)
+
+ Joint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Joint2D (); declared-by-macro
+
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ virtual void AddToManager ();
+ virtual void RemoveFromManager ();
+
+ void RecreateJoint (const Rigidbody2D* ignoreRigidbody);
+
+ void SetConnectedBody (PPtr<Rigidbody2D> body);
+ PPtr<Rigidbody2D> GetConnectedBody () const { return m_ConnectedRigidBody; }
+
+ void SetCollideConnected (bool Collide);
+ bool GetCollideConnected () const { return m_CollideConnected; }
+
+protected:
+ virtual void Create () = 0;
+ virtual void ReCreate();
+ virtual void Cleanup ();
+
+ b2Body* FetchBodyA () const;
+ b2Body* FetchBodyB () const;
+ void FinalizeCreateJoint (b2JointDef* jointDef);
+
+protected:
+ PPtr<Rigidbody2D> m_ConnectedRigidBody; ///< The rigid body to connect to. No rigid body connects to the scene.
+ bool m_CollideConnected; ///< Whether rigid bodies connected with this joint can collide or not.
+ b2Joint* m_Joint;
+};
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/JointDescriptions2D.h b/Runtime/Physics2D/JointDescriptions2D.h
new file mode 100644
index 0000000..23e2062
--- /dev/null
+++ b/Runtime/Physics2D/JointDescriptions2D.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Serialize/SerializeUtility.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+// --------------------------------------------------------------------------
+
+
+struct JointMotor2D
+{
+ float m_MotorSpeed; ///< The target motor speed in degrees/second. range { -1000000, 1000000 }
+ float m_MaximumMotorForce; ///< The maximum force the motor can use to achieve the desired motor speed. range { 0.0, 1000000 }
+
+ void Initialize ()
+ {
+ m_MotorSpeed = 0.0f;
+ m_MaximumMotorForce = 10000.0f;
+ }
+
+ void CheckConsistency ()
+ {
+ m_MotorSpeed = clamp<float> (m_MotorSpeed, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_MaximumMotorForce = clamp<float> (m_MaximumMotorForce, 0, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointMotor2D)
+};
+
+template<class TransferFunction>
+void JointMotor2D::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_MotorSpeed);
+ TRANSFER (m_MaximumMotorForce);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct JointAngleLimits2D
+{
+ float m_LowerAngle; ///< The lower angle (in degrees) limit to constrain the joint to. range { -359.9999, 359.9999 }
+ float m_UpperAngle; ///< The upper angle (in degrees) limit to constrain the joint to. range { -359.9999, 359.9999 }
+
+ void Initialize ()
+ {
+ m_LowerAngle = 0.0f;
+ m_UpperAngle = 359.0f;
+ }
+
+ void CheckConsistency ()
+ {
+ m_LowerAngle = clamp<float> (m_LowerAngle, -359.9999f, 359.9999f);
+ m_UpperAngle = clamp<float> (m_UpperAngle, -359.9999f, 359.9999f);
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointAngleLimit2D)
+};
+
+template<class TransferFunction>
+void JointAngleLimits2D::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_LowerAngle);
+ TRANSFER (m_UpperAngle);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct JointTranslationLimits2D
+{
+ float m_LowerTranslation; ///< The lower translation limit to constrain the joint to. range { -1000000, 1000000 }
+ float m_UpperTranslation; ///< The upper translation limit to constrain the joint to. range { -1000000, 1000000 }
+
+ void Initialize ()
+ {
+ m_LowerTranslation = 0.0f;
+ m_UpperTranslation = 0.0f;
+ }
+
+ void CheckConsistency ()
+ {
+ m_LowerTranslation = clamp<float> (m_LowerTranslation, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_UpperTranslation = clamp<float> (m_UpperTranslation, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ }
+
+ DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (JointTranslationLimits2D)
+};
+
+template<class TransferFunction>
+void JointTranslationLimits2D::Transfer (TransferFunction& transfer)
+{
+ TRANSFER (m_LowerTranslation);
+ TRANSFER (m_UpperTranslation);
+}
+
+#endif
diff --git a/Runtime/Physics2D/Physics2DManager.cpp b/Runtime/Physics2D/Physics2DManager.cpp
new file mode 100644
index 0000000..cf6dcd3
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DManager.cpp
@@ -0,0 +1,1228 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/CollisionListener2D.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "Runtime/Core/Callbacks/PlayerLoopCallbacks.h"
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Geometry/Plane.h"
+#include "Runtime/Geometry/Intersection.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Interfaces/IPhysics2D.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+
+
+PROFILER_INFORMATION(gPhysics2DDynamicUpdateProfile, "Physics2D.DynamicUpdate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DFixedUpdateProfile, "Physics2D.FixedUpdate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DInterpolationsProfile, "Physics2D.Interpolation", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DSimProfile, "Physics2D.Simulate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DUpdateTransformsProfile, "Physics2D.UpdateTransforms", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DCallbacksProfile, "Physics2D.Callbacks", kProfilerPhysics)
+PROFILER_INFORMATION(gLinecast2DProfile, "Physics2D.Linecast", kProfilerPhysics)
+PROFILER_INFORMATION(gLinecastAll2DProfile, "Physics2D.LinecastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycast2DProfile, "Physics2D.Raycast", kProfilerPhysics)
+PROFILER_INFORMATION(gRaycastAll2DProfile, "Physics2D.RaycastAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapPoint2DProfile, "Physics2D.OverlapPoint", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapPointAll2DProfile, "Physics2D.OverlapPointAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapCircle2DProfile, "Physics2D.OverlapCircle", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapCircleAll2DProfile, "Physics2D.OverlapCircleAll", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapArea2DProfile, "Physics2D.OverlapArea", kProfilerPhysics)
+PROFILER_INFORMATION(gOverlapAreaAll2DProfile, "Physics2D.OverlapAreaAll", kProfilerPhysics)
+PROFILER_INFORMATION(gGetRayIntersection2DProfile, "Physics2D.GetRayIntersection", kProfilerPhysics)
+PROFILER_INFORMATION(gGetRayIntersectionAll2DProfile, "Physics2D.GetRayIntersectionAll", kProfilerPhysics)
+
+
+// --------------------------------------------------------------------------
+
+
+inline bool CheckFixtureLayer(b2Fixture* fixture, int layerMask)
+{
+ DebugAssert(fixture);
+
+ Collider2D* col = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!col)
+ return false; // no collider
+
+ GameObject* go = col->GetGameObjectPtr();
+ if (!go)
+ return false; // no GO
+
+ int goMask = go->GetLayerMask();
+ if ((goMask & layerMask) == 0)
+ return false; // ignoring this layer
+
+ return true; // all passed
+}
+
+// --------------------------------------------------------------------------
+
+
+inline bool CheckColliderDepth(Collider2D* collider, const float minDepth, const float maxDepth)
+{
+ DebugAssert(collider);
+
+ // Fetch depth of the collider.
+ const float depth = collider->GetComponent(Transform).GetPosition ().z;
+
+ // Check if in the depth range.
+ return !(depth < minDepth || depth > maxDepth);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+inline void NormalizeDepthRange(float& minDepth, float& maxDepth)
+{
+ // Clamp any range bounds specified as +- infinity to real values.
+ minDepth = (minDepth == -std::numeric_limits<float>::infinity ()) ? -std::numeric_limits<float>::max () : minDepth;
+ maxDepth = (maxDepth == std::numeric_limits<float>::infinity ()) ? std::numeric_limits<float>::max () : maxDepth;
+
+ if (minDepth < maxDepth)
+ return;
+
+ std::swap (minDepth, maxDepth);
+}
+
+
+// --------------------------------------------------------------------------
+
+
+struct ContactFilter2D : public b2ContactFilter
+{
+ virtual bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB)
+ {
+ Collider2D* colliderA = reinterpret_cast<Collider2D*>(fixtureA->GetUserData ());
+ Collider2D* colliderB = reinterpret_cast<Collider2D*>(fixtureB->GetUserData ());
+
+ if (!colliderA->GetEnabled () || !colliderB->GetEnabled ())
+ return false;
+
+ // Fetch collider layers.
+ const int layerA = colliderA->GetGameObject ().GetLayer();
+ const int layerB = colliderB->GetGameObject ().GetLayer();
+
+ // Decide on the contact based upon the layer collision mask.
+ return GetPhysics2DSettings().GetLayerCollisionMask(layerA) & (1<<layerB);
+ }
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct ColliderHitsByDepthComparitor
+{
+ inline bool operator()(const Collider2D* a, const Collider2D* b)
+ {
+ return a->GetComponent(Transform).GetPosition ().z < b->GetComponent(Transform).GetPosition ().z;
+ }
+
+ inline int CompareDepth(const Collider2D* a, const Collider2D* b)
+ {
+ const float depthA = a->GetComponent(Transform).GetPosition ().z;
+ const float depthB = b->GetComponent(Transform).GetPosition ().z;
+
+ if (depthA < depthB) return -1;
+ if (depthA > depthB) return 1;
+ return 0;
+ }
+} m_CompareDepth;
+
+
+// --------------------------------------------------------------------------
+
+
+struct RayHitsByDepthComparitor
+{
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.collider->GetComponent(Transform).GetPosition ().z < b.collider->GetComponent(Transform).GetPosition ().z;
+ }
+};
+
+struct RayHitsByInverseDepthComparitor
+{
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.collider->GetComponent(Transform).GetPosition ().z > b.collider->GetComponent(Transform).GetPosition ().z;
+ }
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class Raycast2DQuery : public b2RayCastCallback
+{
+public:
+ Raycast2DQuery(const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>& outHits)
+ : m_PointA(pointA)
+ , m_PointB(pointB)
+ , m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+ }
+
+ int RunQuery ()
+ {
+ // Calculate if the ray has zero-length or not.
+ const bool rayHasMagnitude = SqrMagnitude (m_PointB - m_PointA) > b2_epsilon * b2_epsilon;
+
+ // Perform a discrete check at the start-point (Box2D does not discover these for a ray-cast).
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().OverlapPointAll (m_PointA, m_LayerMask, m_MinDepth, m_MaxDepth, &colliderHits) > 0)
+ {
+ const Vector2f collisionNormal = rayHasMagnitude ? NormalizeFast (m_PointA-m_PointB) : Vector2f::zero;
+ for (dynamic_array<Collider2D*>::iterator colliderItr = colliderHits.begin (); colliderItr != colliderHits.end (); ++colliderItr)
+ {
+ RaycastHit2D rayHit;
+ rayHit.collider = *colliderItr;
+ rayHit.point = m_PointA;
+ rayHit.normal = collisionNormal;
+ rayHit.fraction = 0.0f;
+ m_Hits.push_back (rayHit);
+ }
+ }
+
+ if (rayHasMagnitude)
+ {
+ // Perform the ray-cast.
+ GetPhysics2DWorld ()->RayCast (this, b2Vec2(m_PointA.x, m_PointA.y), b2Vec2(m_PointB.x, m_PointB.y));
+
+ // Sort the hits by fraction.
+ std::sort (m_Hits.begin(), m_Hits.end(), RaycastHitsByFractionComparitor());
+ }
+
+ return m_Hits.size ();
+ }
+
+ virtual float32 ReportFixture (b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float32 fraction)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return -1.0f;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return -1.0f; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return -1.0f; // ignore and continue;
+
+ RaycastHit2D hit;
+ hit.point.Set (point.x, point.y);
+ hit.normal.Set (normal.x, normal.y);
+ hit.fraction = fraction;
+ hit.collider = collider;
+
+ // For chain colliders, Box2D will report all individual segments that are hit; also similar situation
+ // when using composite convex colliders. Try searching for a hit with same Collider2D, and replace info
+ // if closer hit. Unfortunately, this is is O(n).
+ for (size_t i = 0, n = m_Hits.size(); i != n; ++i)
+ {
+ if (m_Hits[i].collider == collider)
+ {
+ if (fraction < m_Hits[i].fraction)
+ m_Hits[i] = hit;
+
+ return 1.0f; // continue
+ }
+ }
+
+ // Add new hit
+ m_Hits.push_back (hit);
+
+ return 1.0f; // continue
+ }
+
+private:
+ struct RaycastHitsByFractionComparitor
+ {
+ inline bool operator()(const RaycastHit2D& a, const RaycastHit2D& b)
+ {
+ return a.fraction < b.fraction;
+ }
+ };
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ Vector2f m_PointA;
+ Vector2f m_PointB;
+ dynamic_array<RaycastHit2D>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapPointQuery2D : public b2QueryCallback
+{
+public:
+ OverlapPointQuery2D(const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ m_Point.Set (point.x, point.y);
+ }
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ b2AABB aabb;
+ aabb.lowerBound = aabb.upperBound = m_Point;
+ GetPhysics2DWorld ()->QueryAABB (this, aabb);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ // Test point against fixture.
+ if (!fixture->TestPoint (m_Point))
+ return true;
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ b2Vec2 m_Point;
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ dynamic_array<Collider2D*>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapCircleQuery2D : public b2QueryCallback
+{
+public:
+ OverlapCircleQuery2D(const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ // Extremely small radii indicates a point query.
+ if (radius < 0.00001f)
+ {
+ m_Point.Set (point.x, point.y);
+ m_AABB.lowerBound = m_AABB.upperBound = m_Point;
+ m_PointQuery = true;
+ }
+ else
+ {
+ // Calculate circle shape and its AABB.
+ m_CircleShape.m_p.Set (point.x, point.y);
+ m_CircleShape.m_radius = radius;
+ m_QueryTransform.SetIdentity ();
+ m_CircleShape.ComputeAABB( &m_AABB, m_QueryTransform, 0 );
+ m_PointQuery = false;
+ }
+ }
+
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ GetPhysics2DWorld ()->QueryAABB (this, m_AABB);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ if (m_PointQuery)
+ {
+ // Test point against fixture.
+ if (!fixture->TestPoint (m_Point))
+ return true;
+ }
+ else
+ {
+ // Test circle against fixture.
+ if ( !b2TestOverlap( &m_CircleShape, 0, fixture->GetShape (), 0, m_QueryTransform, fixture->GetBody ()->GetTransform () ) )
+ return true;
+ }
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ b2Vec2 m_Point;
+ b2CircleShape m_CircleShape;
+ b2AABB m_AABB;
+ b2Transform m_QueryTransform;
+ dynamic_array<Collider2D*>& m_Hits;
+ bool m_PointQuery;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+class OverlapAreaQuery2D : public b2QueryCallback
+{
+public:
+ OverlapAreaQuery2D(Vector2f pointA, Vector2f pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>& outHits)
+ : m_LayerMask(layerMask)
+ , m_MinDepth(minDepth)
+ , m_MaxDepth(maxDepth)
+ , m_Hits(outHits)
+ {
+ NormalizeDepthRange(m_MinDepth, m_MaxDepth);
+
+ // Normalize the points.
+ if (pointA.x > pointB.x)
+ std::swap (pointA.x, pointB.x);
+ if (pointA.y > pointB.y)
+ std::swap (pointA.y, pointB.y);
+
+ // Calculate the AABB.
+ m_PolygonAABB.lowerBound.Set (pointA.x, pointA.y);
+ m_PolygonAABB.upperBound.Set (pointB.x, pointB.y);
+
+ // Calculate polygon shape.
+ b2Vec2 verts[4];
+ verts[0].Set( pointA.x, pointA.y );
+ verts[1].Set( pointB.x, pointA.y );
+ verts[2].Set( pointB.x, pointB.y );
+ verts[3].Set( pointA.x, pointB.y );
+ m_PolygonShape.Set( verts, 4 );
+
+ m_QueryTransform.SetIdentity ();
+ }
+
+ int RunQuery ()
+ {
+ // Reset results.
+ m_Hits.clear();
+
+ // Finish if AABB is invalid.
+ if (!m_PolygonAABB.IsValid())
+ return 0;
+
+ GetPhysics2DWorld ()->QueryAABB (this, m_PolygonAABB);
+
+ // Sort the hits by depth.
+ std::sort (m_Hits.begin(), m_Hits.end(), ColliderHitsByDepthComparitor());
+
+ return m_Hits.size ();
+ }
+
+ virtual bool ReportFixture (b2Fixture* fixture)
+ {
+ // Handle whether ray-casts are hitting triggers or not.
+ if (fixture->IsSensor () && !GetPhysics2DSettings ().GetRaycastsHitTriggers ())
+ return true;
+
+ // Ignore if not in the selected fixture layer.
+ if (!CheckFixtureLayer (fixture, m_LayerMask))
+ return true; // ignore and continue
+
+ // Ignore if not in the selected depth-range.
+ Collider2D* collider = reinterpret_cast<Collider2D*>(fixture->GetUserData());
+ if (!CheckColliderDepth (collider, m_MinDepth, m_MaxDepth))
+ return true; // ignore and continue;
+
+ // Ignore if we've already selected this collider and it has a higher depth.
+ for (size_t i = 0; i != m_Hits.size (); ++i)
+ {
+ if (m_Hits[i] == collider)
+ {
+ if (m_CompareDepth.CompareDepth (m_Hits[i], collider) == 1)
+ m_Hits[i] = collider;
+ return true;
+ }
+ }
+
+ // Test polygon against fixture.
+ if ( !b2TestOverlap( &m_PolygonShape, 0, fixture->GetShape (), 0, m_QueryTransform, fixture->GetBody ()->GetTransform () ) )
+ return true;
+
+ // Add new hit
+ m_Hits.push_back (collider);
+
+ return true;
+ }
+
+private:
+ int m_LayerMask;
+ float m_MinDepth;
+ float m_MaxDepth;
+ b2PolygonShape m_PolygonShape;
+ b2AABB m_PolygonAABB;
+ b2Transform m_QueryTransform;
+ dynamic_array<Collider2D*>& m_Hits;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct Physics2DState
+{
+ Physics2DState() :
+ m_PhysicsManager(NULL),
+ m_PhysicsWorld(NULL),
+ m_PhysicsGroundBody(NULL) { }
+
+ void Initialize();
+ void Cleanup();
+
+ Physics2DManager* m_PhysicsManager;
+ b2World* m_PhysicsWorld;
+ b2Body* m_PhysicsGroundBody;
+ CollisionListener2D m_Collisions;
+ ContactFilter2D m_ContactFilter;
+};
+
+static Physics2DState g_Physics2DState;
+
+
+// --------------------------------------------------------------------------
+
+void Physics2DState::Initialize()
+{
+ Assert (m_PhysicsManager == NULL);
+ Assert (m_PhysicsWorld == NULL);
+
+ m_PhysicsManager = new Physics2DManager ();
+ SetIPhysics2D (m_PhysicsManager);
+
+ // Initialize the Box2D physics world.
+ b2Vec2 gravity(0.0f, -9.81f);
+ m_PhysicsWorld = new b2World (gravity);
+ m_PhysicsWorld->SetContactListener (&m_Collisions);
+ m_PhysicsWorld->SetContactFilter (&m_ContactFilter);
+
+ // Initialize the static ground-body.
+ b2BodyDef groundBodyDef;
+ m_PhysicsGroundBody = GetPhysics2DWorld()->CreateBody (&groundBodyDef);
+
+ // Register physics updates.
+ REGISTER_PLAYERLOOP_CALL (Physics2DFixedUpdate, GetPhysics2DManager().FixedUpdate());
+ REGISTER_PLAYERLOOP_CALL (Physics2DUpdate, GetPhysics2DManager().DynamicUpdate());
+ REGISTER_PLAYERLOOP_CALL (Physics2DResetInterpolatedTransformPosition, GetPhysics2DManager().ResetInterpolations());
+}
+
+
+void Physics2DState::Cleanup()
+{
+ delete m_PhysicsWorld;
+ m_PhysicsWorld = NULL;
+
+ delete m_PhysicsManager;
+ m_PhysicsManager = NULL;
+ SetIPhysics2D (NULL);
+}
+
+
+void InitializePhysics2DManager ()
+{
+ g_Physics2DState.Initialize ();
+}
+
+
+void CleanupPhysics2DManager ()
+{
+ g_Physics2DState.Cleanup ();
+}
+
+
+b2World* GetPhysics2DWorld ()
+{
+ Assert (g_Physics2DState.m_PhysicsWorld);
+ return g_Physics2DState.m_PhysicsWorld;
+}
+
+
+b2Body* GetPhysicsGroundBody ()
+{
+ Assert (g_Physics2DState.m_PhysicsGroundBody);
+ return g_Physics2DState.m_PhysicsGroundBody;
+}
+
+
+Physics2DManager& GetPhysics2DManager()
+{
+ Assert (g_Physics2DState.m_PhysicsManager);
+ return *g_Physics2DState.m_PhysicsManager;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+Physics2DManager::Physics2DManager()
+ : m_RigidbodyTransformMessageEnabled(true)
+{
+ Object::FindAllDerivedClasses (ClassID (Collider2D), &m_AllCollider2DTypes);
+}
+
+
+void Physics2DManager::FixedUpdate()
+{
+ PROFILER_AUTO(gPhysics2DFixedUpdateProfile, NULL)
+
+ // Gather interpolation info.
+ {
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ // Store interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin();i!=m_InterpolatedBodies.end();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ i->disabled = 0;
+
+ if ( rigidBody->GetBody() == NULL )
+ continue;
+
+ if (rigidBody->GetInterpolation() == kInterpolate2D)
+ {
+ i->position = rigidBody->GetBodyPosition();
+ i->rotation = rigidBody->GetBodyRotation();
+ }
+ }
+ }
+
+ // simulate
+ {
+ PROFILER_AUTO(gPhysics2DSimProfile, NULL)
+
+ const Physics2DSettings& settings = GetPhysics2DSettings();
+ g_Physics2DState.m_PhysicsWorld->Step (GetTimeManager().GetFixedDeltaTime(), settings.GetVelocityIterations(), settings.GetPositionIterations());
+ }
+
+ // update data back from simulation
+ {
+ PROFILER_AUTO(gPhysics2DUpdateTransformsProfile, NULL)
+
+ // Disable rigid body, collider & joint transform changed message handler.
+ // Turns off setting the pose while we are fetching the state.
+ SetTransformMessageEnabled (false);
+
+ // Update position / rotation of all rigid bodies
+ b2Body* body = GetPhysics2DWorld()->GetBodyList();
+ while (body != NULL)
+ {
+ Rigidbody2D* rb = (Rigidbody2D*)body->GetUserData();
+ if (rb != NULL && body->GetType() != b2_staticBody && body->IsAwake())
+ {
+ GameObject& go = rb->GetGameObject();
+ Transform& transform = go.GetComponent (Transform);
+
+ // Calculate new position.
+ const b2Vec2& pos2 = body->GetPosition();
+ Vector3f pos3 = transform.GetPosition();
+ pos3.x = pos2.x;
+ pos3.y = pos2.y;
+
+ // Calculate new rotation.
+ Vector3f localEuler = QuaternionToEuler (transform.GetLocalRotation ());
+ localEuler.z = body->GetAngle ();
+
+ // Update position and rotation.
+ transform.SetPositionAndRotation (pos3, EulerToQuaternion(localEuler));
+ }
+
+ body = body->GetNext();
+ }
+
+ // Enable transform change notifications back.
+ SetTransformMessageEnabled (true);
+ }
+
+ // do script callbacks
+ {
+ PROFILER_AUTO(gPhysics2DCallbacksProfile, NULL)
+
+ const bool oldDisableDestruction = GetDisableImmediateDestruction();
+ SetDisableImmediateDestruction(true);
+
+ // report collisions and triggers
+ g_Physics2DState.m_Collisions.ReportCollisions();
+
+ SetDisableImmediateDestruction(oldDisableDestruction);
+ }
+}
+
+
+void Physics2DManager::DynamicUpdate()
+{
+ PROFILER_AUTO(gPhysics2DDynamicUpdateProfile, NULL);
+
+ {
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ // Disable rigid body / collider transform changed message handler.
+ // Turns off setting the pose while we are fetching the state.
+ SetTransformMessageEnabled (false);
+
+ // Also disable rigidbody (2D) transform changed message, otherwise interpolation will affect physics results.
+ // This is not done in SetTransformMessageEnabled, as we need the rigidbody (2D) messages in the physics fixed update
+ // so kinematic child rigid-bodies (2D) are moved with their parents.
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody2D), kTransformChanged.messageID, false);
+
+ // Interpolation time is [0...1] between the two steps
+ // Extrapolation time the delta time since the last fixed step
+ const float dynamicTime = GetTimeManager().GetCurTime();
+ const float step = GetTimeManager().GetFixedDeltaTime();
+ const float fixedTime = GetTimeManager().GetFixedTime();
+
+ // Update interpolated position
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin ();i!=m_InterpolatedBodies.end ();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ const RigidbodyInterpolation2D interpolation = rigidBody->GetInterpolation ();
+
+ // Ignore if disabled, no interpolation is specified or the body is sleeping.
+ if (i->disabled || interpolation == kNoInterpolation2D || rigidBody->IsSleeping ())
+ continue;
+
+ // Interpolate between this physics and last physics frame.
+ if (interpolation == kInterpolate2D)
+ {
+ const float interpolationTime = clamp01 ((dynamicTime - fixedTime) / step);
+
+ // Interpolate position.
+ const Vector3f position = Lerp (i->position, rigidBody->GetBodyPosition(), interpolationTime);
+
+ // Interpolate rotation.
+ const Quaternionf rotation = Slerp (i->rotation, rigidBody->GetBodyRotation (), interpolationTime);
+
+ // Update position/rotation.
+ rigidBody->GetComponent(Transform).SetPositionAndRotationSafe (position, rotation);
+ continue;
+ }
+
+ // Extrapolate current position using velocity.
+ else if (interpolation == kExtrapolate2D)
+ {
+ const float extrapolationTime = dynamicTime - fixedTime;
+
+ // Interpolate position.
+ const Vector2f linearVelocity2D = rigidBody->GetVelocity();
+ const Vector3f linearVelocity3D(linearVelocity2D.x * extrapolationTime, linearVelocity2D.y * extrapolationTime, 0.0f);
+ const Vector3f position = rigidBody->GetBodyPosition () + linearVelocity3D;
+
+ // Interpolate rotation.
+ const float angularVelocity2D = rigidBody->GetAngularVelocity ();
+ const Quaternionf rotation = CompareApproximately (angularVelocity2D, 0.0f) ? AngularVelocityToQuaternion(Vector3f(0.0f, 0.0f, angularVelocity2D), extrapolationTime) * rigidBody->GetBodyRotation() : rigidBody->GetBodyRotation();
+
+ // Update position/rotation.
+ rigidBody->GetComponent(Transform).SetPositionAndRotationSafe (position, rotation);
+ continue;
+ }
+ }
+
+ // Re-enable rigidbody (2D) transform changed messages.
+ GameObject::GetMessageHandler ().SetMessageEnabled (ClassID(Rigidbody2D), kTransformChanged.messageID, true);
+
+ // Enable transform change notifications back.
+ SetTransformMessageEnabled (true);
+ }
+}
+
+
+void Physics2DManager::ResetInterpolations ()
+{
+ PROFILER_AUTO(gPhysics2DInterpolationsProfile, NULL)
+
+ for (InterpolatedBodiesIterator i=m_InterpolatedBodies.begin ();i!=m_InterpolatedBodies.end ();++i)
+ {
+ Rigidbody2D* rigidBody = i->body;
+ if (rigidBody->GetBody() == NULL || rigidBody->IsSleeping ())
+ continue;
+
+ Transform& transform = rigidBody->GetComponent(Transform);
+
+ Vector3f pos = rigidBody->GetBodyPosition();
+ Quaternionf rot = rigidBody->GetBodyRotation();
+ transform.SetPositionAndRotationSafeWithoutNotification (pos, rot);
+ }
+}
+
+
+int Physics2DManager::Linecast (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gLinecast2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ Raycast2DQuery query (pointA, pointB, layerMask, minDepth, maxDepth, raycastHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = raycastHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::LinecastAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gLinecastAll2DProfile, NULL)
+
+ Raycast2DQuery query (pointA, pointB, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::Raycast (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gRaycast2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ // Calculate destination point.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ Vector2f normalizedDirection = NormalizeFast (direction);
+ const Vector2f pointB = origin + (normalizedDirection * (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance));
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ Raycast2DQuery query (origin, pointB, layerMask, minDepth, maxDepth, raycastHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ {
+ RaycastHit2D& hit = raycastHits[index];
+ if (isInfiniteDistance)
+ hit.fraction *= PHYSICS_2D_RAYCAST_DISTANCE;
+
+ *(outHits++) = hit;
+ }
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::RaycastAll (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gRaycastAll2DProfile, NULL)
+
+ // Calculate points.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ Vector2f normalizedDirection = NormalizeFast (direction);
+ const Vector2f pointB = origin + (normalizedDirection * (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance));
+
+ Raycast2DQuery query (origin, pointB, layerMask, minDepth, maxDepth, *outHits);
+ const int resultCount = query.RunQuery ();
+
+ // Finish if not infinite distance.
+ if (!isInfiniteDistance || resultCount == 0)
+ return resultCount;
+
+ // Change fraction to distance.
+ for (dynamic_array<RaycastHit2D>::iterator hitItr = outHits->begin(); hitItr != outHits->end(); ++hitItr)
+ hitItr->fraction *= PHYSICS_2D_RAYCAST_DISTANCE;
+
+ return resultCount;
+}
+
+
+int Physics2DManager::GetRayIntersection(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, RaycastHit2D* outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gGetRayIntersection2DProfile, NULL)
+
+ // Finish if no available hits capacity.
+ if (outHitsSize == 0)
+ return 0;
+
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ const int resultCount = GetRayIntersectionAll (origin, direction, distance, layerMask, &raycastHits);
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = raycastHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::GetRayIntersectionAll(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, dynamic_array<RaycastHit2D>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gGetRayIntersectionAll2DProfile, NULL)
+
+ // Clear hits.
+ outHits->clear ();
+
+ // Set ray.
+ Ray ray;
+ ray.SetOrigin (origin);
+ ray.SetApproxDirection (direction);
+
+ // Calculate destination point.
+ const bool isInfiniteDistance = distance == std::numeric_limits<float>::infinity();
+ const float rayDistanceScale = isInfiniteDistance ? 1.0f : 1.0f / distance;
+ const Vector3f destination = ray.GetPoint (isInfiniteDistance ? PHYSICS_2D_RAYCAST_DISTANCE : distance);
+
+ // If the ray is parallel to the X/Y plane then no intersections are possible.
+ if (CompareApproximately (origin.z, destination.z))
+ return 0;
+
+ // Calculate 2D start/end points.
+ const Vector2f pointA = Vector2f(origin.x, origin.y);
+ const Vector2f pointB = Vector2f(destination.x, destination.y);
+
+ // Find all the colliders that hit somewhere along the ray.
+ // NOTE:- These will all be unique colliders (not duplicates).
+ dynamic_array<RaycastHit2D> potentialHits(kMemTempAlloc);
+ if (LinecastAll (pointA, pointB, layerMask, origin.z, destination.z, &potentialHits) == 0)
+ return 0;
+
+ // Sort the hits by depth.
+ const bool isForwardDepth = origin.z < direction.z;
+ if (isForwardDepth)
+ std::sort (potentialHits.begin(), potentialHits.end(), RayHitsByDepthComparitor());
+ else
+ std::sort (potentialHits.begin(), potentialHits.end(), RayHitsByInverseDepthComparitor());
+
+ // Calculate the plane normal.
+ const Vector3f planeNormal(0.0f, 0.0f, isForwardDepth ? 1.0f : -1.0f);
+
+ // Iterate all hits and check collider intersections.
+ for (dynamic_array<RaycastHit2D>::iterator hitItr = potentialHits.begin (); hitItr != potentialHits.end (); ++hitItr)
+ {
+ // Fetch the hit.
+ RaycastHit2D& hit = *hitItr;
+
+ // Fetch the collider depth.
+ const Collider2D* collider = hit.collider;
+ const float depth = collider->GetGameObject ().GetComponent (Transform).GetPosition ().z;
+
+ // Configure a collider plane.
+ Plane colliderPlane;
+ colliderPlane.SetNormalAndPosition (planeNormal, Vector3f(0.0f, 0.0f, depth));
+
+ // Test ray for intersection position.
+ float intersectionDistance;
+ if (!IntersectRayPlane (ray, colliderPlane, &intersectionDistance))
+ continue;
+
+ // Test if the intersection point overlaps the collider.
+ const Vector3f intersectionPoint3 = ray.GetPoint (intersectionDistance);
+ const Vector2f intersectionPoint2 = Vector2f(intersectionPoint3.x, intersectionPoint3.y);
+ if (!collider->OverlapPoint (intersectionPoint2))
+ continue;
+
+ // Update the hit with the 3D intersection details.
+ // NOTE: We leave the normal in 2D space as we can't use the plane normal.
+ hit.point = intersectionPoint2;
+ hit.fraction = intersectionDistance * rayDistanceScale;
+
+ // Add hit result.
+ outHits->push_back (hit);
+ }
+
+ return outHits->size ();
+}
+
+
+int Physics2DManager::OverlapPoint (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapPoint2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapPointQuery2D query (point, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapPointAll (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapPointAll2DProfile, NULL)
+
+ OverlapPointQuery2D query (point, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::OverlapCircle (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapCircle2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapCircleQuery2D query (point, radius, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapCircleAll (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapCircleAll2DProfile, NULL)
+
+ OverlapCircleQuery2D query (point, radius, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+
+int Physics2DManager::OverlapArea (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapArea2DProfile, NULL)
+
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ OverlapAreaQuery2D query (pointA, pointB, layerMask, minDepth, maxDepth, colliderHits);
+ const int resultCount = query.RunQuery ();
+
+ // Transfer the first n-results.
+ const int allowedResultCount = std::min (resultCount, outHitsSize);
+ for (int index = 0; index < allowedResultCount; ++index)
+ *(outHits++) = colliderHits[index];
+
+ return allowedResultCount;
+}
+
+
+int Physics2DManager::OverlapAreaAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits)
+{
+ Assert(g_Physics2DState.m_PhysicsWorld);
+ Assert(outHits);
+ PROFILER_AUTO(gOverlapAreaAll2DProfile, NULL)
+
+ OverlapAreaQuery2D query (pointA, pointB, layerMask, minDepth, maxDepth, *outHits);
+ return query.RunQuery ();
+}
+
+void Physics2DManager::InvalidateColliderCollisions (Collider2D* collider)
+{
+ g_Physics2DState.m_Collisions.InvalidateColliderCollisions (collider);
+}
+
+
+void Physics2DManager::DestroyColliderCollisions (Collider2D* collider)
+{
+ g_Physics2DState.m_Collisions.DestroyColliderCollisions (collider);
+}
+
+
+#if ENABLE_PROFILER
+void Physics2DManager::GetProfilerStats (Physics2DStats& stats)
+{
+ // Fetch the physics world.
+ b2World* world = g_Physics2DState.m_PhysicsWorld;
+
+ // Cannot populate stats without a world.
+ if (world == NULL)
+ return;
+
+ // Calculate body metrics.
+ int dynamicBodyCount = 0;
+ int kinematicBodyCount = 0;
+ int activeBodyCount = 0;
+ int sleepingBodyCount = 0;
+ int discreteBodyCount = 0;
+ int continuousBodyCount = 0;
+ int activeColliderShapesCount = 0;
+ int sleepingColliderShapesCount = 0;
+
+ for (b2Body* body = world->GetBodyList (); body; body = body->GetNext ())
+ {
+ // Check body type.
+ const b2BodyType bodyType = body->GetType ();
+ if (bodyType == b2_staticBody)
+ continue;
+ else if (bodyType == b2_dynamicBody)
+ dynamicBodyCount++;
+ else if (bodyType == b2_kinematicBody)
+ kinematicBodyCount++;
+
+ // Check sleep state.
+ if (body->IsAwake ())
+ {
+ activeBodyCount++;
+ activeColliderShapesCount += body->GetFixtureCount ();
+ }
+ else
+ {
+ sleepingBodyCount++;
+ sleepingColliderShapesCount += body->GetFixtureCount ();
+ }
+
+ // Check CCD state.
+ if (body->IsBullet ())
+ continuousBodyCount++;
+ else
+ discreteBodyCount++;
+ }
+
+ // Populate profile counts.
+ stats.m_TotalBodyCount = world->GetBodyCount () - 1; // Ignore the hidden static ground-body.
+ stats.m_ActiveBodyCount = activeBodyCount;
+ stats.m_SleepingBodyCount = sleepingBodyCount;
+ stats.m_DynamicBodyCount = dynamicBodyCount;
+ stats.m_KinematicBodyCount = kinematicBodyCount;
+ stats.m_DiscreteBodyCount = discreteBodyCount;
+ stats.m_ContinuousBodyCount = continuousBodyCount;
+ stats.m_ActiveColliderShapesCount = activeColliderShapesCount;
+ stats.m_SleepingColliderShapesCount = sleepingColliderShapesCount;
+ stats.m_JointCount = world->GetJointCount ();
+ stats.m_ContactCount = world->GetContactCount ();
+
+ // Populate profile times.
+ const b2Profile& timeProfile = world->GetProfile ();
+ const float millisecondUpscale = 1000000.0f;
+ stats.m_StepTime = (int)(timeProfile.step * millisecondUpscale);
+ stats.m_CollideTime = (int)(timeProfile.collide * millisecondUpscale);
+ stats.m_SolveTime = (int)(timeProfile.solve * millisecondUpscale);
+ stats.m_SolveInitialization = (int)(timeProfile.solveInit * millisecondUpscale);
+ stats.m_SolveVelocity = (int)(timeProfile.solveVelocity * millisecondUpscale);
+ stats.m_SolvePosition = (int)(timeProfile.solvePosition * millisecondUpscale);
+ stats.m_SolveBroadphase = (int)(timeProfile.broadphase * millisecondUpscale);
+ stats.m_SolveTimeOfImpact = (int)(timeProfile.solveTOI * millisecondUpscale);
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+
+
+void Physics2DManager::SetTransformMessageEnabled (const bool enable)
+{
+ for (size_t i = 0, n = m_AllCollider2DTypes.size(); i < n; ++i)
+ GameObject::GetMessageHandler ().SetMessageEnabled (m_AllCollider2DTypes[i], kTransformChanged.messageID, enable);
+
+ m_RigidbodyTransformMessageEnabled = enable;
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DManager.h b/Runtime/Physics2D/Physics2DManager.h
new file mode 100644
index 0000000..cef3917
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DManager.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Interfaces/IPhysics2D.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Math/Quaternion.h"
+#include "Runtime/Scripting/Backend/ScriptingTypes.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Utilities/LinkedList.h"
+
+class Rigidbody2D;
+class Collider2D;
+class b2World;
+class b2Body;
+class Ray;
+struct Physics2DStats;
+
+#define PHYSICS_2D_RAYCAST_DISTANCE (1e+5f)
+#define PHYSICS_2D_LARGE_RANGE_CLAMP (1e+6f)
+#define PHYSICS_2D_SMALL_RANGE_CLAMP (0.0001f)
+
+// --------------------------------------------------------------------------
+
+
+struct Rigidbody2DInterpolationInfo : public ListElement
+{
+ Vector3f position;
+ Quaternionf rotation;
+ Rigidbody2D* body;
+ int disabled;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+struct RaycastHit2D
+{
+ Vector2f point;
+ Vector2f normal;
+ float fraction;
+ Collider2D* collider;
+};
+
+// --------------------------------------------------------------------------
+
+
+class Physics2DManager : public IPhysics2D
+{
+private:
+ typedef List<Rigidbody2DInterpolationInfo> InterpolatedBodiesList;
+ typedef InterpolatedBodiesList::iterator InterpolatedBodiesIterator;
+
+public:
+ Physics2DManager();
+ // ~Physics2DManager() // declared-by-macro
+
+ // IPhysics2D interface
+ virtual void FixedUpdate ();
+ virtual void DynamicUpdate ();
+ virtual void ResetInterpolations ();
+
+ // 2D line-casts.
+ int Linecast (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize);
+ int LinecastAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits);
+
+ // 2D ray-casts.
+ int Raycast (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, RaycastHit2D* outHits, const int outHitsSize);
+ int RaycastAll (const Vector2f& origin, const Vector2f& direction, const float distance, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<RaycastHit2D>* outHits);
+
+ // 3D ray-intersections.
+ int GetRayIntersection(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, RaycastHit2D* outHits, const int outHitsSize);
+ int GetRayIntersectionAll(const Vector3f& origin, const Vector3f& direction, const float distance, const int layerMask, dynamic_array<RaycastHit2D>* outHits);
+
+ // 2D geometry overlaps.
+ int OverlapPoint (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize);
+ int OverlapPointAll (const Vector2f& point, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits);
+ int OverlapCircle (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize);
+ int OverlapCircleAll (const Vector2f& point, const float radius, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits);
+ int OverlapArea (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, Collider2D** outHits, const int outHitsSize);
+ int OverlapAreaAll (const Vector2f& pointA, const Vector2f& pointB, const int layerMask, const float minDepth, const float maxDepth, dynamic_array<Collider2D*>* outHits);
+
+ void InvalidateColliderCollisions (Collider2D* collider);
+ void DestroyColliderCollisions (Collider2D* collider);
+
+ inline bool IsTransformMessageEnabled() const { return m_RigidbodyTransformMessageEnabled; }
+ inline List<Rigidbody2DInterpolationInfo>& GetInterpolatedBodies() { return m_InterpolatedBodies; }
+
+#if ENABLE_PROFILER
+ virtual void GetProfilerStats (Physics2DStats& stats);
+#endif
+
+private:
+ void SetTransformMessageEnabled (const bool enable);
+
+private:
+ std::vector<SInt32> m_AllCollider2DTypes;
+ bool m_RigidbodyTransformMessageEnabled;
+ InterpolatedBodiesList m_InterpolatedBodies;
+};
+
+
+// --------------------------------------------------------------------------
+
+
+void InitializePhysics2DManager ();
+void CleanupPhysics2DManager ();
+b2World* GetPhysics2DWorld ();
+b2Body* GetPhysicsGroundBody ();
+Physics2DManager& GetPhysics2DManager ();
+
+#endif
diff --git a/Runtime/Physics2D/Physics2DMaterial.cpp b/Runtime/Physics2D/Physics2DMaterial.cpp
new file mode 100644
index 0000000..ff47e78
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DMaterial.cpp
@@ -0,0 +1,65 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+IMPLEMENT_CLASS (PhysicsMaterial2D)
+IMPLEMENT_OBJECT_SERIALIZE (PhysicsMaterial2D)
+
+
+// --------------------------------------------------------------------------
+
+
+PhysicsMaterial2D::PhysicsMaterial2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+PhysicsMaterial2D::~PhysicsMaterial2D ()
+{
+}
+
+
+void PhysicsMaterial2D::Reset ()
+{
+ Super::Reset();
+ m_Friction = 0.4f;
+ m_Bounciness = 0.0f;
+}
+
+
+void PhysicsMaterial2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Friction = clamp(m_Friction, 0.0f, 100000.0f);
+ m_Bounciness = clamp(m_Bounciness, 0.0f, 1.0f);
+}
+
+
+template<class TransferFunction>
+void PhysicsMaterial2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_Friction, "friction", kSimpleEditorMask);
+ transfer.Transfer (m_Bounciness, "bounciness", kSimpleEditorMask);
+}
+
+
+void PhysicsMaterial2D::SetFriction (float friction)
+{
+ m_Friction = clamp (friction, 0.0f, 100000.0f);
+ SetDirty ();
+}
+
+
+void PhysicsMaterial2D::SetBounciness (float bounce)
+{
+ m_Bounciness = clamp (bounce, 0.0f, 1.0f);
+ SetDirty ();
+}
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DMaterial.h b/Runtime/Physics2D/Physics2DMaterial.h
new file mode 100644
index 0000000..0130b3b
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DMaterial.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/NamedObject.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class PhysicsMaterial2D : public NamedObject
+{
+public:
+ REGISTER_DERIVED_CLASS (PhysicsMaterial2D, NamedObject)
+ DECLARE_OBJECT_SERIALIZE (PhysicsMaterial2D)
+
+ PhysicsMaterial2D (MemLabelId label, ObjectCreationMode mode);
+ // ~PhysicsMaterial2D (); declared-by-macro
+
+ virtual void Reset ();
+ virtual void CheckConsistency ();
+
+ float GetFriction () const { return m_Friction; }
+ void SetFriction (float friction);
+
+ float GetBounciness () const { return m_Bounciness; }
+ void SetBounciness (float bounce);
+
+private:
+ float m_Friction; ///< Friction. Range { 0.0, 100000.0 }
+ float m_Bounciness; ///< Bounciness. Range { 0.0, 1.0 }
+
+ PPtr<Object> m_Owner;
+};
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DModule.jam b/Runtime/Physics2D/Physics2DModule.jam
new file mode 100644
index 0000000..85ef5aa
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DModule.jam
@@ -0,0 +1,178 @@
+rule Physics2DModule_ReportCpp
+{
+ local physics2DSources =
+
+ Physics2DModule.jam
+
+ Collider2D.cpp
+ Collider2D.h
+ BoxCollider2D.cpp
+ BoxCollider2D.h
+ CircleCollider2D.cpp
+ CircleCollider2D.h
+ EdgeCollider2D.cpp
+ EdgeCollider2D.h
+ PolygonColliderBase2D.cpp
+ PolygonColliderBase2D.h
+ PolygonCollider2D.cpp
+ PolygonCollider2D.h
+ SpriteCollider2D.cpp
+ SpriteCollider2D.h
+ CollisionListener2D.cpp
+ CollisionListener2D.h
+ DistanceJoint2D.cpp
+ DistanceJoint2D.h
+ HingeJoint2D.cpp
+ HingeJoint2D.h
+ Joint2D.cpp
+ Joint2D.h
+ Physics2DManager.cpp
+ Physics2DManager.h
+ Physics2DMaterial.cpp
+ Physics2DMaterial.h
+ Physics2DModuleRegistration.cpp
+ Physics2DSettings.cpp
+ Physics2DSettings.h
+ Rigidbody2D.cpp
+ Rigidbody2D.h
+ JointDescriptions2D.h
+ SliderJoint2D.cpp
+ SliderJoint2D.h
+ SpringJoint2D.cpp
+ SpringJoint2D.h
+ ;
+
+ local box2DSources =
+
+ Box2D.h
+ Collision/b2BroadPhase.cpp
+ Collision/b2BroadPhase.h
+ Collision/b2CollideCircle.cpp
+ Collision/b2CollideEdge.cpp
+ Collision/b2CollidePolygon.cpp
+ Collision/b2Collision.cpp
+ Collision/b2Collision.h
+ Collision/b2Distance.cpp
+ Collision/b2Distance.h
+ Collision/b2DynamicTree.cpp
+ Collision/b2DynamicTree.h
+ Collision/b2TimeOfImpact.cpp
+ Collision/b2TimeOfImpact.h
+
+ Collision/Shapes/b2ChainShape.cpp
+ Collision/Shapes/b2ChainShape.h
+ Collision/Shapes/b2CircleShape.cpp
+ Collision/Shapes/b2CircleShape.h
+ Collision/Shapes/b2EdgeShape.cpp
+ Collision/Shapes/b2EdgeShape.h
+ Collision/Shapes/b2PolygonShape.cpp
+ Collision/Shapes/b2PolygonShape.h
+ Collision/Shapes/b2Shape.h
+
+ Common/b2BlockAllocator.cpp
+ Common/b2BlockAllocator.h
+ Common/b2Draw.cpp
+ Common/b2Draw.h
+ Common/b2GrowableStack.h
+ Common/b2Math.cpp
+ Common/b2Math.h
+ Common/b2Settings.cpp
+ Common/b2Settings.h
+ Common/b2StackAllocator.cpp
+ Common/b2StackAllocator.h
+ Common/b2Timer.cpp
+ Common/b2Timer.h
+
+ Dynamics/b2Body.cpp
+ Dynamics/b2Body.h
+ Dynamics/b2ContactManager.cpp
+ Dynamics/b2ContactManager.h
+ Dynamics/b2Fixture.cpp
+ Dynamics/b2Fixture.h
+ Dynamics/b2Island.cpp
+ Dynamics/b2Island.h
+ Dynamics/b2TimeStep.h
+ Dynamics/b2World.cpp
+ Dynamics/b2World.h
+ Dynamics/b2WorldCallbacks.cpp
+ Dynamics/b2WorldCallbacks.h
+
+ Dynamics/Contacts/b2ChainAndCircleContact.cpp
+ Dynamics/Contacts/b2ChainAndCircleContact.h
+ Dynamics/Contacts/b2ChainAndPolygonContact.cpp
+ Dynamics/Contacts/b2ChainAndPolygonContact.h
+ Dynamics/Contacts/b2CircleContact.cpp
+ Dynamics/Contacts/b2CircleContact.h
+ Dynamics/Contacts/b2Contact.cpp
+ Dynamics/Contacts/b2Contact.h
+ Dynamics/Contacts/b2ContactSolver.cpp
+ Dynamics/Contacts/b2ContactSolver.h
+ Dynamics/Contacts/b2EdgeAndCircleContact.cpp
+ Dynamics/Contacts/b2EdgeAndCircleContact.h
+ Dynamics/Contacts/b2EdgeAndPolygonContact.cpp
+ Dynamics/Contacts/b2EdgeAndPolygonContact.h
+ Dynamics/Contacts/b2PolygonAndCircleContact.cpp
+ Dynamics/Contacts/b2PolygonAndCircleContact.h
+ Dynamics/Contacts/b2PolygonContact.cpp
+ Dynamics/Contacts/b2PolygonContact.h
+
+ Dynamics/Joints/b2DistanceJoint.cpp
+ Dynamics/Joints/b2DistanceJoint.h
+ Dynamics/Joints/b2FrictionJoint.cpp
+ Dynamics/Joints/b2FrictionJoint.h
+ Dynamics/Joints/b2GearJoint.cpp
+ Dynamics/Joints/b2GearJoint.h
+ Dynamics/Joints/b2Joint.cpp
+ Dynamics/Joints/b2Joint.h
+ Dynamics/Joints/b2MotorJoint.cpp
+ Dynamics/Joints/b2MotorJoint.h
+ Dynamics/Joints/b2MouseJoint.cpp
+ Dynamics/Joints/b2MouseJoint.h
+ Dynamics/Joints/b2PrismaticJoint.cpp
+ Dynamics/Joints/b2PrismaticJoint.h
+ Dynamics/Joints/b2PulleyJoint.cpp
+ Dynamics/Joints/b2PulleyJoint.h
+ Dynamics/Joints/b2RevoluteJoint.cpp
+ Dynamics/Joints/b2RevoluteJoint.h
+ Dynamics/Joints/b2RopeJoint.cpp
+ Dynamics/Joints/b2RopeJoint.h
+ Dynamics/Joints/b2WeldJoint.cpp
+ Dynamics/Joints/b2WeldJoint.h
+ Dynamics/Joints/b2WheelJoint.cpp
+ Dynamics/Joints/b2WheelJoint.h
+
+ Rope/b2Rope.cpp
+ Rope/b2Rope.h
+ ;
+
+ local modulesources =
+ Runtime/Physics2D/$(physics2DSources)
+ External/Box2D/Box2D/$(box2DSources)
+ ;
+
+ return $(modulesources) ;
+}
+
+rule Physics2DModule_ReportTxt
+{
+ return
+ Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt
+ ;
+}
+
+rule Physics2DModule_ReportIncludes
+{
+ return
+ External/Box2D
+ Projects/PrecompiledHeaders
+ ;
+}
+
+rule Physics2DModule_Init
+{
+ OverrideModule Physics2D : GetModule_Cpp : byOverridingWithMethod : Physics2DModule_ReportCpp ;
+ OverrideModule Physics2D : GetModule_Txt : byOverridingWithMethod : Physics2DModule_ReportTxt ;
+ OverrideModule Physics2D : GetModule_Inc : byOverridingWithMethod : Physics2DModule_ReportIncludes ;
+}
+
+#RegisterModule Physics2D ;
diff --git a/Runtime/Physics2D/Physics2DModuleRegistration.cpp b/Runtime/Physics2D/Physics2DModuleRegistration.cpp
new file mode 100644
index 0000000..cd0b9b3
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DModuleRegistration.cpp
@@ -0,0 +1,53 @@
+#include "UnityPrefix.h"
+#include "Runtime/BaseClasses/ClassRegistration.h"
+#include "Runtime/Modules/ModuleRegistration.h"
+
+#if ENABLE_2D_PHYSICS
+
+static void RegisterPhysics2DClasses (ClassRegistrationContext& context)
+{
+ REGISTER_CLASS (Physics2DSettings)
+ REGISTER_CLASS (Rigidbody2D)
+
+ REGISTER_CLASS (Collider2D)
+ REGISTER_CLASS (CircleCollider2D)
+ REGISTER_CLASS (PolygonCollider2D)
+ REGISTER_CLASS (PolygonColliderBase2D)
+ #if ENABLE_SPRITECOLLIDER
+ REGISTER_CLASS (SpriteCollider2D)
+ #endif
+ REGISTER_CLASS (BoxCollider2D)
+ REGISTER_CLASS (EdgeCollider2D)
+
+ REGISTER_CLASS (Joint2D)
+ REGISTER_CLASS (SpringJoint2D)
+ REGISTER_CLASS (DistanceJoint2D)
+ REGISTER_CLASS (HingeJoint2D)
+ REGISTER_CLASS (SliderJoint2D)
+
+ REGISTER_CLASS (PhysicsMaterial2D)
+}
+
+
+#if ENABLE_MONO || UNITY_WINRT
+void ExportPhysics2DBindings();
+
+static void RegisterPhysics2DICallModule ()
+{
+ #if !INTERNAL_CALL_STRIPPING
+ ExportPhysics2DBindings ();
+ #endif
+}
+#endif
+
+extern "C" EXPORT_MODULE void RegisterModule_Physics2D()
+{
+ ModuleRegistrationInfo info;
+ info.registerClassesCallback = &RegisterPhysics2DClasses;
+ #if ENABLE_MONO || UNITY_WINRT
+ info.registerIcallsCallback = &RegisterPhysics2DICallModule;
+ #endif
+ RegisterModuleInfo(info);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DSettings.cpp b/Runtime/Physics2D/Physics2DSettings.cpp
new file mode 100644
index 0000000..9dd61d7
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DSettings.cpp
@@ -0,0 +1,167 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/BaseClasses/Tags.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+// --------------------------------------------------------------------------
+
+
+Physics2DSettings::Physics2DSettings (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+ m_VelocityIterations = 8;
+ m_PositionIterations = 3;
+ m_Gravity = Vector2f (0, -9.81f);
+ m_RaycastsHitTriggers = true;
+ m_LayerCollisionMatrix.resize_initialized (kNumLayers, 0xffffffff);
+}
+
+
+Physics2DSettings::~Physics2DSettings ()
+{
+}
+
+
+void Physics2DSettings::InitializeClass ()
+{
+ InitializePhysics2DManager ();
+}
+
+
+void Physics2DSettings::CleanupClass ()
+{
+ CleanupPhysics2DManager ();
+}
+
+
+void Physics2DSettings::Reset ()
+{
+ Super::Reset();
+ m_VelocityIterations = 8;
+ m_PositionIterations = 3;
+ m_Gravity = Vector2f (0, -9.81F);
+ m_LayerCollisionMatrix.resize_initialized (kNumLayers, 0xffffffff);
+}
+
+
+void Physics2DSettings::AwakeFromLoad(AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+ SetGravity (m_Gravity);
+}
+
+
+void Physics2DSettings::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_VelocityIterations = std::max (1, m_VelocityIterations);
+ m_PositionIterations = std::max (1, m_PositionIterations);
+}
+
+
+template<class TransferFunction>
+void Physics2DSettings::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+ TRANSFER (m_Gravity);
+ TRANSFER (m_DefaultMaterial);
+ TRANSFER (m_VelocityIterations);
+ TRANSFER (m_PositionIterations);
+ TRANSFER (m_RaycastsHitTriggers);
+ transfer.Align ();
+ transfer.Transfer (m_LayerCollisionMatrix, "m_LayerCollisionMatrix", kHideInEditorMask);
+}
+
+
+void Physics2DSettings::SetGravity (const Vector2f& value)
+{
+ m_Gravity = value;
+ SetDirty ();
+
+ GetPhysics2DWorld()->SetGravity(b2Vec2(m_Gravity.x, m_Gravity.y));
+
+ if (m_Gravity == Vector2f::zero)
+ return;
+
+ // Wake all dynamic bodies that have non-zero gravity-scale.
+ for (b2Body* body = GetPhysics2DWorld()->GetBodyList (); body != NULL; body = body->GetNext ())
+ {
+ if (body->GetType () == b2_dynamicBody && body->GetGravityScale () != 0.0f)
+ body->SetAwake (true);
+ }
+}
+
+
+void Physics2DSettings::SetVelocityIterations (const int velocityIterations)
+{
+ if ( velocityIterations == m_VelocityIterations )
+ return;
+
+ m_VelocityIterations = std::max (1,velocityIterations);
+ SetDirty ();
+}
+
+
+void Physics2DSettings::SetPositionIterations (const int positionIterations)
+{
+ if ( positionIterations == m_PositionIterations )
+ return;
+
+ m_PositionIterations = std::max (1,positionIterations);
+ SetDirty ();
+}
+
+
+void Physics2DSettings::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);
+
+ 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 Physics2DSettings::GetIgnoreCollision(int layer1, int layer2) const
+{
+ if (layer1 >= kNumLayers || layer2 >= kNumLayers)
+ {
+ ErrorString(Format("layer numbers must be between 0 and %d", kNumLayers));
+ return false;
+ }
+
+ bool collides = m_LayerCollisionMatrix[layer1] & (1<<layer2);
+ return !collides;
+}
+
+
+GET_MANAGER (Physics2DSettings)
+GET_MANAGER_PTR (Physics2DSettings)
+IMPLEMENT_CLASS_HAS_INIT (Physics2DSettings)
+IMPLEMENT_OBJECT_SERIALIZE (Physics2DSettings)
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/Physics2DSettings.h b/Runtime/Physics2D/Physics2DSettings.h
new file mode 100644
index 0000000..8a8b3ed
--- /dev/null
+++ b/Runtime/Physics2D/Physics2DSettings.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/GameManager.h"
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class Physics2DSettings : public GlobalGameManager
+{
+public:
+ Physics2DSettings (MemLabelId label, ObjectCreationMode mode);
+ // ~Physics2DSettings (); declared-by-macro
+
+ REGISTER_DERIVED_CLASS (Physics2DSettings, GlobalGameManager)
+ DECLARE_OBJECT_SERIALIZE (Physics2DSettings)
+
+ static void InitializeClass ();
+ static void CleanupClass ();
+
+ virtual void AwakeFromLoad(AwakeFromLoadMode mode);
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+
+ const Vector2f& GetGravity () const { return m_Gravity; }
+ void SetGravity (const Vector2f& value);
+
+ int GetVelocityIterations () const { return m_VelocityIterations; }
+ void SetVelocityIterations (const int velocityIterations);
+
+ int GetPositionIterations () const { return m_PositionIterations; }
+ void SetPositionIterations (const int positionIterations);
+
+ inline bool GetRaycastsHitTriggers () const { return m_RaycastsHitTriggers; }
+ inline void SetRaycastsHitTriggers (const bool raycastsHitTriggers) { m_RaycastsHitTriggers = raycastsHitTriggers; }
+
+ void IgnoreCollision (int layer1, int layer2, bool ignore);
+ bool GetIgnoreCollision(int layer1, int layer2) const;
+ UInt32 GetLayerCollisionMask(int layer) const { return m_LayerCollisionMatrix[layer]; }
+
+ PhysicsMaterial2D* GetDefaultPhysicsMaterial () { return m_DefaultMaterial; }
+
+private:
+ Vector2f m_Gravity; ///< The gravity applied to all rigid bodies in the scene.
+ PPtr<PhysicsMaterial2D> m_DefaultMaterial; ///< The default material to use on a collider if no material is specified on it.
+ int m_VelocityIterations; ///< The number of iterations used to solve simulation velocities. More iterations yield a better simulation but is more expensive. (Default 8) range { 1 , infinity }
+ int m_PositionIterations; ///< The number of iterations used to solve simulation positions. More iterations yield a better simulation but is more expensive. (Default 3) range { 1 , infinity }
+ bool m_RaycastsHitTriggers; ///< Whether ray/line casts hit triggers or not.
+
+ dynamic_array<UInt32> m_LayerCollisionMatrix;
+
+};
+
+Physics2DSettings& GetPhysics2DSettings ();
+Physics2DSettings* GetPhysics2DSettingsPtr ();
+
+#endif
diff --git a/Runtime/Physics2D/PolygonCollider2D.cpp b/Runtime/Physics2D/PolygonCollider2D.cpp
new file mode 100644
index 0000000..da41b0d
--- /dev/null
+++ b/Runtime/Physics2D/PolygonCollider2D.cpp
@@ -0,0 +1,134 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/PolygonCollider2D.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/FloatConversion.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Filters/AABBUtility.h"
+#if ENABLE_SPRITES
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+#endif
+
+IMPLEMENT_CLASS (PolygonCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (PolygonCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+PolygonCollider2D::PolygonCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+PolygonCollider2D::~PolygonCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void PolygonCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Poly);
+}
+
+void PolygonCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ // Create pentagon shape
+ CreateNgon (5, Vector2f(1, 1), Vector2f(0,0), m_Poly);
+}
+
+
+void PolygonCollider2D::SmartReset ()
+{
+ float radius;
+ Vector2f offset;
+
+ GameObject* go = GetGameObjectPtr();
+#if ENABLE_SPRITES
+ if (go)
+ {
+ SpriteRenderer* sr = go->QueryComponentT<SpriteRenderer>(ClassID(SpriteRenderer));
+ if (sr)
+ {
+ Sprite* sprite = sr->GetSprite();
+ if (sprite)
+ {
+ m_Poly.GenerateFrom(sprite, Vector2f(0, 0), 0.25f, 200, true);
+ if (m_Poly.GetPathCount() > 0) // We might fail if all pixels are under the threshold. No workaround in 4.3.
+ return;
+ }
+ }
+ }
+#endif
+
+ // Resolve what size collider we should have from object bounds
+ AABB aabb;
+ if (go && CalculateLocalAABB (GetGameObject (), &aabb))
+ {
+ Vector3f dist = aabb.GetExtent ();
+ radius = std::max(dist.x, dist.y);
+ if (radius <= 0.0f)
+ radius = 1.0f;
+ offset.x = aabb.GetCenter().x;
+ offset.y = aabb.GetCenter().y;
+ }
+ else
+ {
+ radius = 1.0f;
+ offset = Vector2f::zero;
+ }
+
+ // Create pentagon shape
+ CreateNgon (5, Vector2f(radius, radius), offset, m_Poly);
+}
+
+
+void PolygonCollider2D::RefreshPoly()
+{
+ Create();
+ SetDirty();
+}
+
+
+void PolygonCollider2D::CreatePrimitive (int sides, Vector2f scale, Vector2f offset)
+{
+ Assert (sides > 2);
+ Assert (scale.x > 0.0f);
+ Assert (scale.y > 0.0f);
+
+ CreateNgon (sides, scale, offset, m_Poly);
+
+ // Create polygon shape.
+ Create();
+ SetDirty();
+}
+
+
+void PolygonCollider2D::CreateNgon (const int sides, const Vector2f scale, const Vector2f offset, Polygon2D& polygon2D)
+{
+ Polygon2D::TPath path;
+
+ // Generate regular n-sided polygon.
+ const float tau = kPI * 2.0f;
+ const float chordAngle = tau / (float)sides;
+ float angle = 0.0f;
+ for (int chord = 0; chord < sides; ++chord, angle += chordAngle)
+ {
+ path.push_back (Vector2f(offset.x + scale.x * Sin(angle), offset.y + scale.y * Cos(angle)));
+ }
+
+ // Set polygon path.
+ polygon2D.SetPathCount (1);
+ polygon2D.SetPath (0, path);
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/PolygonCollider2D.h b/Runtime/Physics2D/PolygonCollider2D.h
new file mode 100644
index 0000000..6a5ed55
--- /dev/null
+++ b/Runtime/Physics2D/PolygonCollider2D.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/PolygonColliderBase2D.h"
+
+
+// --------------------------------------------------------------------------
+
+
+class PolygonCollider2D : public PolygonColliderBase2D
+{
+public:
+ REGISTER_DERIVED_CLASS (PolygonCollider2D, PolygonColliderBase2D)
+ DECLARE_OBJECT_SERIALIZE (PolygonCollider2D)
+
+ PolygonCollider2D (MemLabelId label, ObjectCreationMode mode);
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ virtual const Polygon2D& GetPoly() const { return m_Poly; }
+ Polygon2D& GetPoly() { return m_Poly; }
+ void RefreshPoly();
+
+ void CreatePrimitive (int sides, Vector2f scale = Vector2f(1.0f, 1.0f), Vector2f offset = Vector2f::zero);
+ static void CreateNgon (const int sides, const Vector2f scale, const Vector2f offset, Polygon2D& polygon2D);
+
+private:
+ Polygon2D m_Poly;
+};
+
+#endif
diff --git a/Runtime/Physics2D/PolygonColliderBase2D.cpp b/Runtime/Physics2D/PolygonColliderBase2D.cpp
new file mode 100644
index 0000000..1dfcb80
--- /dev/null
+++ b/Runtime/Physics2D/PolygonColliderBase2D.cpp
@@ -0,0 +1,336 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS
+
+#include "Runtime/Physics2D/PolygonColliderBase2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "External/libtess2/libtess2/tesselator.h"
+
+PROFILER_INFORMATION(gPhysics2DProfilePolygonColliderBaseCreate, "Physics2D.PolygonColliderCreate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfilePolygonColliderBaseDecomposition, "Physics2D.PolygonColliderDecomposition", kProfilerPhysics)
+
+IMPLEMENT_CLASS (PolygonColliderBase2D)
+
+
+// --------------------------------------------------------------------------
+
+
+PolygonColliderBase2D::PolygonColliderBase2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+PolygonColliderBase2D::~PolygonColliderBase2D ()
+{
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void PolygonColliderBase2D::Create (const Rigidbody2D* ignoreRigidbody)
+{
+ PROFILER_AUTO(gPhysics2DProfilePolygonColliderBaseCreate, NULL);
+
+ // Ensure we're cleaned-up.
+ Cleanup ();
+
+ // Ignore if not active.
+ if (!IsActive())
+ return;
+
+ const Polygon2D& poly = GetPoly();
+ if (poly.IsEmpty())
+ return;
+
+ const int pathCount = poly.GetPathCount();
+ if (pathCount == 0)
+ return;
+
+ // Calculate collider transformation.
+ Matrix4x4f relativeTransform;
+ b2Body* body;
+ CalculateColliderTransformation (ignoreRigidbody, &body, relativeTransform);
+
+ {
+ PROFILER_AUTO(gPhysics2DProfilePolygonColliderBaseDecomposition, NULL);
+
+ // Extract the convex shapes from the path(s).
+ dynamic_array<b2Shape*> shapePtr;
+ b2Shape* shapeMem = ExtractConvexShapes(shapePtr, relativeTransform);
+
+ // Finish if no shapes generated.
+ if (shapeMem == NULL)
+ return;
+
+ b2FixtureDef def;
+ FinalizeCreate(def, body, &shapePtr);
+
+ FREE_TEMP_MANUAL(shapeMem);
+ }
+}
+
+
+b2Shape* PolygonColliderBase2D::ExtractConvexShapes(dynamic_array<b2Shape*>& shapes, const Matrix4x4f& relativeTransform )
+{
+ // Calculate the maximum number of vertices for the shape type.
+ const int kMaxPolygonVerts = b2_maxPolygonVertices;
+ const int kVertexSize = 2;
+ const Vector3f scale = GetComponent(Transform).GetWorldScaleLossy();
+
+ // Tessellation
+ TESStesselator* tess = tessNewTess (NULL);
+
+ // Add all paths as a tessellation contour.
+ const Polygon2D& poly = GetPoly();
+ const int pathCount = poly.GetPathCount();
+ int addedContours = 0;
+ for (int pathIndex = 0; pathIndex < pathCount; ++pathIndex)
+ {
+ // Fetch the path.
+ const Polygon2D::TPath& path = poly.GetPath(pathIndex);
+
+ // Ignore illegal path.
+ if (path.size() < 3)
+ continue;
+
+ // Validate the path.
+ b2Vec2* points;
+ ALLOC_TEMP(points, b2Vec2, path.size());
+ const int validPointCount = TransformPoints (path, relativeTransform, scale, points);
+
+ // Add path contour.
+ tessAddContour (tess, kVertexSize, points, sizeof(b2Vec2), validPointCount);
+ addedContours++;
+ }
+
+ // Finish if no contours added.
+ if (addedContours == 0)
+ return NULL;
+
+ // Perform the tessellation.
+ const int tessError = tessTesselate(tess, TESS_WINDING_ODD, TESS_POLYGONS, kMaxPolygonVerts, kVertexSize, NULL);
+ AssertBreak(tessError == 1);
+
+ // Allocate the shape array.
+ const int elemCount = tessGetElementCount (tess);
+
+ // Finish if no elements.
+ if (elemCount == 0)
+ return NULL;
+
+ shapes.resize_uninitialized(elemCount);
+ b2PolygonShape* polygons = ALLOC_TEMP_MANUAL(b2PolygonShape, elemCount);
+
+ // Extract the tessellation results into the shape array.
+ const TESSindex* elements = tessGetElements(tess);
+ const TESSreal* real = tessGetVertices(tess);
+ b2Vec2* buffer;
+ ALLOC_TEMP(buffer, b2Vec2, kMaxPolygonVerts);
+ int totalElementCount=0;
+ for (int elementIndex = 0; elementIndex < elemCount; ++elementIndex)
+ {
+ const int* indices = &elements[elementIndex * kMaxPolygonVerts];
+
+ // Extract vertices
+ int bufSize = 0;
+ for (int i = 0; i < kMaxPolygonVerts && indices[i] != TESS_UNDEF; ++i)
+ {
+ const float& x = real[indices[i]*kVertexSize];
+ const float& y = real[indices[i]*kVertexSize + 1];
+
+ b2Vec2 newPoint(x, y);
+ if (bufSize > 0 && b2DistanceSquared(buffer[bufSize-1], newPoint) <= b2_linearSlop * b2_linearSlop)
+ continue;
+
+ buffer[bufSize] = newPoint;
+ ++bufSize;
+ }
+
+ // Ignore small paths.
+ if (bufSize < 3)
+ continue;
+
+ // Fill shape
+ if (ValidatePolygonShape (buffer, bufSize))
+ {
+ b2PolygonShape& shape = polygons[totalElementCount];
+ new (&shape) b2PolygonShape();
+ shape.Set(buffer, bufSize);
+ shapes[totalElementCount++] = &shape;
+ }
+ }
+
+ tessDeleteTess(tess);
+
+ // Finish if nothing generated.
+ if (totalElementCount == 0)
+ {
+ if (polygons)
+ FREE_TEMP_MANUAL(polygons);
+ return NULL;
+ }
+
+ shapes.resize_initialized(totalElementCount);
+
+ return polygons;
+}
+
+
+int PolygonColliderBase2D::TransformPoints(const Polygon2D::TPath& path, const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints)
+{
+ int outCount = 0;
+ for (size_t i = 0; i < path.size(); ++i)
+ {
+ // Calculate 3D vertex.
+ const Vector3f vertex3D = relativeTransform.MultiplyPoint3 (Vector3f(path[i].x * scale.x, path[i].y * scale.y, 0.0f));
+
+ // If any vertex are infinite or are a very large scale then abort transformation.
+ // We abort here rather than ignore the vertex otherwise we may end-up with large-scale collider geometry warping if
+ // only a few points are infinite or out-of-bounds. This less likely an issue with the small-scale.
+ if (!IsFinite (vertex3D) || SqrMagnitude(vertex3D ) > (PHYSICS_2D_LARGE_RANGE_CLAMP*PHYSICS_2D_LARGE_RANGE_CLAMP))
+ return 0;
+
+ // Fetch 2D vertex.
+ b2Vec2 vertex2D(vertex3D.x, vertex3D.y);
+
+ // Skip point if they end up being too close. Box2d fires asserts if distance between neighbors is less than b2_linearSlop.
+ if (outCount > 0 && b2DistanceSquared(*(outPoints-1), vertex2D) <= b2_linearSlop * b2_linearSlop)
+ continue;
+
+ *outPoints++ = vertex2D;
+ ++outCount;
+ }
+
+ return outCount;
+}
+
+
+bool PolygonColliderBase2D::ValidatePolygonShape(const b2Vec2* const points, const int pointCount)
+{
+ // Invalid polygon if the vertex count isn't in range.
+ if (pointCount < 3 || pointCount > b2_maxPolygonVertices)
+ return false;
+
+ // Validate the polygon using the exact same code that Box2D uses. This at least
+ // ensures that we don't trigger any runtime asserts in Box2D.
+
+ // Copy vertices into local buffer
+ b2Vec2 ps[b2_maxPolygonVertices];
+ for (int32 i = 0; i < pointCount; ++i)
+ ps[i] = points[i];
+
+ // Create the convex hull using the Gift wrapping algorithm
+ // http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
+
+ // Find the right most point on the hull
+ int32 i0 = 0;
+ float32 x0 = ps[0].x;
+ for (int32 i = 1; i < pointCount; ++i)
+ {
+ float32 x = ps[i].x;
+ if (x > x0 || (x == x0 && ps[i].y < ps[i0].y))
+ {
+ i0 = i;
+ x0 = x;
+ }
+ }
+
+ int32 hull[b2_maxPolygonVertices];
+ int32 validPointCount = 0;
+ int32 ih = i0;
+
+ for (;;)
+ {
+ hull[validPointCount] = ih;
+
+ int32 ie = 0;
+ for (int32 j = 1; j < pointCount; ++j)
+ {
+ if (ie == ih)
+ {
+ ie = j;
+ continue;
+ }
+
+ b2Vec2 r = ps[ie] - ps[hull[validPointCount]];
+ b2Vec2 v = ps[j] - ps[hull[validPointCount]];
+ float32 c = b2Cross(r, v);
+ if (c < 0.0f)
+ {
+ ie = j;
+ }
+
+ // Collinearity check
+ if (c == 0.0f && v.LengthSquared() > r.LengthSquared())
+ {
+ ie = j;
+ }
+ }
+
+ ++validPointCount;
+ ih = ie;
+
+ if (ie == i0)
+ {
+ break;
+ }
+ }
+
+ // Finish if invalid point count.
+ if (validPointCount < 3)
+ return false;
+
+
+ // The following code is directly from Box2D.
+ // Unfortunately Box2D simply asserts if the area inside the polygon is below a specific threshold when it
+ // is calculating the centroid so using Box2Ds code and invalidating the polygon rather than throwing an assert is required.
+
+ // Copy vertices.
+ b2Vec2 vertices[b2_maxPolygonVertices];
+ for (int32 i = 0; i < validPointCount; ++i)
+ {
+ vertices[i] = ps[hull[i]];
+ }
+
+ b2Vec2 c; c.Set(0.0f, 0.0f);
+ float32 area = 0.0f;
+
+ // pRef is the reference point for forming triangles.
+ // It's location doesn't change the result (except for rounding error).
+ b2Vec2 pRef(0.0f, 0.0f);
+
+ const float32 inv3 = 1.0f / 3.0f;
+
+ for (int32 i = 0; i < validPointCount; ++i)
+ {
+ // Triangle vertices.
+ b2Vec2 p1 = pRef;
+ b2Vec2 p2 = vertices[i];
+ b2Vec2 p3 = i + 1 < validPointCount ? vertices[i+1] : vertices[0];
+
+ b2Vec2 e1 = p2 - p1;
+ b2Vec2 e2 = p3 - p1;
+
+ float32 D = b2Cross(e1, e2);
+
+ float32 triangleArea = 0.5f * D;
+ area += triangleArea;
+
+ // Area weighted centroid
+ c += triangleArea * inv3 * (p1 + p2 + p3);
+ }
+
+ // Check for valid area.
+ return IsFinite (area) && area > b2_epsilon;
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/PolygonColliderBase2D.h b/Runtime/Physics2D/PolygonColliderBase2D.h
new file mode 100644
index 0000000..02d7069
--- /dev/null
+++ b/Runtime/Physics2D/PolygonColliderBase2D.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Collider2D.h"
+#include "Runtime/Graphics/Polygon2D.h"
+
+class Vector3f;
+
+
+// --------------------------------------------------------------------------
+
+
+class PolygonColliderBase2D : public Collider2D
+{
+public:
+ REGISTER_DERIVED_ABSTRACT_CLASS (PolygonColliderBase2D, Collider2D)
+
+ PolygonColliderBase2D (MemLabelId label, ObjectCreationMode mode);
+
+ virtual const Polygon2D& GetPoly() const = 0;
+
+protected:
+ virtual void Create (const Rigidbody2D* ignoreRigidbody = NULL);
+
+ b2Shape* ExtractConvexShapes(dynamic_array<b2Shape*>& shapes, const Matrix4x4f& relativeTransform);
+ static int TransformPoints(const Polygon2D::TPath& path, const Matrix4x4f& relativeTransform, const Vector3f& scale, b2Vec2* outPoints);
+ static bool ValidatePolygonShape(const b2Vec2* const points, const int pointCount);
+};
+
+#endif
diff --git a/Runtime/Physics2D/RigidBody2D.h b/Runtime/Physics2D/RigidBody2D.h
new file mode 100644
index 0000000..ed75dca
--- /dev/null
+++ b/Runtime/Physics2D/RigidBody2D.h
@@ -0,0 +1,125 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+
+class Vector2f;
+class Vector3f;
+class Quaternionf;
+class b2Body;
+struct b2Vec2;
+struct RootMotionData;
+
+
+// --------------------------------------------------------------------------
+
+enum RigidbodyInterpolation2D { kNoInterpolation2D = 0, kInterpolate2D = 1, kExtrapolate2D = 2 };
+enum RigidbodySleepMode2D { kNeverSleep2D = 0, kStartAwake2D = 1, kStartAsleep2D = 2 };
+enum CollisionDetectionMode2D { kDiscreteCollision2D = 0, kContinuousCollision2D = 1 };
+
+// --------------------------------------------------------------------------
+
+
+class Rigidbody2D : public Unity::Component
+{
+public:
+ REGISTER_DERIVED_CLASS (Rigidbody2D, Unity::Component)
+ DECLARE_OBJECT_SERIALIZE (Rigidbody2D)
+
+ Rigidbody2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~Rigidbody2D(); declared-by-macro
+
+ static void InitializeClass ();
+ static void CleanupClass () {}
+
+ virtual void CheckConsistency ();
+ virtual void Reset ();
+ virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode);
+ virtual void Deactivate (DeactivateOperation operation);
+
+ void Create ();
+
+ void TransformChanged (int changeMask);
+ void ApplyRootMotionBuiltin (RootMotionData* rootMotion);
+
+ float GetDrag () const { return m_LinearDrag; }
+ void SetDrag (float drag);
+
+ float GetAngularDrag () const { return m_AngularDrag; }
+ void SetAngularDrag (float drag);
+
+ float GetGravityScale () const { return m_GravityScale; }
+ void SetGravityScale (float scale);
+
+ void SetIsKinematic (bool isKinematic);
+ bool GetIsKinematic () const { return m_IsKinematic; }
+
+ void SetFixedAngle (bool fixedAngle);
+ bool IsFixedAngle () const { return m_FixedAngle; }
+
+ RigidbodyInterpolation2D GetInterpolation () { return (RigidbodyInterpolation2D)m_Interpolate; }
+ void SetInterpolation (RigidbodyInterpolation2D interpolation);
+
+ RigidbodySleepMode2D GetSleepMode () { return (RigidbodySleepMode2D)m_SleepingMode; }
+ void SetSleepMode (RigidbodySleepMode2D mode);
+
+ CollisionDetectionMode2D GetCollisionDetectionMode () { return (CollisionDetectionMode2D)m_CollisionDetection; }
+ void SetCollisionDetectionMode (CollisionDetectionMode2D mode);
+
+ // Get position and rotation. This can be different from transform state when using interpolation.
+ Vector3f GetBodyPosition () const;
+ Quaternionf GetBodyRotation () const;
+
+ // Linear velocity.
+ Vector2f GetVelocity () const;
+ void SetVelocity (const Vector2f& velocity);
+
+ // Angular velocity.
+ float GetAngularVelocity () const;
+ void SetAngularVelocity (float velocity);
+
+ // Sleeping.
+ void SetSleeping (bool sleeping);
+ bool IsSleeping () const;
+
+ // Mass.
+ float GetMass () const { return m_Mass; }
+ void SetMass (float mass);
+ void CalculateColliderBodyMass ();
+
+ // Add forces.
+ void AddForce (const Vector2f& force);
+ void AddTorque (float torque);
+ void AddForceAtPosition (const Vector2f& force, const Vector2f& position);
+
+ inline b2Body* GetBody() { return m_Body; }
+
+ static Rigidbody2D* FindRigidbody (const GameObject* gameObject, const Rigidbody2D* ignoreRigidbody = NULL);
+
+private:
+ void FetchPoseFromTransform (b2Vec2* outPos, float* outRot);
+ void UpdateInterpolationInfo ();
+ void Cleanup ();
+ void InformCollidersOfNewBody ();
+ void ReCreateInboundJoints ();
+ void RetargetDependencies (Rigidbody2D* ignoreRigidBody);
+
+private:
+ b2Body* m_Body;
+ Rigidbody2DInterpolationInfo* m_InterpolationInfo;
+
+ float m_Mass; ///< The mass of the body. range { 0.0001, 1000000 }
+ float m_LinearDrag; ///< The linear drag coefficient. 0 means no damping. range { 0, 1000000 }
+ float m_AngularDrag; ///< The angular drag coefficient. 0 means no damping. range { 0, 1000000 }
+ float m_GravityScale; ///< How much gravity affects this body. range { -1000000, 1000000 }
+ bool m_FixedAngle; ///< Whether the body's angle is fixed or not.
+ bool m_IsKinematic; ///< Whether the body is kinematic or not. If not then the body is dynamic.
+ UInt8 m_Interpolate; ///< The per-frame update mode for the body. enum { None = 0, Interpolate = 1, Extrapolate = 2 }
+ UInt8 m_SleepingMode; ///< The sleeping mode for the body. enum { Never Sleep = 0, Start Awake = 1, Start Asleep = 2 }
+ UInt8 m_CollisionDetection; ///< The collision detection mode for the body. enum { Discrete = 0, Continuous = 1 }
+};
+
+#endif
diff --git a/Runtime/Physics2D/Rigidbody2D.cpp b/Runtime/Physics2D/Rigidbody2D.cpp
new file mode 100644
index 0000000..6b2b01a
--- /dev/null
+++ b/Runtime/Physics2D/Rigidbody2D.cpp
@@ -0,0 +1,700 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Collider2D.h"
+#include "Runtime/Physics2D/Joint2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/BaseClasses/MessageHandler.h"
+#include "Runtime/GameCode/RootMotionData.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+IMPLEMENT_CLASS_HAS_INIT (Rigidbody2D)
+IMPLEMENT_OBJECT_SERIALIZE (Rigidbody2D)
+
+
+// --------------------------------------------------------------------------
+
+
+Rigidbody2D::Rigidbody2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_Body(NULL)
+, m_InterpolationInfo(NULL)
+{
+}
+
+Rigidbody2D::~Rigidbody2D ()
+{
+ Cleanup ();
+}
+
+
+void Rigidbody2D::InitializeClass ()
+{
+ REGISTER_MESSAGE (Rigidbody2D, kTransformChanged, TransformChanged, int);
+ REGISTER_MESSAGE_PTR (Rigidbody2D, kAnimatorMoveBuiltin, ApplyRootMotionBuiltin, RootMotionData);
+}
+
+
+template<class TransferFunction>
+void Rigidbody2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Mass);
+ TRANSFER (m_LinearDrag);
+ TRANSFER (m_AngularDrag);
+ TRANSFER (m_GravityScale);
+ TRANSFER (m_FixedAngle);
+ TRANSFER (m_IsKinematic);
+ TRANSFER (m_Interpolate);
+ TRANSFER (m_SleepingMode);
+ TRANSFER (m_CollisionDetection);
+ transfer.Align();
+}
+
+
+void Rigidbody2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Mass = clamp<float> (m_Mass, 0.0001f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_LinearDrag = clamp<float> (m_LinearDrag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_AngularDrag = clamp<float> (m_AngularDrag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_GravityScale = clamp<float> (m_GravityScale, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ if (m_Interpolate>2)
+ m_Interpolate = kNoInterpolation2D;
+
+ if (m_SleepingMode>2)
+ m_SleepingMode = kStartAwake2D;
+
+ if (m_CollisionDetection!=0 && m_CollisionDetection!=1)
+ m_CollisionDetection = kDiscreteCollision2D;
+}
+
+
+void Rigidbody2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Mass = 1.0f;
+ m_LinearDrag = 0.0f;
+ m_AngularDrag = 0.05f;
+ m_GravityScale = 1.0f;
+ m_FixedAngle = false;
+ m_IsKinematic = false;
+ m_Interpolate = (RigidbodyInterpolation2D)kNoInterpolation2D;
+ m_SleepingMode = (RigidbodySleepMode2D)kStartAwake2D;
+ m_CollisionDetection = (CollisionDetectionMode2D)kDiscreteCollision2D;
+}
+
+
+void Rigidbody2D::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+
+ Assert (GameObject::GetMessageHandler().HasMessageCallback (ClassID(Rigidbody2D), kTransformChanged.messageID));
+
+ // Create the body if it's not created already.
+ if (IsActive() && m_Body == NULL)
+ Create ();
+
+ // Apply all body properties.
+ // Note that we should not allow anything to adjust the current sleep-state of the body here when the component
+ // is first woken otherwise we'll potentially override the need to be initially asleep (sleep mode).
+ if (!(awakeMode & (kDidLoadFromDisk | kInstantiateOrCreateFromCodeAwakeFromLoad)))
+ {
+ SetMass (m_Mass);
+ SetDrag (m_LinearDrag);
+ SetAngularDrag (m_AngularDrag);
+ SetGravityScale (m_GravityScale);
+ SetIsKinematic (m_IsKinematic);
+ SetFixedAngle (m_FixedAngle);
+ SetCollisionDetectionMode ((CollisionDetectionMode2D)m_CollisionDetection);
+ SetSleepMode ((RigidbodySleepMode2D)m_SleepingMode);
+ }
+
+ // Inform the colliders about the new body.
+ if (awakeMode & kInstantiateOrCreateFromCodeAwakeFromLoad)
+ InformCollidersOfNewBody ();
+}
+
+
+void Rigidbody2D::Deactivate (DeactivateOperation operation)
+{
+ Cleanup ();
+}
+
+
+void Rigidbody2D::Create ()
+{
+ if (m_Body != NULL)
+ return;
+
+ // Configure the body definitio0n.
+ b2BodyDef bodyDef;
+ bodyDef.type = m_IsKinematic ? b2_kinematicBody : b2_dynamicBody;
+ bodyDef.userData = this;
+ bodyDef.bullet = m_CollisionDetection == kContinuousCollision2D;
+ bodyDef.linearDamping = m_LinearDrag;
+ bodyDef.angularDamping = m_AngularDrag;
+ bodyDef.gravityScale = m_GravityScale;
+ bodyDef.fixedRotation = m_FixedAngle;
+ bodyDef.allowSleep = m_SleepingMode != kNeverSleep2D;
+ bodyDef.awake = m_SleepingMode != kStartAsleep2D;;
+
+ // Fetch the body pose.
+ if (IsActive ())
+ FetchPoseFromTransform (&bodyDef.position, &bodyDef.angle);
+
+ // Create the body.
+ m_Body = GetPhysics2DWorld()->CreateBody (&bodyDef);
+
+ // Calculate the collider body mass.
+ CalculateColliderBodyMass ();
+
+ // Set interpolation.
+ SetInterpolation ((RigidbodyInterpolation2D)m_Interpolate);
+}
+
+
+void Rigidbody2D::TransformChanged (int changeMask)
+{
+ // Finish if no body exists.
+ if (!m_Body)
+ return;
+
+ // Finish if transform message is disabled.
+ if (!GetPhysics2DManager().IsTransformMessageEnabled())
+ return;
+
+ // Update the rigid-body transform if the position or rotation has changed.
+ if (changeMask & (Transform::kPositionChanged | Transform::kRotationChanged))
+ {
+ b2Vec2 position;
+ float angle;
+ FetchPoseFromTransform (&position, &angle);
+ m_Body->SetTransform (position, angle);
+ m_Body->SetAwake(true);
+
+ // Disable interpolation if transform has changed.
+ if (m_InterpolationInfo)
+ m_InterpolationInfo->disabled = 1;
+ }
+
+ // Recreate inbound joints if the scale has changed.
+ if (changeMask & Transform::kScaleChanged)
+ {
+ ReCreateInboundJoints ();
+ return;
+ }
+}
+
+
+void Rigidbody2D::ApplyRootMotionBuiltin (RootMotionData* rootMotion)
+{
+ if (m_Body == NULL || rootMotion->didApply)
+ return;
+
+ if(GetIsKinematic())
+ {
+ b2Vec2 position = m_Body->GetPosition ();
+ position.x += rootMotion->deltaPosition.x;
+ position.y += rootMotion->deltaPosition.y;
+ m_Body->SetTransform (position, rootMotion->targetRotation.z);
+ }
+ else
+ {
+ Quaternionf rotation = GetComponent(Transform).GetRotation();
+ Quaternionf invRotation = Inverse(rotation);
+
+ // Get the physics velocity in local space
+ const Vector2f velocity2 = GetVelocity();
+ const Vector3f physicsVelocityLocal = RotateVectorByQuat(invRotation, Vector3f(velocity2.x, velocity2.y, 0.0f));
+
+ // 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 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 (GetGravityScale () > 0.0f)
+ AddForce(GetPhysics2DSettings ().GetGravity() * -Lerp(1.0F, 0.0F, rootMotion->gravityWeight));
+
+ // Apply velocity & rotation
+ Vector3f globalVelocity = RotateVectorByQuat(rotation, localVelocity);
+ m_Body->SetLinearVelocity (b2Vec2(globalVelocity.x, globalVelocity.y));
+ const Vector3f localEuler = QuaternionToEuler (rootMotion->targetRotation);
+ m_Body->SetTransform (m_Body->GetPosition (), localEuler.z);
+ }
+
+ m_Body->SetAwake(true);
+ rootMotion->didApply = true;
+}
+
+
+void Rigidbody2D::SetDrag (float drag)
+{
+ ABORT_INVALID_FLOAT (drag, drag, Rigidbody2D);
+
+ m_LinearDrag = clamp<float> (drag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetLinearDamping (m_LinearDrag);
+}
+
+
+void Rigidbody2D::SetAngularDrag (float drag)
+{
+ ABORT_INVALID_FLOAT (drag, angularDrag, Rigidbody2D);
+
+ m_AngularDrag = clamp<float> (drag, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetAngularDamping (m_AngularDrag);
+}
+
+
+void Rigidbody2D::SetGravityScale (float scale)
+{
+ ABORT_INVALID_FLOAT (scale, gravityScale, Rigidbody2D);
+
+ m_GravityScale = clamp<float> (scale, -PHYSICS_2D_LARGE_RANGE_CLAMP, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty ();
+
+ if (m_Body)
+ {
+ m_Body->SetGravityScale (m_GravityScale);
+
+ // Wake body if a non-zero gravity is applied.
+ if (m_GravityScale < 0.0f || m_GravityScale > 0.0f)
+ m_Body->SetAwake (true);
+ }
+}
+
+
+void Rigidbody2D::SetIsKinematic (bool isKinematic)
+{
+ m_IsKinematic = isKinematic;
+ SetDirty();
+
+ if (m_Body)
+ {
+ m_Body->SetType (m_IsKinematic ? b2_kinematicBody : b2_dynamicBody);
+
+ // Wake the body.
+ m_Body->SetAwake(true);
+
+ // If transitioning into a kinematic body, reset velocities to match what 3D physics does.
+ if (m_IsKinematic)
+ {
+ m_Body->SetLinearVelocity (b2Vec2_zero);
+ m_Body->SetAngularVelocity (0.0f);
+ }
+
+ // Calculate the collider body mass.
+ // NOTE: Unfortunately we must do this here as Box2D resets its mass-data when this is changed.
+ CalculateColliderBodyMass ();
+ }
+}
+
+
+void Rigidbody2D::SetFixedAngle (bool fixedAngle)
+{
+ m_FixedAngle = fixedAngle;
+ SetDirty();
+
+ if (m_Body)
+ {
+ m_Body->SetFixedRotation(m_FixedAngle);
+
+ // Calculate the collider body mass.
+ // NOTE: Unfortunately we must do this here as Box2D resets its mass-data when this is changed.
+ CalculateColliderBodyMass ();
+ }
+}
+
+
+void Rigidbody2D::SetInterpolation (RigidbodyInterpolation2D interpolation)
+{
+ m_Interpolate = interpolation;
+ SetDirty();
+
+ if (m_Body)
+ UpdateInterpolationInfo();
+}
+
+
+void Rigidbody2D::SetSleepMode (RigidbodySleepMode2D mode)
+{
+ m_SleepingMode = mode;
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetSleepingAllowed (mode != kNeverSleep2D);
+}
+
+
+void Rigidbody2D::SetCollisionDetectionMode (CollisionDetectionMode2D mode)
+{
+ m_CollisionDetection = mode;
+ SetDirty ();
+
+ if (m_Body)
+ m_Body->SetBullet( mode == kContinuousCollision2D );
+}
+
+
+Vector3f Rigidbody2D::GetBodyPosition () const
+{
+ Assert (m_Body != NULL);
+
+ const b2Vec2& pos2D = m_Body->GetPosition();
+ const Transform& transform = GetGameObject().GetComponent (Transform);
+ Vector3f pos3D = transform.GetPosition();
+ pos3D.x = pos2D.x;
+ pos3D.y = pos2D.y;
+
+ return pos3D;
+}
+
+
+Quaternionf Rigidbody2D::GetBodyRotation () const
+{
+ Assert (m_Body != NULL);
+
+ return EulerToQuaternion (Vector3f(0, 0, m_Body->GetAngle()));
+}
+
+
+Vector2f Rigidbody2D::GetVelocity () const
+{
+ // Return no linear velocity if no body is available.
+ if (m_Body == NULL)
+ return Vector2f::zero;
+
+ // Return the body linear velocity.
+ const b2Vec2& velocity = m_Body->GetLinearVelocity ();
+ return Vector2f (velocity.x, velocity.y);
+}
+
+
+void Rigidbody2D::SetVelocity (const Vector2f& velocity)
+{
+ ABORT_INVALID_VECTOR2 (velocity, velocity, Rigidbody2D);
+
+ // Ignore linear velocity if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->SetLinearVelocity( b2Vec2(velocity.x, velocity.y) );
+}
+
+
+float Rigidbody2D::GetAngularVelocity () const
+{
+ // Return no angular velocity if no body is available.
+ if (m_Body == NULL)
+ return 0.0f;
+
+ // Return the body angular velocity.
+ return math::degrees (m_Body->GetAngularVelocity());
+}
+
+
+void Rigidbody2D::SetAngularVelocity (float velocity)
+{
+ ABORT_INVALID_FLOAT (velocity, angularVelocity, Rigidbody2D);
+
+ // Ignore linear velocity if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->SetAngularVelocity (math::radians (velocity));
+}
+
+
+void Rigidbody2D::SetSleeping (bool sleeping)
+{
+ // Ignore sleeping if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->SetAwake(!sleeping);
+}
+
+
+bool Rigidbody2D::IsSleeping() const
+{
+ // Not sleeping if no body is available.
+ if (m_Body == NULL)
+ return false;
+
+ return !m_Body->IsAwake();
+}
+
+
+void Rigidbody2D::SetMass (float mass)
+{
+ ABORT_INVALID_FLOAT (mass, mass, Rigidbody2D);
+
+ // Clamp mass.
+ m_Mass = clamp<float> (mass, 0.0001f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+
+ // Calculate the collider body mass.
+ CalculateColliderBodyMass ();
+}
+
+
+void Rigidbody2D::CalculateColliderBodyMass ()
+{
+ // Finish if body is not dynamic or has no fixtures.
+ if (m_Body == NULL ||
+ m_Body->GetType () != b2_dynamicBody)
+ return;
+
+ // Reset the mass-data.
+ m_Body->ResetMassData ();
+
+ // Scale body mass to target mass.
+ b2MassData bodyMassData;
+ m_Body->GetMassData (&bodyMassData);
+ const float massScale = m_Mass / bodyMassData.mass;
+ bodyMassData.mass *= massScale;
+
+ // Scale or set rotational inertia.
+ if (m_Body->GetFixtureCount () > 0)
+ bodyMassData.I *= massScale;
+ else
+ bodyMassData.I = m_Mass * 10.0f;
+
+ m_Body->SetMassData (&bodyMassData);
+}
+
+
+void Rigidbody2D::AddForce (const Vector2f& force)
+{
+ ABORT_INVALID_ARG_VECTOR2 (force, force, AddForce, Rigidbody2D);
+
+ // Ignore force if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->ApplyForceToCenter (b2Vec2(force.x,force.y), true);
+}
+
+
+void Rigidbody2D::AddTorque (float torque)
+{
+ ABORT_INVALID_ARG_FLOAT (torque, torque, AddTorque, Rigidbody2D);
+
+ // Ignore torque if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->ApplyTorque (torque, true);
+}
+
+
+void Rigidbody2D::AddForceAtPosition (const Vector2f& force, const Vector2f& position)
+{
+ ABORT_INVALID_ARG_VECTOR2 (force, force, AddForceAtPosition, Rigidbody2D);
+ ABORT_INVALID_ARG_VECTOR2 (position, position, AddForceAtPosition, Rigidbody2D);
+
+ // Ignore force-at-position if no body is available.
+ if (m_Body == NULL)
+ return;
+
+ m_Body->ApplyForce (b2Vec2(force.x, force.y), b2Vec2(position.x, position.y), true);
+}
+
+
+Rigidbody2D* Rigidbody2D::FindRigidbody (const GameObject* gameObject, const Rigidbody2D* ignoreRigidbody)
+{
+ Assert (gameObject != NULL);
+
+ // If there's a rigid-body on this game-object then return it.
+ Rigidbody2D* rigidBody = gameObject->QueryComponent (Rigidbody2D);
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive ())
+ return rigidBody;
+
+ // Search for a rigid-body up the transform hierarchy.
+ Transform* parent = gameObject->GetComponent (Transform).GetParent ();
+ while (parent)
+ {
+ GameObject* parentGameObject = parent->GetGameObjectPtr ();
+ if (parentGameObject)
+ rigidBody = parentGameObject->QueryComponent (Rigidbody2D);
+ else
+ rigidBody = NULL;
+
+ if (rigidBody && rigidBody != ignoreRigidbody && rigidBody->IsActive ())
+ return rigidBody;
+
+ parent = parent->GetParent ();
+ }
+
+ // Nothing found!
+ return NULL;
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void Rigidbody2D::FetchPoseFromTransform (b2Vec2* outPos, float* outRot)
+{
+ Transform& transform = GetComponent (Transform);
+ Vector3f pos = transform.GetPosition ();
+ Quaternionf rot = transform.GetRotation ();
+
+ AssertFiniteParameter(pos)
+ AssertFiniteParameter(rot)
+
+ float rotZ = QuaternionToEuler(rot).z;
+ outPos->Set (pos.x, pos.y);
+ *outRot = rotZ;
+}
+
+
+void Rigidbody2D::UpdateInterpolationInfo ()
+{
+ // Remove the interpolation info if no interpolation selected.
+ if (m_Interpolate == kNoInterpolation2D)
+ {
+ UNITY_DELETE(m_InterpolationInfo, kMemPhysics);
+ return;
+ }
+
+ // Finish if we already have interpolation info.
+ if (m_InterpolationInfo != NULL)
+ return;
+
+ // Generate the interpolation info.
+ m_InterpolationInfo = UNITY_NEW(Rigidbody2DInterpolationInfo, kMemPhysics);
+ Rigidbody2DInterpolationInfo& info = *m_InterpolationInfo;
+ info.body = this;
+ info.disabled = 1;
+ info.position = Vector3f::zero;
+ info.rotation = Quaternionf::identity();
+ GetPhysics2DManager().GetInterpolatedBodies().push_back(*m_InterpolationInfo);
+}
+
+
+void Rigidbody2D::Cleanup ()
+{
+ if (m_Body == NULL)
+ return;
+
+ // Process all colliders as the rigid-body is going away.
+ const int fixtureCount = m_Body->GetFixtureCount ();
+ if (fixtureCount > 0 )
+ {
+ dynamic_array<Collider2D*> attachedColliders(kMemTempAlloc);
+
+ // Gather all attached colliders.
+ for (b2Fixture* fixture = m_Body->GetFixtureList (); fixture != NULL; fixture = fixture->GetNext ())
+ {
+ Collider2D* collider = (Collider2D*)fixture->GetUserData ();
+ attachedColliders.push_back (collider);
+ }
+
+ // Process all colliders.
+ for (dynamic_array<Collider2D*>::iterator colliderItr = attachedColliders.begin (); colliderItr != attachedColliders.end (); ++colliderItr)
+ (*colliderItr)->Cleanup ();
+ }
+
+ // Process all joints as the rigid-body is going away.
+ if (m_Body->GetJointList () != NULL)
+ {
+ dynamic_array<Joint2D*> attachedJoints(kMemTempAlloc);
+
+ // Gather all joints.
+ for (b2JointEdge* joints = m_Body->GetJointList(); joints != NULL; joints = joints->next)
+ attachedJoints.push_back ((Joint2D*)joints->joint->GetUserData());
+
+ // Process all joints.
+ for (dynamic_array<Joint2D*>::iterator jointItr = attachedJoints.begin (); jointItr != attachedJoints.end (); ++jointItr)
+ (*jointItr)->Cleanup ();
+ }
+
+ // Destroy the body.
+ GetPhysics2DWorld()->DestroyBody (m_Body);
+ m_Body = NULL;
+
+ // Destroy the body interpolation information.
+ UNITY_DELETE(m_InterpolationInfo, kMemPhysics);
+}
+
+
+void Rigidbody2D::InformCollidersOfNewBody ()
+{
+ // Fetch all potential colliders.
+ dynamic_array<Unity::Component*> colliders (kMemTempAlloc);
+ GetComponentsInChildren (GetGameObject (), false, ClassID (Collider2D), colliders);
+
+ // Finish if no colliders are found.
+ if (colliders.size () == 0)
+ return;
+
+ // Recreate the colliders.
+ for (dynamic_array<Unity::Component*>::iterator colliderItr = colliders.begin (); colliderItr != colliders.end (); ++colliderItr)
+ {
+ Collider2D* collider = (Collider2D*)*colliderItr;
+
+ // Ignore if not enabled.
+ if (!collider->GetEnabled ())
+ continue;
+
+ collider->RecreateCollider (NULL);
+ }
+}
+
+
+void Rigidbody2D::ReCreateInboundJoints ()
+{
+ // Finish if not appropriate.
+ if (m_Body == NULL)
+ return;
+
+ // Fetch the joints this body is connected to.
+ // This can occur when there's "inbound" joints from other components on other game objects.
+ b2JointEdge* joints = m_Body->GetJointList();
+
+ // Process all joints.
+ while (joints != NULL)
+ {
+ // Fetch the joint.
+ b2Joint* joint = joints->joint;
+
+ // Fetch the next joint.
+ b2JointEdge* nextJoint = joints->next;
+
+ // Fetch the joint component.
+ Joint2D* jointComponent = (Joint2D*)joint->GetUserData();
+ Assert (jointComponent != NULL);
+
+ // Recreate the joint.
+ jointComponent->ReCreate ();
+
+ // Next joint.
+ joints = nextJoint;
+ }
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt b/Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt
new file mode 100644
index 0000000..6298c40
--- /dev/null
+++ b/Runtime/Physics2D/ScriptBindings/Physics2DBindings.txt
@@ -0,0 +1,820 @@
+C++RAW
+#include "UnityPrefix.h"
+#include "Configuration/UnityConfigure.h"
+#include "Runtime/Scripting/ScriptingExportUtility.h"
+#include "Runtime/Scripting/ScriptingUtility.h"
+#include "Runtime/Scripting/ScriptingManager.h"
+
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Physics2D/Physics2DMaterial.h"
+#include "Runtime/Physics2D/Physics2DSettings.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/PolygonCollider2D.h"
+#include "Runtime/Physics2D/SpriteCollider2D.h"
+#include "Runtime/Physics2D/CircleCollider2D.h"
+#include "Runtime/Physics2D/BoxCollider2D.h"
+#include "Runtime/Physics2D/EdgeCollider2D.h"
+
+#include "Runtime/Physics2D/Joint2D.h"
+#include "Runtime/Physics2D/SpringJoint2D.h"
+#include "Runtime/Physics2D/DistanceJoint2D.h"
+#include "Runtime/Physics2D/HingeJoint2D.h"
+#include "Runtime/Physics2D/SliderJoint2D.h"
+
+#include "Runtime/Geometry/Ray.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Graphics/Polygon2D.h"
+#include "Runtime/Misc/GameObjectUtility.h"
+
+CSRAW
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace UnityEngine
+{
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+NONSEALED_CLASS Physics2D
+ CSRAW public const int IgnoreRaycastLayer = 1 << 2;
+ CSRAW public const int DefaultRaycastLayers = ~IgnoreRaycastLayer;
+ CSRAW public const int AllLayers = ~0;
+
+
+ // --------------------------------------------------------------------------
+
+
+ THREAD_SAFE
+ CUSTOM_PROP static int velocityIterations { return GetPhysics2DSettings().GetVelocityIterations (); } { SCRIPTINGAPI_THREAD_CHECK(SetVelocityIterations) return GetPhysics2DSettings().SetVelocityIterations (value); }
+ CUSTOM_PROP static int positionIterations { return GetPhysics2DSettings().GetPositionIterations (); } { SCRIPTINGAPI_THREAD_CHECK(SetPositionIterations) return GetPhysics2DSettings().SetPositionIterations (value); }
+ CUSTOM_PROP static Vector2 gravity { return GetPhysics2DSettings().GetGravity (); } { SCRIPTINGAPI_THREAD_CHECK(set_gravity) return GetPhysics2DSettings().SetGravity (value); }
+ CUSTOM_PROP static bool raycastsHitTriggers { return GetPhysics2DSettings().GetRaycastsHitTriggers (); } { SCRIPTINGAPI_THREAD_CHECK(SetRaycastsHitTriggers) return GetPhysics2DSettings().SetRaycastsHitTriggers (value); }
+
+
+ // --------------------------------------------------------------------------
+
+
+ CUSTOM static public void IgnoreLayerCollision (int layer1, int layer2, bool ignore = true)
+ {
+ GetPhysics2DSettings().IgnoreCollision(layer1, layer2, ignore);
+ }
+
+ CUSTOM static public bool GetIgnoreLayerCollision (int layer1, int layer2)
+ {
+ return GetPhysics2DSettings().GetIgnoreCollision(layer1, layer2);
+ }
+
+
+ // --------------------------------------------------------------------------
+
+
+ C++RAW
+
+ // Create a managed array of native ray-cast hits.
+ static ScriptingArrayPtr CreateManagedRaycastArrayFromNative (RaycastHit2D* nativeHits, size_t size)
+ {
+ // Return an empty array if no hits.
+ if (size == 0)
+ return CreateEmptyStructArray (MONO_COMMON.raycastHit2D);
+
+ // Generate scripting array.
+ ScriptingArrayPtr scriptArray = CreateScriptingArray<RaycastHit2D> (MONO_COMMON.raycastHit2D, size);
+
+ // Transfer elements.
+ RaycastHit2D* scriptHit = Scripting::GetScriptingArrayStart<RaycastHit2D> (scriptArray);
+ for (size_t i = 0; i < size; ++i, ++scriptHit, ++nativeHits)
+ {
+ // Transfer members.
+ scriptHit->point = nativeHits->point;
+ scriptHit->normal = nativeHits->normal;
+ scriptHit->fraction = nativeHits->fraction;
+ scriptHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (nativeHits->collider));
+ }
+ return scriptArray;
+ }
+
+ CSRAW
+
+
+ // --------------------------------------------------------------------------
+
+
+ CUSTOM private static void Internal_Linecast (Vector2 start, Vector2 end, int layerMask, float minDepth, float maxDepth, out RaycastHit2D raycastHit)
+ {
+ if (GetPhysics2DManager ().Linecast (start, end, layerMask, minDepth, maxDepth, raycastHit, 1) == 1)
+ raycastHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHit->collider));
+ }
+
+ // Returns the first hit along the specified line.
+ CSRAW public static RaycastHit2D Linecast (Vector2 start, Vector2 end, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D raycastHit;
+ Internal_Linecast (start, end, layerMask, minDepth, maxDepth, out raycastHit);
+ return raycastHit;
+ }
+
+ // Returns all hits along the specified line.
+ CUSTOM public static RaycastHit2D[] LinecastAll (Vector2 start, Vector2 end, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().LinecastAll (start, end, layerMask, minDepth, maxDepth, &raycastHits) == 0)
+ return CreateEmptyStructArray (MONO_COMMON.raycastHit2D);
+
+ return CreateManagedRaycastArrayFromNative (raycastHits.data (), raycastHits.size ());
+ }
+
+ // Returns all hits along the line (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int LinecastNonAlloc (Vector2 start, Vector2 end, RaycastHit2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D* raycastHits = Scripting::GetScriptingArrayStart<RaycastHit2D>(results);
+ const int resultCount = GetPhysics2DManager ().Linecast (start, end, layerMask, minDepth, maxDepth, raycastHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i, ++raycastHits)
+ raycastHits->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHits->collider));
+
+ return resultCount;
+ }
+
+ CUSTOM private static void Internal_Raycast (Vector2 origin, Vector2 direction, float distance, int layerMask, float minDepth, float maxDepth, out RaycastHit2D raycastHit)
+ {
+ if (GetPhysics2DManager ().Raycast (origin, direction, distance, layerMask, minDepth, maxDepth, raycastHit, 1) == 1)
+ raycastHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHit->collider));
+ }
+
+ // Returns the first hit along the ray.
+ CSRAW public static RaycastHit2D Raycast (Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D raycastHit;
+ Internal_Raycast (origin, direction, distance, layerMask, minDepth, maxDepth, out raycastHit);
+ return raycastHit;
+ }
+
+ // Returns all hits along the ray.
+ CUSTOM public static RaycastHit2D[] RaycastAll (Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().RaycastAll (origin, direction, distance, layerMask, minDepth, maxDepth, &raycastHits) == 0)
+ return CreateEmptyStructArray(MONO_COMMON.raycastHit2D);
+
+ return CreateManagedRaycastArrayFromNative (raycastHits.data (), raycastHits.size ());
+ }
+
+ // Returns all hits along the ray (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int RaycastNonAlloc (Vector2 origin, Vector2 direction, RaycastHit2D[] results, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ RaycastHit2D* raycastHits = Scripting::GetScriptingArrayStart<RaycastHit2D>(results);
+ const int resultCount = GetPhysics2DManager ().Raycast (origin, direction, distance, layerMask, minDepth, maxDepth, raycastHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i, ++raycastHits)
+ raycastHits->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHits->collider));
+
+ return resultCount;
+ }
+
+
+ // --------------------------------------------------------------------------
+
+
+ CUSTOM private static void Internal_GetRayIntersection (Ray ray, float distance, int layerMask, out RaycastHit2D raycastHit)
+ {
+ if (GetPhysics2DManager ().GetRayIntersection (ray.GetOrigin (), ray.GetDirection (), distance, layerMask, raycastHit, 1) == 1)
+ raycastHit->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHit->collider));
+ }
+
+ // Returns the first hit intersecting the 3D ray.
+ CSRAW public static RaycastHit2D GetRayIntersection (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ RaycastHit2D raycastHit;
+ Internal_GetRayIntersection (ray, distance, layerMask, out raycastHit);
+ return raycastHit;
+ }
+
+ // Returns all hits intersecting the 3D ray.
+ CUSTOM public static RaycastHit2D[] GetRayIntersectionAll (Ray ray, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ dynamic_array<RaycastHit2D> raycastHits(kMemTempAlloc);
+ if (GetPhysics2DManager ().GetRayIntersectionAll (ray.GetOrigin (), ray.GetDirection (), distance, layerMask, &raycastHits) == 0)
+ return CreateEmptyStructArray(MONO_COMMON.raycastHit2D);
+
+ return CreateManagedRaycastArrayFromNative (raycastHits.data (), raycastHits.size ());
+ }
+
+ // Returns all hits intersecting the 3D ray (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int GetRayIntersectionNonAlloc (Ray ray, RaycastHit2D[] results, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
+ {
+ RaycastHit2D* raycastHits = Scripting::GetScriptingArrayStart<RaycastHit2D>(results);
+ const int resultCount = GetPhysics2DManager ().GetRayIntersection (ray.GetOrigin (), ray.GetDirection (), distance, layerMask, raycastHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i, ++raycastHits)
+ raycastHits->collider = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (raycastHits->collider));
+
+ return resultCount;
+ }
+
+
+ // --------------------------------------------------------------------------
+
+
+ // Returns a collider overlapping the point.
+ CUSTOM public static Collider2D OverlapPoint (Vector2 point, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D* collider;
+ const int resultCount = GetPhysics2DManager ().OverlapPoint (point, layerMask, minDepth, maxDepth, &collider, 1);
+
+ if (resultCount == 0)
+ return SCRIPTING_NULL;
+
+ return Scripting::ScriptingWrapperFor (collider);
+ }
+
+ // Returns all colliders overlapping the point.
+ CUSTOM public static Collider2D[] OverlapPointAll (Vector2 point, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ GetPhysics2DManager ().OverlapPointAll (point, layerMask, minDepth, maxDepth, &colliderHits);
+
+ // Generate scripting array.
+ return CreateScriptingArrayFromUnityObjects(colliderHits, ScriptingClassFor(Collider2D));
+ }
+
+ // Returns all colliders overlapping the point (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int OverlapPointNonAlloc (Vector2 point, Collider2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D** colliderHits = Scripting::GetScriptingArrayStart<Collider2D*>(results);
+ const int resultCount = GetPhysics2DManager ().OverlapPoint (point, layerMask, minDepth, maxDepth, colliderHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i)
+ colliderHits[i] = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (colliderHits[i]));
+
+ return resultCount;
+ }
+
+ // Returns a collider overlapping the circle.
+ CUSTOM public static Collider2D OverlapCircle (Vector2 point, float radius, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D* collider;
+ const int resultCount = GetPhysics2DManager ().OverlapCircle (point, radius, layerMask, minDepth, maxDepth, &collider, 1);
+
+ if (resultCount == 0)
+ return SCRIPTING_NULL;
+
+ return Scripting::ScriptingWrapperFor (collider);
+ }
+
+ // Returns all colliders overlapping the circle.
+ CUSTOM public static Collider2D[] OverlapCircleAll (Vector2 point, float radius, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ GetPhysics2DManager ().OverlapCircleAll (point, radius, layerMask, minDepth, maxDepth, &colliderHits);
+
+ // Generate scripting array.
+ return CreateScriptingArrayFromUnityObjects(colliderHits, ScriptingClassFor(Collider2D));
+ }
+
+ // Returns all colliders overlapping the circle (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int OverlapCircleNonAlloc (Vector2 point, float radius, Collider2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D** colliderHits = Scripting::GetScriptingArrayStart<Collider2D*>(results);
+ const int resultCount = GetPhysics2DManager ().OverlapCircle (point, radius, layerMask, minDepth, maxDepth, colliderHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i)
+ colliderHits[i] = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (colliderHits[i]));
+
+ return resultCount;
+ }
+
+ // Returns a collider overlapping the area.
+ CUSTOM public static Collider2D OverlapArea (Vector2 pointA, Vector2 pointB, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D* collider;
+ const int resultCount = GetPhysics2DManager ().OverlapArea (pointA, pointB, layerMask, minDepth, maxDepth, &collider, 1);
+
+ if (resultCount == 0)
+ return SCRIPTING_NULL;
+
+ return Scripting::ScriptingWrapperFor (collider);
+ }
+
+ // Returns all colliders overlapping the area.
+ CUSTOM public static Collider2D[] OverlapAreaAll (Vector2 pointA, Vector2 pointB, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ dynamic_array<Collider2D*> colliderHits(kMemTempAlloc);
+ GetPhysics2DManager ().OverlapAreaAll (pointA, pointB, layerMask, minDepth, maxDepth, &colliderHits);
+
+ // Generate scripting array.
+ return CreateScriptingArrayFromUnityObjects(colliderHits, ScriptingClassFor(Collider2D));
+ }
+
+ // Returns all colliders overlapping the area (limited by the size of the array). This does not produce any garbage.
+ CUSTOM public static int OverlapAreaNonAlloc (Vector2 pointA, Vector2 pointB, Collider2D[] results, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity)
+ {
+ Collider2D** colliderHits = Scripting::GetScriptingArrayStart<Collider2D*>(results);
+ const int resultCount = GetPhysics2DManager ().OverlapArea (pointA, pointB, layerMask, minDepth, maxDepth, colliderHits, GetScriptingArraySize(results));
+
+ for (size_t i = 0; i < resultCount; ++i)
+ colliderHits[i] = reinterpret_cast<Collider2D*>(ScriptingGetObjectReference (colliderHits[i]));
+
+ return resultCount;
+ }
+
+END
+
+
+// NOTE: must match memory layout of native RaycastHit2D
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT RaycastHit2D
+ CSRAW private Vector2 m_Point;
+ CSRAW private Vector2 m_Normal;
+ CSRAW private float m_Fraction;
+ #if UNITY_WINRT
+ CSRAW private int m_ColliderHandle;
+ #else
+ CSRAW private Collider2D m_Collider;
+ #endif
+
+ CSRAW public Vector2 point { get { return m_Point; } set { m_Point = value; } }
+ CSRAW public Vector2 normal { get { return m_Normal; } set { m_Normal = value; } }
+ CSRAW public float fraction { get { return m_Fraction; } set { m_Fraction = value; } }
+
+ CONDITIONAL !UNITY_WINRT
+ CSRAW public Collider2D collider { get { return m_Collider; } }
+
+ CONDITIONAL UNITY_WINRT
+ CSRAW public Collider2D collider { get { return UnityEngineInternal.ScriptingUtils.GCHandleToObject<Collider2D>(m_ColliderHandle); } }
+
+ CSRAW public Rigidbody2D rigidbody { get { return collider != null ? collider.attachedRigidbody : null; } }
+
+ CSRAW public Transform transform { get {
+ Rigidbody2D body = rigidbody;
+ if (body != null)
+ return body.transform;
+ else if (collider != null)
+ return collider.transform;
+ else
+ return null;
+ } }
+
+ // Implicitly convert a hit to a boolean based upon whether a collider reference exists or not.
+ CSRAW public static implicit operator bool (RaycastHit2D hit) { return hit.collider != null; }
+
+ // Compare the hit by fraction along the ray. If no colliders exist then fraction is moved "up". This allows sorting an array of sparse results.
+ CSRAW public int CompareTo(RaycastHit2D other) { if (collider == null) return 1; if (other.collider == null) return -1; return fraction.CompareTo(other.fraction); }
+END
+
+
+// [[Rigidbody2D]] interpolation mode.
+ENUM RigidbodyInterpolation2D
+ // 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
+
+
+// [[Rigidbody2D]] sleeping mode.
+ENUM RigidbodySleepMode2D
+ // Never sleep.
+ NeverSleep = 0,
+
+ // Start the rigid body awake.
+ StartAwake = 1,
+
+ // Start the rigid body asleep.
+ StartAsleep = 2
+END
+
+
+// [[Rigidbody2D]] collision detection mode.
+ENUM CollisionDetectionMode2D
+ // Provides standard (and least expensive) collision detection suitable for most circumstances.
+ None = 0,
+
+ // Provides the most accurate collision detection to present tunnelling but is more expensive. Use for vert fast moving objects only.
+ Continuous = 1
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS Rigidbody2D : Component
+ // The linear velocity vector of the object.
+ AUTO_PROP Vector2 velocity GetVelocity SetVelocity
+
+ // The angular velocity vector of the object in degrees/sec.
+ AUTO_PROP float angularVelocity GetAngularVelocity SetAngularVelocity
+
+ // The (linear) drag of the object.
+ AUTO_PROP float drag GetDrag SetDrag
+
+ // The angular drag of the object.
+ AUTO_PROP float angularDrag GetAngularDrag SetAngularDrag
+
+ // Controls the effect of gravity on the object.
+ AUTO_PROP float gravityScale GetGravityScale SetGravityScale
+
+ // Controls whether the object is kinematic or dynamic.
+ AUTO_PROP bool isKinematic GetIsKinematic SetIsKinematic
+
+ // Controls whether the objects angle can be changed by collisions with other objects.
+ AUTO_PROP bool fixedAngle IsFixedAngle SetFixedAngle
+
+ // Checks whether the rigid body is sleeping or not.
+ CUSTOM bool IsSleeping() { return self->IsSleeping(); }
+
+ // Checks whether the rigid body is awake or not.
+ CUSTOM bool IsAwake() { return !self->IsSleeping(); }
+
+ // Sets the rigid body into a sleep state.
+ CUSTOM void Sleep() { self->SetSleeping(true); }
+
+ // Wakes the rigid from sleeping.
+ CUSTOM void WakeUp() { self->SetSleeping(false); }
+
+ // Interpolation allows you to smooth out the effect of running physics at a fixed rate.
+ AUTO_PROP RigidbodyInterpolation2D interpolation GetInterpolation SetInterpolation
+
+ // Controls how the object sleeps.
+ AUTO_PROP RigidbodySleepMode2D sleepMode GetSleepMode SetSleepMode
+
+ // The Rigidbody's collision detection mode.
+ AUTO_PROP CollisionDetectionMode2D collisionDetectionMode GetCollisionDetectionMode SetCollisionDetectionMode
+
+ // Controls the mass of the object by adjusting the density of all colliders attached to the object.
+ AUTO_PROP float mass GetMass SetMass
+
+ CUSTOM void AddForce (Vector2 force) { self->AddForce (force); }
+ CUSTOM void AddTorque (float torque) { self->AddTorque (torque); }
+ CUSTOM void AddForceAtPosition (Vector2 force, Vector2 position) { self->AddForceAtPosition (force, position); }
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+NONSEALED_CLASS Collider2D : Behaviour
+ AUTO_PROP bool isTrigger GetIsTrigger SetIsTrigger
+ AUTO_PTR_PROP Rigidbody2D attachedRigidbody GetRigidbody
+
+ // Gets the number of shapes this collider has generated.
+ AUTO_PROP int shapeCount GetShapeCount
+
+ // Checks whether the specified point overlaps the collider or not.
+ CUSTOM public bool OverlapPoint (Vector2 point) { return self->OverlapPoint(point); }
+
+ // The shared physics material of this collider.
+ CUSTOM_PROP PhysicsMaterial2D sharedMaterial { return Scripting::ScriptingWrapperFor (self->GetMaterial ()); } { self->SetMaterial (value); }
+
+ // OnCollisionEnter2D is called when this [[Collider2D]] has begun touching another [[Collider2D]].
+ CSNONE void OnCollisionEnter2D (Collision2D info);
+
+ // OnCollisionStay2D is called once per frame for this [[Collider2D]] if it is continuing to touch another [[Collider2D]].
+ CSNONE void OnCollisionStay2D (Collision2D info);
+
+ // OnCollisionExit2D is called when this [[Collider2D]] has stopped touching another [[Collider2D]].
+ CSNONE void OnCollisionExit2D (Collision2D info);
+
+ // OnTriggerEnter2D is called when a [[Collider2D]] has begun touching another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerEnter2D (Collider2D other);
+
+ // OnTriggerStay2D is called once per frame for a [[Collider2D]] if it is continuing to touch another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerStay2D (Collider2D other);
+
+ // OnTriggerExit2D is called when a [[Collider2D]] has stopped touching another [[Collider2D]] configured as a trigger.
+ CSNONE void OnTriggerExit2D (Collider2D other);
+
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS CircleCollider2D : Collider2D
+ AUTO_PROP Vector2 center GetCenter SetCenter
+ AUTO_PROP float radius GetRadius SetRadius
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS BoxCollider2D : Collider2D
+ AUTO_PROP Vector2 center GetCenter SetCenter
+ AUTO_PROP Vector2 size GetSize SetSize
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS EdgeCollider2D : Collider2D
+
+ // Reset to a single horizontal edge.
+ CUSTOM void Reset() { self->Reset (); }
+
+ // Get the number of edges. This is one less than the number of points.
+ CUSTOM_PROP int edgeCount { { return self->GetEdgeCount (); } }
+
+ // Get the number of points. This cannot be less than two which will form a single edge.
+ CUSTOM_PROP int pointCount { { return self->GetPointCount (); } }
+
+ // Get or set the points defining multiple continuous edges.
+ CUSTOM_PROP Vector2[] points
+ {
+ return CreateScriptingArrayStride<Vector2f>(self->GetPoints ().data (), self->GetPointCount (), MONO_COMMON.vector2, sizeof(Vector2f));
+ }
+ {
+ if (self->SetPoints (Scripting::GetScriptingArrayStart<Vector2f> (value), GetScriptingArraySize (value)))
+ return;
+
+ ErrorString("Invalid points assigned to 2D edge collider.");
+ }
+
+END
+
+
+CONDITIONAL (ENABLE_2D_PHYSICS && ENABLE_SPRITECOLLIDER)
+CLASS SpriteCollider2D : Collider2D
+
+ CUSTOM_PROP Sprite sprite
+ {
+ return Scripting::ScriptingWrapperFor(self->GetSprite());
+ }
+ {
+ self->SetSprite(value);
+ }
+
+END
+
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS PolygonCollider2D : Collider2D
+
+ // Get/Set a single path of points.
+ CUSTOM_PROP Vector2[] points
+ {
+ return CreateScriptingArrayStride<Vector2f>(self->GetPoly().GetPoints(), self->GetPoly().GetPointCount(), MONO_COMMON.vector2, sizeof(*self->GetPoly().GetPoints()));
+ }
+ {
+ self->GetPoly().SetPoints(Scripting::GetScriptingArrayStart<Vector2f>(value), GetScriptingArraySize(value));
+ self->RefreshPoly();
+ }
+
+ // Get the specified path of points.
+ CUSTOM Vector2[] GetPath(int index)
+ {
+ if (index >= self->GetPoly().GetPathCount())
+ {
+ Scripting::RaiseOutOfRangeException("Path %d does not exist.", index);
+ return SCRIPTING_NULL;
+ }
+
+ const Polygon2D::TPath& path = self->GetPoly().GetPath(index);
+ return CreateScriptingArrayStride<Vector2f>(path.data(), path.size(), MONO_COMMON.vector2, sizeof(*path.data()));
+ }
+
+ // Set the specified path of points.
+ CUSTOM void SetPath(int index, Vector2[] points)
+ {
+ const Vector2f* arrayStart = Scripting::GetScriptingArrayStart<Vector2f>(points);
+ const int arraySize = GetScriptingArraySize(points);
+ Polygon2D::TPath path;
+ path.assign(arrayStart, arrayStart+arraySize);
+ self->GetPoly().SetPath(index, path);
+ self->RefreshPoly();
+ }
+
+ // Get the number of paths.
+ CUSTOM_PROP int pathCount { return self->GetPoly().GetPathCount(); } { self->GetPoly().SetPathCount(value); self->RefreshPoly(); }
+
+ // Get the total number of points in all paths.
+ CUSTOM int GetTotalPointCount() { return self->GetPoly().GetTotalPointCount(); }
+
+ // Create a primitive n-sided polygon.
+ CUSTOM void CreatePrimitive(int sides, Vector2 scale = Vector2.one, Vector2 offset = Vector2.zero)
+ {
+ if (sides < 3)
+ {
+ ErrorString ("Cannot create a 2D polygon primitive collider with less than two sides.");
+ return;
+ }
+
+ if (scale.x <= 0.0f || scale.y <= 0.0f)
+ {
+ ErrorString ("Cannot create a 2D polygon primitive collider with an axis scale less than or equal to zero.");
+ return;
+ }
+
+ self->CreatePrimitive (sides, scale, offset);
+ }
+
+END
+
+// Describes a contact point where the collision occurs.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT ContactPoint2D
+ CSRAW internal Vector2 m_Point;
+ CSRAW internal Vector2 m_Normal;
+ CSRAW internal Collider2D m_Collider;
+ CSRAW internal Collider2D m_OtherCollider;
+
+ // The point of contact.
+ CSRAW public Vector2 point { get { return m_Point; } }
+
+ // Normal of the contact point.
+ CSRAW public Vector2 normal { get { return m_Normal; } }
+
+ // The first collider in contact.
+ CSRAW public Collider2D collider { get { return m_Collider; } }
+
+ // The other collider in contact.
+ CSRAW public Collider2D otherCollider { get { return m_OtherCollider; } }
+END
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CSRAW [StructLayout (LayoutKind.Sequential)]
+NONSEALED_CLASS Collision2D
+ CSRAW internal Rigidbody2D m_Rigidbody;
+ CSRAW internal Collider2D m_Collider;
+ CSRAW internal ContactPoint2D[] m_Contacts;
+ CSRAW internal Vector2 m_RelativeVelocity;
+
+ CSRAW public Rigidbody2D rigidbody { get { return m_Rigidbody; } }
+ CSRAW public Collider2D collider { get { return m_Collider; } }
+ CSRAW public Transform transform { get { return rigidbody != null ? rigidbody.transform : collider.transform; } }
+ CSRAW public GameObject gameObject { get { return m_Rigidbody != null ? m_Rigidbody.gameObject : m_Collider.gameObject; } }
+ CSRAW public ContactPoint2D[] contacts { get { return m_Contacts; } }
+ CSRAW public Vector2 relativeVelocity { get { return m_RelativeVelocity; } }
+
+END
+
+
+// JointAngleLimits2D is used by the [[HingeJoint2D]] to limit the joints angle.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT JointAngleLimits2D
+ CSRAW private float m_LowerAngle;
+ CSRAW private float m_UpperAngle;
+
+ // The lower angle limit of the joint.
+ CSRAW public float min { get { return m_LowerAngle; } set { m_LowerAngle = value; } }
+
+ // The upper angle limit of the joint.
+ CSRAW public float max { get { return m_UpperAngle; } set { m_UpperAngle = value; } }
+END
+
+
+// JointTranslationLimits2D is used by the [[SliderJoint2D]] to limit the joints translation.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT JointTranslationLimits2D
+ CSRAW private float m_LowerTranslation;
+ CSRAW private float m_UpperTranslation;
+
+ // The lower translation limit of the joint.
+ CSRAW public float min { get { return m_LowerTranslation; } set { m_LowerTranslation = value; } }
+
+ // The upper translation limit of the joint.
+ CSRAW public float max { get { return m_UpperTranslation; } set { m_UpperTranslation = value; } }
+END
+
+
+// JointMotor2D is used by the [[HingeJoint2D]] and [[SliderJoint2D]] to motorize a joint.
+CONDITIONAL ENABLE_2D_PHYSICS
+STRUCT JointMotor2D
+ CSRAW private float m_MotorSpeed;
+ CSRAW private float m_MaximumMotorTorque;
+
+ // The target motor speed in degrees/second.
+ CSRAW public float motorSpeed { get { return m_MotorSpeed; } set { m_MotorSpeed = value; } }
+
+ // The maximum torque in N-m the motor can use to achieve the desired motor speed.
+ CSRAW public float maxMotorTorque { get { return m_MaximumMotorTorque; } set { m_MaximumMotorTorque = value; } }
+END
+
+
+// Joint2D is the base class for all 2D joints.
+CONDITIONAL ENABLE_2D_PHYSICS
+NONSEALED_CLASS Joint2D : Behaviour
+
+ // A reference to another rigid-body this joint connects to.
+ AUTO_PTR_PROP Rigidbody2D connectedBody GetConnectedBody SetConnectedBody
+
+ // Whether the connected rigid-bodies should collide with each other or not.
+ AUTO_PROP bool collideConnected GetCollideConnected SetCollideConnected
+
+END
+
+// The SpringJoint2D ensures that the two connected rigid-bodies stay at a specific distance apart using a spring system.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS SpringJoint2D : Joint2D
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // The distance the joint should maintain between the two connected rigid-bodies.
+ AUTO_PROP float distance GetDistance SetDistance
+
+ // The damping ratio for the oscillation whilst trying to achieve the specified distance. 0 means no damping. 1 means critical damping. range { 0.0, 1.0 }
+ AUTO_PROP float dampingRatio GetDampingRatio SetDampingRatio
+
+ // The frequency in Hertz for the oscillation whilst trying to achieve the specified distance. range { 0.0, infinity }
+ AUTO_PROP float frequency GetFrequency SetFrequency
+
+END
+
+
+// The DistanceJoint2D ensures that the two connected rigid-bodies stay at a maximum specific distance apart.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS DistanceJoint2D : Joint2D
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // The maximum distance the joint should maintain between the two connected rigid-bodies.
+ AUTO_PROP float distance GetDistance SetDistance
+
+END
+
+
+// The HingeJoint2D constrains the two connected rigid-bodies around the anchor points not restricting the relative rotation of them. Can be used for wheels, rollers, chains, rag-dol joints, levers etc.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS HingeJoint2D : Joint2D
+
+ // Setting the motor or limit automatically enabled them.
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // Enables the joint's motor.
+ AUTO_PROP bool useMotor GetUseMotor SetUseMotor
+
+ // Enables the joint's limits.
+ AUTO_PROP bool useLimits GetUseLimits SetUseLimits
+
+ // The motor will apply a force up to a maximum torque to achieve the target velocity in degrees per second.
+ AUTO_PROP JointMotor2D motor GetMotor SetMotor
+
+ // The limits of the hinge joint.
+ AUTO_PROP JointAngleLimits2D limits GetLimits SetLimits
+
+END
+
+// The SliderJoint2D constrains the two connected rigid-bodies to have on degree of freedom: translation along a fixed axis. Relative motion is prevented.
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS SliderJoint2D : Joint2D
+
+ // Setting the motor or limit automatically enabled them.
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 anchor GetAnchor SetAnchor
+
+ // The Position of the anchor around which the joints motion is constrained.
+ AUTO_PROP Vector2 connectedAnchor GetConnectedAnchor SetConnectedAnchor
+
+ // The translation angle that the joint slides along.
+ AUTO_PROP float angle GetAngle SetAngle
+
+ // Enables the joint's motor.
+ AUTO_PROP bool useMotor GetUseMotor SetUseMotor
+
+ // Enables the joint's limits.
+ AUTO_PROP bool useLimits GetUseLimits SetUseLimits
+
+ // The motor will apply a force up to a maximum torque to achieve the target velocity in degrees per second.
+ AUTO_PROP JointMotor2D motor GetMotor SetMotor
+
+ // The limits of the slider joint.
+ AUTO_PROP JointTranslationLimits2D limits GetLimits SetLimits
+
+END
+
+CONDITIONAL ENABLE_2D_PHYSICS
+CLASS PhysicsMaterial2D : Object
+
+ CUSTOM private static void Internal_Create ([Writable]PhysicsMaterial2D mat, string name)
+ {
+ PhysicsMaterial2D* material = NEW_OBJECT (PhysicsMaterial2D);
+ SmartResetObject(*material);
+ material->SetNameCpp (name);
+ Scripting::ConnectScriptingWrapperToObject (mat.GetScriptingObject(), material);
+ }
+
+ // Creates a new material.
+ CSRAW public PhysicsMaterial2D () { Internal_Create (this,null); }
+
+ // Creates a new material named /name/.
+ CSRAW public PhysicsMaterial2D (string name) { Internal_Create (this,name); }
+
+ // 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
+
+ // The friction.
+ AUTO_PROP float friction GetFriction SetFriction
+
+END
+
+CSRAW }
diff --git a/Runtime/Physics2D/SliderJoint2D.cpp b/Runtime/Physics2D/SliderJoint2D.cpp
new file mode 100644
index 0000000..d802c17
--- /dev/null
+++ b/Runtime/Physics2D/SliderJoint2D.cpp
@@ -0,0 +1,231 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/SliderJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "External/Box2D/Box2D/Box2D.h"
+
+
+IMPLEMENT_CLASS (SliderJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (SliderJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+SliderJoint2D::SliderJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+, m_OldReferenceAngle (std::numeric_limits<float>::infinity ())
+{
+}
+
+
+SliderJoint2D::~SliderJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void SliderJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_Angle);
+ TRANSFER (m_UseMotor);
+ transfer.Align ();
+ TRANSFER (m_Motor);
+ TRANSFER (m_UseLimits);
+ transfer.Align ();
+ TRANSFER (m_TranslationLimits);
+}
+
+
+void SliderJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Motor.CheckConsistency ();
+ m_TranslationLimits.CheckConsistency ();
+
+ m_Angle = clamp<float> (m_Angle, 0.0f, 359.9999f);
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SliderJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Angle = 0.0f;
+ m_UseMotor = false;
+ m_UseLimits = false;
+ m_Motor.Initialize ();
+ m_TranslationLimits.Initialize ();
+
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SliderJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, SliderJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SliderJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, SliderJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SliderJoint2D::SetAngle (float angle)
+{
+ ABORT_INVALID_FLOAT (angle, angle, DistanceJoint2D);
+
+ m_Angle = clamp<float> (angle, 0.0f, 359.9999f);
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SliderJoint2D::SetUseMotor (bool enable)
+{
+ m_UseMotor = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2PrismaticJoint*)m_Joint)->EnableMotor(m_UseMotor);
+}
+
+
+void SliderJoint2D::SetUseLimits (bool enable)
+{
+ m_UseLimits = enable;
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2PrismaticJoint*)m_Joint)->EnableLimit(m_UseLimits);
+}
+
+
+void SliderJoint2D::SetMotor (const JointMotor2D& motor)
+{
+ m_Motor = motor;
+ m_Motor.CheckConsistency ();
+ SetDirty();
+
+ // Motor is automatically enabled if motor is set.
+ SetUseMotor(true);
+
+ if (m_Joint != NULL)
+ {
+ b2PrismaticJoint* joint = (b2PrismaticJoint*)m_Joint;
+ joint->SetMotorSpeed (math::radians (m_Motor.m_MotorSpeed));
+ joint->SetMaxMotorForce (m_Motor.m_MaximumMotorForce);
+ }
+}
+
+
+void SliderJoint2D::SetLimits (const JointTranslationLimits2D& limits)
+{
+ m_TranslationLimits = limits;
+ m_TranslationLimits.CheckConsistency ();
+ SetDirty();
+
+ // Limits ares automatically enabled if limits are set.
+ SetUseLimits(true);
+
+ if (m_Joint != NULL)
+ {
+ b2PrismaticJoint* joint = (b2PrismaticJoint*)m_Joint;
+ joint->SetLimits (m_TranslationLimits.m_LowerTranslation, m_TranslationLimits.m_UpperTranslation);
+ }
+}
+
+
+// --------------------------------------------------------------------------
+
+
+void SliderJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Fetch bodies.
+ b2Body* bodyA = FetchBodyA();
+ b2Body* bodyB = FetchBodyB();
+
+ // Configure the joint definition.
+ b2PrismaticJointDef jointDef;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+ jointDef.enableMotor = m_UseMotor;
+ jointDef.enableLimit = m_UseLimits;
+ jointDef.motorSpeed = math::radians (m_Motor.m_MotorSpeed);
+ jointDef.maxMotorForce = m_Motor.m_MaximumMotorForce;
+ jointDef.lowerTranslation = m_TranslationLimits.m_LowerTranslation;
+ jointDef.upperTranslation = m_TranslationLimits.m_UpperTranslation;
+ jointDef.referenceAngle = m_OldReferenceAngle == std::numeric_limits<float>::infinity () ? bodyB->GetAngle() - bodyA->GetAngle() : m_OldReferenceAngle;
+
+ float angle = math::radians (m_Angle);
+ jointDef.localAxisA = bodyA->GetLocalVector (b2Vec2 (math::sin (angle), -math::cos(angle)));
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+void SliderJoint2D::ReCreate()
+{
+ // Do we have an existing joint and we're still active/enabled?
+ if (m_Joint != NULL && IsActive () && GetEnabled ())
+ {
+ // Yes, so keep reference angle.
+ m_OldReferenceAngle = ((b2RevoluteJoint*)m_Joint)->GetReferenceAngle ();
+ }
+ else
+ {
+ // No, so reset reference angle.
+ m_OldReferenceAngle = std::numeric_limits<float>::infinity ();
+ }
+
+ Super::ReCreate ();
+}
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/SliderJoint2D.h b/Runtime/Physics2D/SliderJoint2D.h
new file mode 100644
index 0000000..6088f44
--- /dev/null
+++ b/Runtime/Physics2D/SliderJoint2D.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class SliderJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (SliderJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (SliderJoint2D)
+
+ SliderJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~SliderJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ float GetAngle () const { return m_Angle; }
+ void SetAngle (float angle);
+
+ bool GetUseMotor () const { return m_UseMotor; }
+ void SetUseMotor (bool enable);
+
+ bool GetUseLimits () const { return m_UseLimits; }
+ void SetUseLimits (bool enable);
+
+ JointMotor2D GetMotor () const { return m_Motor; }
+ void SetMotor (const JointMotor2D& motor);
+
+ JointTranslationLimits2D GetLimits () const { return m_TranslationLimits; }
+ void SetLimits (const JointTranslationLimits2D& limits);
+
+protected:
+ virtual void Create ();
+ virtual void ReCreate();
+
+protected:
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+ float m_Angle; ///< The translation angle that the joint slides along. range { 0.0, 359.9999 }
+ JointMotor2D m_Motor; ///< The joint motor.
+ JointTranslationLimits2D m_TranslationLimits; ///< The joint angle limits.
+ bool m_UseMotor; ///< Whether to use the joint motor or not.
+ bool m_UseLimits; ///< Whether to use the angle limits or not.
+
+private:
+ float m_OldReferenceAngle;
+};
+
+#endif
diff --git a/Runtime/Physics2D/SpringJoint2D.cpp b/Runtime/Physics2D/SpringJoint2D.cpp
new file mode 100644
index 0000000..cfbc6a0
--- /dev/null
+++ b/Runtime/Physics2D/SpringJoint2D.cpp
@@ -0,0 +1,161 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Physics2D/SpringJoint2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Utilities/ValidateArgs.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+
+IMPLEMENT_CLASS (SpringJoint2D)
+IMPLEMENT_OBJECT_SERIALIZE (SpringJoint2D)
+
+
+// --------------------------------------------------------------------------
+
+
+SpringJoint2D::SpringJoint2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+SpringJoint2D::~SpringJoint2D ()
+{
+}
+
+
+template<class TransferFunction>
+void SpringJoint2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer(transfer);
+
+ TRANSFER (m_Anchor);
+ TRANSFER (m_ConnectedAnchor);
+ TRANSFER (m_Distance);
+ TRANSFER (m_DampingRatio);
+ TRANSFER (m_Frequency);
+}
+
+
+void SpringJoint2D::CheckConsistency ()
+{
+ Super::CheckConsistency ();
+
+ m_Distance = clamp<float> (m_Distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_Frequency = clamp<float> (m_Frequency, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ m_DampingRatio = clamp(m_DampingRatio, 0.0f, 1.0f);
+
+ if (!IsFinite(m_Anchor))
+ m_Anchor = Vector2f::zero;
+
+ if (!IsFinite(m_ConnectedAnchor))
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SpringJoint2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Distance = 1.0f;
+ m_DampingRatio = 0.0f;
+ m_Frequency = 10.0f;
+ m_Anchor = Vector2f::zero;
+ m_ConnectedAnchor = Vector2f::zero;
+}
+
+
+void SpringJoint2D::SetAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, anchor, SpringJoint2D);
+
+ m_Anchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SpringJoint2D::SetConnectedAnchor (const Vector2f& anchor)
+{
+ ABORT_INVALID_VECTOR2 (anchor, connectedAnchor, SpringJoint2D);
+
+ m_ConnectedAnchor = anchor;
+ SetDirty();
+
+ // Recreate the joint.
+ if (m_Joint != NULL)
+ ReCreate();
+}
+
+
+void SpringJoint2D::SetDistance (float distance)
+{
+ ABORT_INVALID_FLOAT (distance, distance, SpringJoint2D);
+
+ m_Distance = clamp<float> (distance, b2_linearSlop, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2DistanceJoint*)m_Joint)->SetLength (m_Distance);
+}
+
+
+void SpringJoint2D::SetDampingRatio (float ratio)
+{
+ ABORT_INVALID_FLOAT (ratio, dampingRatio, SpringJoint2D);
+
+ m_DampingRatio = clamp(ratio, 0.0f, 1.0f);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2DistanceJoint*)m_Joint)->SetDampingRatio (m_DampingRatio);
+}
+
+
+void SpringJoint2D::SetFrequency (float frequency)
+{
+ ABORT_INVALID_FLOAT (frequency, frequency, SpringJoint2D);
+
+ m_Frequency = clamp<float> (frequency, 0.0f, PHYSICS_2D_LARGE_RANGE_CLAMP);
+ SetDirty();
+
+ if (m_Joint != NULL)
+ ((b2DistanceJoint*)m_Joint)->SetFrequency (m_Frequency);
+}
+
+// --------------------------------------------------------------------------
+
+
+void SpringJoint2D::Create ()
+{
+ Assert (m_Joint == NULL);
+
+ if (!IsActive ())
+ return;
+
+ // Fetch transform scales.
+ const Vector3f scale = GetComponent (Transform).GetWorldScaleLossy ();
+ const Vector3f connectedScale = m_ConnectedRigidBody.IsNull () ? Vector3f::one : m_ConnectedRigidBody->GetComponent (Transform).GetWorldScaleLossy ();
+
+ // Configure the joint definition.
+ b2DistanceJointDef jointDef;
+ jointDef.dampingRatio = m_DampingRatio;
+ jointDef.frequencyHz = m_Frequency;
+ jointDef.length = m_Distance;
+ jointDef.localAnchorA.Set (m_Anchor.x * scale.x, m_Anchor.y * scale.y);
+ jointDef.localAnchorB.Set (m_ConnectedAnchor.x * connectedScale.x, m_ConnectedAnchor.y * connectedScale.y);
+
+ // Create the joint.
+ FinalizeCreateJoint (&jointDef);
+}
+
+
+#endif //ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/SpringJoint2D.h b/Runtime/Physics2D/SpringJoint2D.h
new file mode 100644
index 0000000..6d46cd4
--- /dev/null
+++ b/Runtime/Physics2D/SpringJoint2D.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#if ENABLE_2D_PHYSICS || DOXYGEN
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Physics2D/Joint2D.h"
+
+class Vector2f;
+
+
+// --------------------------------------------------------------------------
+
+
+class SpringJoint2D : public Joint2D
+{
+public:
+
+ REGISTER_DERIVED_CLASS (SpringJoint2D, Joint2D)
+ DECLARE_OBJECT_SERIALIZE (SpringJoint2D)
+
+ SpringJoint2D (MemLabelId label, ObjectCreationMode mode);
+ // virtual ~SpringJoint2D (); declared-by-macro
+
+ virtual void CheckConsistency();
+ virtual void Reset ();
+
+ Vector2f GetAnchor () const { return m_Anchor; }
+ virtual void SetAnchor (const Vector2f& anchor);
+
+ Vector2f GetConnectedAnchor () const { return m_ConnectedAnchor; }
+ virtual void SetConnectedAnchor (const Vector2f& anchor);
+
+ void SetDistance (float distance);
+ float GetDistance () const { return m_Distance; }
+
+ void SetDampingRatio (float ratio);
+ float GetDampingRatio () const { return m_DampingRatio; }
+
+ void SetFrequency (float frequency);
+ float GetFrequency () const { return m_Frequency; }
+
+protected:
+ virtual void Create ();
+ void AutoCalculateDistance ();
+
+protected:
+ float m_Distance; ///< The distance which the joint should attempt to maintain between attached bodies. range { 0.005, 1000000 }
+ float m_DampingRatio; ///< The damping ratio for the oscillation whilst trying to achieve the specified distance. 0 means no damping. 1 means critical damping. range { 0.0, 1.0 }
+ float m_Frequency; ///< The frequency in Hertz for the oscillation whilst trying to achieve the specified distance. range { 0.0, 1000000 }
+ Vector2f m_Anchor; ///< The local-space anchor from the base rigid-body.
+ Vector2f m_ConnectedAnchor; ///< The local-space anchor from the connected rigid-body.
+};
+
+#endif
diff --git a/Runtime/Physics2D/SpriteCollider2D.cpp b/Runtime/Physics2D/SpriteCollider2D.cpp
new file mode 100644
index 0000000..772bf0d
--- /dev/null
+++ b/Runtime/Physics2D/SpriteCollider2D.cpp
@@ -0,0 +1,91 @@
+#include "UnityPrefix.h"
+
+#if ENABLE_2D_PHYSICS && ENABLE_SPRITECOLLIDER
+
+#include "Runtime/Physics2D/SpriteCollider2D.h"
+#include "Runtime/Physics2D/RigidBody2D.h"
+#include "Runtime/Physics2D/Physics2DManager.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/Filters/Mesh/SpriteRenderer.h"
+
+#include "External/Box2D/Box2D/Box2D.h"
+#include "External/libtess2/libtess2/tesselator.h"
+
+PROFILER_INFORMATION(gPhysics2DProfileSpriteColliderCreate, "Physics2D.SpriteColliderCreate", kProfilerPhysics)
+PROFILER_INFORMATION(gPhysics2DProfileSpriteColliderDecomposition, "Physics2D.SpriteColliderDecomposition", kProfilerPhysics)
+
+IMPLEMENT_CLASS_INIT_ONLY (SpriteCollider2D)
+IMPLEMENT_OBJECT_SERIALIZE (SpriteCollider2D)
+
+
+// --------------------------------------------------------------------------
+
+
+static Polygon2D gEmptyPolygon2D;
+void SpriteCollider2D::InitializeClass()
+{
+ gEmptyPolygon2D.Clear();
+}
+
+
+SpriteCollider2D::SpriteCollider2D (MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode)
+{
+}
+
+
+SpriteCollider2D::~SpriteCollider2D ()
+{
+}
+
+
+template<class TransferFunction>
+void SpriteCollider2D::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ TRANSFER (m_Sprite);
+}
+
+
+void SpriteCollider2D::Reset ()
+{
+ Super::Reset ();
+
+ m_Sprite = NULL;
+}
+
+
+void SpriteCollider2D::SmartReset ()
+{
+ Super::SmartReset ();
+#if UNITY_EDITOR
+ GameObject* go = GetGameObjectPtr();
+ if (go)
+ {
+ SpriteRenderer* sr = go->QueryComponent(SpriteRenderer);
+ if (sr)
+ m_Sprite = sr->GetSprite();
+ }
+#endif
+}
+
+void SpriteCollider2D::SetSprite(PPtr<Sprite> sprite)
+{
+ if (m_Sprite != sprite)
+ {
+ m_Sprite = sprite;
+
+ Create();
+ SetDirty();
+ }
+}
+
+const Polygon2D& SpriteCollider2D::GetPoly() const
+{
+ return m_Sprite.IsNull() ? gEmptyPolygon2D : m_Sprite->GetPoly();
+}
+
+#endif // #if ENABLE_2D_PHYSICS
diff --git a/Runtime/Physics2D/SpriteCollider2D.h b/Runtime/Physics2D/SpriteCollider2D.h
new file mode 100644
index 0000000..948b296
--- /dev/null
+++ b/Runtime/Physics2D/SpriteCollider2D.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#if (ENABLE_2D_PHYSICS || DOXYGEN) && ENABLE_SPRITECOLLIDER
+
+#include "Runtime/Math/Vector2.h"
+#include "Runtime/Utilities/dynamic_array.h"
+#include "Runtime/Graphics/SpriteFrame.h"
+#include "Runtime/Physics2D/PolygonColliderBase2D.h"
+
+class Sprite;
+
+
+// --------------------------------------------------------------------------
+
+
+class SpriteCollider2D : public PolygonColliderBase2D
+{
+public:
+ REGISTER_DERIVED_CLASS (SpriteCollider2D, PolygonColliderBase2D)
+ DECLARE_OBJECT_SERIALIZE (SpriteCollider2D)
+
+ SpriteCollider2D (MemLabelId label, ObjectCreationMode mode);
+ static void InitializeClass();
+
+ virtual const Polygon2D& GetPoly() const;
+
+ virtual void Reset ();
+ virtual void SmartReset ();
+
+ PPtr<Sprite> GetSprite() const { return m_Sprite; }
+ void SetSprite(PPtr<Sprite> sprite);
+
+private:
+ PPtr<Sprite> m_Sprite;
+};
+
+#endif