Browse Source

Implementation of one way collisions for chain and edge shapes

Mike Lilligreen 11 years ago
parent
commit
a4e0b1c7b7

+ 85 - 6
engine/source/2d/scene/ContactFilter.cc

@@ -21,33 +21,42 @@
 //-----------------------------------------------------------------------------
 
 #include "ContactFilter.h"
-#include "2d/sceneobject/SceneObject.h"
 
 // Debug Profiling.
 #include "debug/profiler.h"
 
 //-----------------------------------------------------------------------------
 
-bool ContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB)
+bool ContactFilter::ShouldCollide(b2Fixture* pFixtureA, b2Fixture* pFixtureB)
 {
     // Debug Profiling.
     PROFILE_SCOPE(ContactFilter_ShouldCollide);
 
-    PhysicsProxy* pPhysicsProxyA = static_cast<PhysicsProxy*>(fixtureA->GetBody()->GetUserData());
-    PhysicsProxy* pPhysicsProxyB = static_cast<PhysicsProxy*>(fixtureB->GetBody()->GetUserData());
+    PhysicsProxy* pPhysicsProxyA = static_cast<PhysicsProxy*>(pFixtureA->GetBody()->GetUserData());
+    PhysicsProxy* pPhysicsProxyB = static_cast<PhysicsProxy*>(pFixtureB->GetBody()->GetUserData());
 
     // If not scene objects then cannot collide.
     if ( pPhysicsProxyA->getPhysicsProxyType() != PhysicsProxy::PHYSIC_PROXY_SCENEOBJECT ||
          pPhysicsProxyB->getPhysicsProxyType() != PhysicsProxy::PHYSIC_PROXY_SCENEOBJECT )
          return false;
 
-    const SceneObject* pSceneObjectA = static_cast<SceneObject*>(pPhysicsProxyA);
-    const SceneObject* pSceneObjectB = static_cast<SceneObject*>(pPhysicsProxyB);
+    SceneObject* pSceneObjectA = static_cast<SceneObject*>(pPhysicsProxyA);
+    SceneObject* pSceneObjectB = static_cast<SceneObject*>(pPhysicsProxyB);
 
     // No contact if either objects are suppressing collision.
     if ( pSceneObjectA->mCollisionSuppress || pSceneObjectB->mCollisionSuppress )
         return false;
 
+    // Check collision rules for one way shapes.
+    if ( pSceneObjectA->mCollisionOneWay || pSceneObjectB->mCollisionOneWay )
+    {
+        // Filter out one way collisions.
+        bool result = FilterOneWay(pSceneObjectA, pSceneObjectB, pFixtureA, pFixtureB);
+
+        if (result)
+            return false;
+    }
+
     // Check collision rule A -> B.
     if ( (pSceneObjectA->mCollisionGroupMask & pSceneObjectB->mSceneGroupMask) != 0 &&
          (pSceneObjectA->mCollisionLayerMask & pSceneObjectB->mSceneLayerMask) != 0 )
@@ -60,3 +69,73 @@ bool ContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB)
 
     return false;
 }
+
+//-----------------------------------------------------------------------------
+
+bool ContactFilter::FilterOneWay(SceneObject* pSceneObjectA, SceneObject* pSceneObjectB, b2Fixture* pFixtureA, b2Fixture* pFixtureB)
+{
+    // One way collisions only apply to edge or chain shapes.
+    if ((pFixtureA->GetType() == b2Shape::Type::e_chain || pFixtureA->GetType() == b2Shape::Type::e_edge) ||
+        (pFixtureB->GetType() == b2Shape::Type::e_chain || pFixtureB->GetType() == b2Shape::Type::e_edge))
+    {
+        // Convenience renaming.
+        SceneObject* pPlatformObject = NULL;
+        SceneObject* pMovingObject = NULL;
+        b2Fixture* pFixturePlatform = NULL;
+        b2Fixture* pFixtureObject = NULL;
+
+        if (pSceneObjectA->mCollisionOneWay)
+        {
+            pPlatformObject = pSceneObjectA;
+            pMovingObject = pSceneObjectB;
+            pFixturePlatform = pFixtureA;
+            pFixtureObject = pFixtureB;
+        }
+        else if (pSceneObjectB->mCollisionOneWay)
+        {
+            pPlatformObject = pSceneObjectB;
+            pMovingObject = pSceneObjectA;
+            pFixturePlatform = pFixtureB;
+            pFixtureObject = pFixtureA;
+        }
+
+        // Attempting to "cheat" by just getting a bounding box for the shape and using that center.
+        b2Vec2 shapeCentroid;
+        b2AABB* box = new b2AABB();
+
+        if (pFixturePlatform->GetType() == b2Shape::Type::e_chain)
+        {
+            const b2ChainShape* shape = pPlatformObject->getCollisionChainShape(0);
+            shape->ComputeAABB(box, pPlatformObject->getTransform(), 0);
+            shapeCentroid = box->GetCenter();
+        }
+        else
+        {
+            const b2EdgeShape* shape = pPlatformObject->getCollisionEdgeShape(0);
+            shape->ComputeAABB(box, pPlatformObject->getTransform(), 0);
+            shapeCentroid = box->GetCenter();
+        }
+
+        // We no longer need the bounding box, so delete it.
+        delete(box);
+
+        // Get normalized vector from the position of the platform shape to platform object.
+        b2Vec2 nPosition = pPlatformObject->getPosition();
+        nPosition = nPosition - shapeCentroid;
+        nPosition.Normalize();
+
+        // Get normalized velocity vector of the moving object.
+        b2Vec2 nVelocity = pMovingObject->getLinearVelocity();
+        nVelocity.Normalize();
+
+        // Calculate the dot product.
+        F32 product = b2Dot(nPosition, nVelocity);
+
+        // If the result is less than zero, we have a pass through condition so flag as true.
+        if (product < 0.0f)
+            return true;
+    }
+
+    // The moving object should collide with platform, so flag as false.
+    return false;
+}

+ 6 - 1
engine/source/2d/scene/ContactFilter.h

@@ -27,11 +27,16 @@
 #include "Box2D/Box2D.h"
 #endif
 
+#ifndef _SCENE_OBJECT_H_
+#include "2d/sceneobject/SceneObject.h"
+#endif
+
 //-----------------------------------------------------------------------------
 
 class ContactFilter : public b2ContactFilter
 {
-    virtual bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB);
+    virtual bool ShouldCollide(b2Fixture* pFixtureA, b2Fixture* pFixtureB);
+    virtual bool FilterOneWay(SceneObject* pSceneObjectA, SceneObject* pSceneObjectB, b2Fixture* pFixtureA, b2Fixture* pFixtureB);
 };
 
 #endif //_CONTACT_FILTER_H_

+ 3 - 0
engine/source/2d/sceneobject/SceneObject.cc

@@ -137,6 +137,7 @@ SceneObject::SceneObject() :
     mCollisionLayerMask(MASK_ALL),
     mCollisionGroupMask(MASK_ALL),
     mCollisionSuppress(false),
+    mCollisionOneWay(false),
     mGatherContacts(false),
     mpCurrentContacts(NULL),
 
@@ -285,6 +286,7 @@ void SceneObject::initPersistFields()
     addProtectedField("CollisionGroups", TypeS32, Offset(mCollisionGroupMask, SceneObject), &setCollisionGroups, &getCollisionGroups, &writeCollisionGroups, "");
     addProtectedField("CollisionLayers", TypeS32, Offset(mCollisionLayerMask, SceneObject), &setCollisionLayers, &getCollisionLayers, &writeCollisionLayers, "");
     addField("CollisionSuppress", TypeBool, Offset(mCollisionSuppress, SceneObject), &writeCollisionSuppress, "");
+    addField("CollisionOneWay", TypeBool, Offset(mCollisionOneWay, SceneObject), &writeCollisionOneWay, "");
     addProtectedField("GatherContacts", TypeBool, NULL, &setGatherContacts, &defaultProtectedGetFn, &writeGatherContacts, "");
     addProtectedField("DefaultDensity", TypeF32, Offset( mDefaultFixture.density, SceneObject), &setDefaultDensity, &defaultProtectedGetFn, &writeDefaultDensity, "");
     addProtectedField("DefaultFriction", TypeF32, Offset( mDefaultFixture.friction, SceneObject), &setDefaultFriction, &defaultProtectedGetFn, &writeDefaultFriction, "");
@@ -2837,6 +2839,7 @@ void SceneObject::copyTo( SimObject* obj )
     pSceneObject->setCollisionGroupMask( getCollisionGroupMask() );
     pSceneObject->setCollisionLayerMask( getCollisionLayerMask() );
     pSceneObject->setCollisionSuppress( getCollisionSuppress() );
+    pSceneObject->setCollisionOneWay( getCollisionOneWay() );
     pSceneObject->setGatherContacts( getGatherContacts() );
     pSceneObject->setDefaultDensity( getDefaultDensity() );
     pSceneObject->setDefaultFriction( getDefaultFriction() );

+ 4 - 0
engine/source/2d/sceneobject/SceneObject.h

@@ -161,6 +161,7 @@ protected:
     U32                     mCollisionLayerMask;
     U32                     mCollisionGroupMask;
     bool                    mCollisionSuppress;
+    bool                    mCollisionOneWay;
     b2FixtureDef            mDefaultFixture;
     bool                    mGatherContacts;
     Scene::typeContactVector* mpCurrentContacts;
@@ -368,6 +369,8 @@ public:
     inline F32              getDefaultRestitution( void ) const         { return mDefaultFixture.restitution; }
     inline void             setCollisionSuppress( const bool status )   { mCollisionSuppress = status; }
     inline bool             getCollisionSuppress(void) const            { return mCollisionSuppress; }
+    inline void             setCollisionOneWay( const bool status )     { mCollisionOneWay = status; }
+    inline bool             getCollisionOneWay(void) const              { return mCollisionOneWay; }
     inline const Scene::typeContactVector* getCurrentContacts( void ) const    { return mpCurrentContacts; }
     inline U32              getCurrentContactCount( void ) const        { if ( mpCurrentContacts != NULL ) return mpCurrentContacts->size(); else return 0; }
     virtual void            setGatherContacts( const bool gatherContacts ) { mGatherContacts = gatherContacts; initializeContactGathering(); }
@@ -665,6 +668,7 @@ protected:
     static const char*      getCollisionLayers(void* obj, const char* data) { return Utility::mConvertMaskToString( static_cast<SceneObject*>(obj)->getCollisionLayerMask() ); }
     static bool             writeCollisionLayers( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getCollisionLayerMask() != MASK_ALL; }
     static bool             writeCollisionSuppress( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getCollisionSuppress() == true; }
+    static bool             writeCollisionOneWay( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getCollisionOneWay() == true; }
     static bool             setGatherContacts(void* obj, const char* data)  { static_cast<SceneObject*>(obj)->setGatherContacts(dAtoi(data)); return false; }
     static bool             writeGatherContacts( void* obj, StringTableEntry pFieldName ) { return static_cast<SceneObject*>(obj)->getGatherContacts() == true; }
 

+ 22 - 1
engine/source/2d/sceneobject/SceneObject_ScriptBinding.h

@@ -1,4 +1,4 @@
-//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
 // Copyright (c) 2013 GarageGames, LLC
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -1055,6 +1055,27 @@ ConsoleMethodWithDocs(SceneObject, getCollisionSuppress, ConsoleBool, 2, 2, ())
 
 //-----------------------------------------------------------------------------
 
+/*! Sets the one way collision status for chain and edge shapes that belong to this object.
+    @param status Whether collisions happen only in one direction or not (defaults to false).
+    @return No return value.
+*/
+ConsoleMethodWithDocs(SceneObject, setCollisionOneWay, ConsoleVoid, 2, 3, ([bool status?]))
+{
+    object->setCollisionOneWay( argc > 2 ? dAtob(argv[2]) : true );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the one way collision status for chain and edge shapes that belong to this object.
+    @return (bool status) Whether collisions happen only in one direction or not.
+*/
+ConsoleMethodWithDocs(SceneObject, getCollisionOneWay, ConsoleBool, 2, 2, ())
+{
+    return object->getCollisionOneWay();
+}
+
+//-----------------------------------------------------------------------------
+
 /*! Sets whether to gather contacts or not.
     @param gatherContacts Whether to gather contacts or not.  By default contact gather is off as it can become expensive if a lot of contacts are being processed.
     @return No return value.