Browse Source

Created implementations of BroadPhaseLayerInterface, ObjectVsBroadPhaseLayerFilter and ObjectLayerPairFilter that use a bit table internally (#771)

Jorrit Rouwe 1 year ago
parent
commit
615444cb31

+ 3 - 0
Jolt/Jolt.cmake

@@ -203,9 +203,11 @@ set(JOLT_PHYSICS_SRC_FILES
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseBruteForce.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayer.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayer.h
+	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuadTree.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuery.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/BroadPhaseQuery.h
+	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/BroadPhase/QuadTree.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/CastConvexVsTriangles.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/CastConvexVsTriangles.cpp
@@ -240,6 +242,7 @@ set(JOLT_PHYSICS_SRC_FILES
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/NarrowPhaseStats.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayer.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayer.h
+	${JOLT_PHYSICS_ROOT}/Physics/Collision/ObjectLayerPairFilterTable.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterial.h
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterialSimple.cpp
 	${JOLT_PHYSICS_ROOT}/Physics/Collision/PhysicsMaterialSimple.cpp

+ 62 - 0
Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h

@@ -0,0 +1,62 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
+
+JPH_NAMESPACE_BEGIN
+
+/// BroadPhaseLayerInterface implementation.
+/// This defines a mapping between object and broadphase layers.
+/// This implementation uses a simple table
+class BroadPhaseLayerInterfaceTable : public BroadPhaseLayerInterface
+{
+public:
+	JPH_OVERRIDE_NEW_DELETE
+
+							BroadPhaseLayerInterfaceTable(uint inNumObjectLayers, uint inNumBroadPhaseLayers) :
+		mNumBroadPhaseLayers(inNumBroadPhaseLayers)
+	{
+		mObjectToBroadPhase.resize(inNumObjectLayers, BroadPhaseLayer(0));
+#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+		mBroadPhaseLayerNames.resize(inNumBroadPhaseLayers, "Undefined");
+#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
+	}
+
+	void					MapObjectToBroadPhaseLayer(ObjectLayer inObjectLayer, BroadPhaseLayer inBroadPhaseLayer)
+	{
+		JPH_ASSERT((BroadPhaseLayer::Type)inBroadPhaseLayer < mNumBroadPhaseLayers);
+		mObjectToBroadPhase[inObjectLayer] = inBroadPhaseLayer;
+	}
+
+	virtual uint			GetNumBroadPhaseLayers() const override
+	{
+		return mNumBroadPhaseLayers;
+	}
+
+	virtual BroadPhaseLayer	GetBroadPhaseLayer(ObjectLayer inLayer) const override
+	{
+		return mObjectToBroadPhase[inLayer];
+	}
+
+#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+	void					SetBroadPhaseLayerName(BroadPhaseLayer inLayer, const char *inName)
+	{
+		mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer] = inName;
+	}
+
+	virtual const char *	GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override
+	{
+		return mBroadPhaseLayerNames[(BroadPhaseLayer::Type)inLayer];
+	}
+#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
+
+private:
+	uint					mNumBroadPhaseLayers;
+	Array<BroadPhaseLayer>	mObjectToBroadPhase;
+#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+	Array<const char *>		mBroadPhaseLayerNames;
+#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
+};
+
+JPH_NAMESPACE_END

+ 64 - 0
Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h

@@ -0,0 +1,64 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
+
+JPH_NAMESPACE_BEGIN
+
+/// Class that determines if an object layer can collide with a broadphase layer.
+/// This implementation uses a table and constructs itself from an ObjectLayerPairFilter and a BroadPhaseLayerInterface.
+class ObjectVsBroadPhaseLayerFilterTable : public ObjectVsBroadPhaseLayerFilter
+{
+private:
+	/// Get which bit corresponds to the pair (inLayer1, inLayer2)
+	uint					GetBit(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const
+	{
+		// Calculate at which bit the entry for this pair resides
+		return inLayer1 * mNumBroadPhaseLayers + (BroadPhaseLayer::Type)inLayer2;
+	}
+
+public:
+	JPH_OVERRIDE_NEW_DELETE
+
+	/// Construct the table
+	/// @param inBroadPhaseLayerInterface The broad phase layer interface that maps object layers to broad phase layers
+	/// @param inNumBroadPhaseLayers Number of broad phase layers
+	/// @param inObjectLayerPairFilter The object layer pair filter that determines which object layers can collide
+	/// @param inNumObjectLayers Number of object layers
+							ObjectVsBroadPhaseLayerFilterTable(const BroadPhaseLayerInterface &inBroadPhaseLayerInterface, uint inNumBroadPhaseLayers, const ObjectLayerPairFilter &inObjectLayerPairFilter, uint inNumObjectLayers) :
+		mNumBroadPhaseLayers(inNumBroadPhaseLayers)
+	{
+		// Resize table and set all entries to false
+		mTable.resize((inNumBroadPhaseLayers * inNumObjectLayers + 7) / 8, 0);
+
+		// Loop over all object layer pairs
+		for (ObjectLayer o1 = 0; o1 < inNumObjectLayers; ++o1)
+			for (ObjectLayer o2 = 0; o2 < inNumObjectLayers; ++o2)
+			{
+				// Get the broad phase layer for the second object layer
+				BroadPhaseLayer b2 = inBroadPhaseLayerInterface.GetBroadPhaseLayer(o2);
+				JPH_ASSERT((BroadPhaseLayer::Type)b2 < inNumBroadPhaseLayers);
+
+				// If the object layers collide then so should the object and broadphase layer
+				if (inObjectLayerPairFilter.ShouldCollide(o1, o2))
+				{
+					uint bit = GetBit(o1, b2);
+					mTable[bit >> 3] |= 1 << (bit & 0b111);
+				}
+			}
+	}
+
+	/// Returns true if an object layer should collide with a broadphase layer
+	virtual bool			ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override
+	{
+		uint bit = GetBit(inLayer1, inLayer2);
+		return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0;
+	}
+
+private:
+	uint					mNumBroadPhaseLayers;						///< The total number of broadphase layers
+	Array<uint8>			mTable;										///< The table of bits that indicates which layers collide
+};
+
+JPH_NAMESPACE_END

+ 74 - 0
Jolt/Physics/Collision/ObjectLayerPairFilterTable.h

@@ -0,0 +1,74 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include <Jolt/Physics/Collision/ObjectLayer.h>
+
+JPH_NAMESPACE_BEGIN
+
+/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs.
+/// This implementation uses a table to determine if two layers can collide.
+class ObjectLayerPairFilterTable : public ObjectLayerPairFilter
+{
+private:
+	/// Get which bit corresponds to the pair (inLayer1, inLayer2)
+	uint					GetBit(ObjectLayer inLayer1, ObjectLayer inLayer2) const
+	{
+		// We store the lower left half only, so swap the inputs when trying to access the top right half
+		if (inLayer1 > inLayer2)
+			swap(inLayer1, inLayer2);
+
+		JPH_ASSERT(inLayer2 < mNumObjectLayers);
+
+		// Calculate at which bit the entry for this pair resides
+		// We use the fact that a row always starts at inLayer2 * inLayer2 / 2
+		// (this is the amount of bits needed to store a table of inLayer2 entries)
+		return (inLayer2 * inLayer2) / 2 + inLayer1;
+	}
+
+public:
+	JPH_OVERRIDE_NEW_DELETE
+
+	/// Constructs the table with inNumObjectLayers Layers, initially all layer pairs are disabled
+	explicit				ObjectLayerPairFilterTable(uint inNumObjectLayers) :
+		mNumObjectLayers(inNumObjectLayers)
+	{
+		// By default everything collides
+		int table_size = (inNumObjectLayers * inNumObjectLayers / 2 + 7) / 8;
+		mTable.resize(table_size, 0);
+	}
+
+	/// Get the number of object layers
+	uint					GetNumObjectLayers() const
+	{
+		return mNumObjectLayers;
+	}
+
+	/// Disable collision between two object layers
+	void					DisableCollision(ObjectLayer inLayer1, ObjectLayer inLayer2)
+	{
+		uint bit = GetBit(inLayer1, inLayer2);
+		mTable[bit >> 3] &= (0xff ^ (1 << (bit & 0b111)));
+	}
+
+	/// Enable collision between two object layers
+	void					EnableCollision(ObjectLayer inLayer1, ObjectLayer inLayer2)
+	{
+		uint bit = GetBit(inLayer1, inLayer2);
+		mTable[bit >> 3] |= 1 << (bit & 0b111);
+	}
+
+	/// Returns true if two layers can collide
+	virtual bool			ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override
+	{
+		// Test if the bit is set for this group pair
+		uint bit = GetBit(inObject1, inObject2);
+		return (mTable[bit >> 3] & (1 << (bit & 0b111))) != 0;
+	}
+
+private:
+	uint					mNumObjectLayers;							///< The number of layers that this table supports
+	Array<uint8>			mTable;										///< The table of bits that indicates which layers collide
+};
+
+JPH_NAMESPACE_END

+ 106 - 0
UnitTests/Physics/ObjectLayerPairFilterTableTests.cpp

@@ -0,0 +1,106 @@
+// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
+// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
+// SPDX-License-Identifier: MIT
+
+#include "UnitTestFramework.h"
+#include "Layers.h"
+#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayerInterfaceTable.h>
+#include <Jolt/Physics/Collision/BroadPhase/ObjectVsBroadPhaseLayerFilterTable.h>
+#include <Jolt/Physics/Collision/ObjectLayerPairFilterTable.h>
+
+TEST_SUITE("ObjectLayerPairFilterTableTests")
+{
+	TEST_CASE("ObjectLayerPairFilterTableTest")
+	{
+		// Init object layers
+		ObjectLayerPairFilterTable obj_vs_obj_filter(Layers::NUM_LAYERS);
+		obj_vs_obj_filter.EnableCollision(Layers::MOVING, Layers::NON_MOVING);
+		obj_vs_obj_filter.EnableCollision(Layers::MOVING, Layers::MOVING);
+		obj_vs_obj_filter.EnableCollision(Layers::MOVING, Layers::SENSOR);
+		obj_vs_obj_filter.EnableCollision(Layers::LQ_DEBRIS, Layers::NON_MOVING);
+		obj_vs_obj_filter.EnableCollision(Layers::HQ_DEBRIS, Layers::NON_MOVING);
+		obj_vs_obj_filter.EnableCollision(Layers::HQ_DEBRIS, Layers::MOVING);
+
+		// Check collision pairs
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::NON_MOVING, Layers::NON_MOVING));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::NON_MOVING, Layers::MOVING));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::NON_MOVING, Layers::HQ_DEBRIS));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::NON_MOVING, Layers::LQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::NON_MOVING, Layers::SENSOR));
+
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::MOVING, Layers::NON_MOVING));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::MOVING, Layers::MOVING));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::MOVING, Layers::HQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::MOVING, Layers::LQ_DEBRIS));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::MOVING, Layers::SENSOR));
+
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::HQ_DEBRIS, Layers::NON_MOVING));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::HQ_DEBRIS, Layers::MOVING));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::HQ_DEBRIS, Layers::HQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::HQ_DEBRIS, Layers::LQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::HQ_DEBRIS, Layers::SENSOR));
+
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::LQ_DEBRIS, Layers::NON_MOVING));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::LQ_DEBRIS, Layers::MOVING));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::LQ_DEBRIS, Layers::HQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::LQ_DEBRIS, Layers::LQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::LQ_DEBRIS, Layers::SENSOR));
+
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::SENSOR, Layers::NON_MOVING));
+		CHECK(obj_vs_obj_filter.ShouldCollide(Layers::SENSOR, Layers::MOVING));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::SENSOR, Layers::HQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::SENSOR, Layers::LQ_DEBRIS));
+		CHECK(!obj_vs_obj_filter.ShouldCollide(Layers::SENSOR, Layers::SENSOR));
+
+		// Init broad phase layers
+		BroadPhaseLayerInterfaceTable bp_layer_interface(Layers::NUM_LAYERS, BroadPhaseLayers::NUM_LAYERS);
+		bp_layer_interface.MapObjectToBroadPhaseLayer(Layers::NON_MOVING, BroadPhaseLayers::NON_MOVING);
+		bp_layer_interface.MapObjectToBroadPhaseLayer(Layers::MOVING, BroadPhaseLayers::MOVING);
+		bp_layer_interface.MapObjectToBroadPhaseLayer(Layers::HQ_DEBRIS, BroadPhaseLayers::MOVING);
+		bp_layer_interface.MapObjectToBroadPhaseLayer(Layers::LQ_DEBRIS, BroadPhaseLayers::LQ_DEBRIS);
+		bp_layer_interface.MapObjectToBroadPhaseLayer(Layers::SENSOR, BroadPhaseLayers::SENSOR);
+
+	#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
+		// Set layer names
+		bp_layer_interface.SetBroadPhaseLayerName(BroadPhaseLayers::NON_MOVING, "NON_MOVING");
+		bp_layer_interface.SetBroadPhaseLayerName(BroadPhaseLayers::MOVING, "MOVING");
+		bp_layer_interface.SetBroadPhaseLayerName(BroadPhaseLayers::LQ_DEBRIS, "LQ_DEBRIS");
+		bp_layer_interface.SetBroadPhaseLayerName(BroadPhaseLayers::SENSOR, "SENSOR");
+
+		// Check layer name interface
+		CHECK(strcmp(bp_layer_interface.GetBroadPhaseLayerName(BroadPhaseLayers::NON_MOVING), "NON_MOVING") == 0);
+		CHECK(strcmp(bp_layer_interface.GetBroadPhaseLayerName(BroadPhaseLayers::MOVING), "MOVING") == 0);
+		CHECK(strcmp(bp_layer_interface.GetBroadPhaseLayerName(BroadPhaseLayers::LQ_DEBRIS), "LQ_DEBRIS") == 0);
+		CHECK(strcmp(bp_layer_interface.GetBroadPhaseLayerName(BroadPhaseLayers::SENSOR), "SENSOR") == 0);
+	#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED
+
+		// Init object vs broad phase layer filter
+		ObjectVsBroadPhaseLayerFilterTable obj_vs_bp_filter(bp_layer_interface, BroadPhaseLayers::NUM_LAYERS, obj_vs_obj_filter, Layers::NUM_LAYERS);
+
+		// Check collision pairs
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::NON_MOVING, BroadPhaseLayers::NON_MOVING));
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::NON_MOVING, BroadPhaseLayers::MOVING));
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::NON_MOVING, BroadPhaseLayers::LQ_DEBRIS));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::NON_MOVING, BroadPhaseLayers::SENSOR));
+
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::MOVING, BroadPhaseLayers::NON_MOVING));
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::MOVING, BroadPhaseLayers::MOVING));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::MOVING, BroadPhaseLayers::LQ_DEBRIS));
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::MOVING, BroadPhaseLayers::SENSOR));
+
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::HQ_DEBRIS, BroadPhaseLayers::NON_MOVING));
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::HQ_DEBRIS, BroadPhaseLayers::MOVING));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::HQ_DEBRIS, BroadPhaseLayers::LQ_DEBRIS));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::HQ_DEBRIS, BroadPhaseLayers::SENSOR));
+
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::LQ_DEBRIS, BroadPhaseLayers::NON_MOVING));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::LQ_DEBRIS, BroadPhaseLayers::MOVING));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::LQ_DEBRIS, BroadPhaseLayers::LQ_DEBRIS));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::LQ_DEBRIS, BroadPhaseLayers::SENSOR));
+
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::SENSOR, BroadPhaseLayers::NON_MOVING));
+		CHECK(obj_vs_bp_filter.ShouldCollide(Layers::SENSOR, BroadPhaseLayers::MOVING));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::SENSOR, BroadPhaseLayers::LQ_DEBRIS));
+		CHECK(!obj_vs_bp_filter.ShouldCollide(Layers::SENSOR, BroadPhaseLayers::SENSOR));
+	}
+}

+ 1 - 0
UnitTests/UnitTests.cmake

@@ -47,6 +47,7 @@ set(UNIT_TESTS_SRC_FILES
 	${UNIT_TESTS_ROOT}/Physics/HeightFieldShapeTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/HeightFieldShapeTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/HingeConstraintTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/HingeConstraintTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/MotionQualityLinearCastTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/MotionQualityLinearCastTests.cpp
+	${UNIT_TESTS_ROOT}/Physics/ObjectLayerPairFilterTableTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/OffsetCenterOfMassShapeTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/OffsetCenterOfMassShapeTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/PathConstraintTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/PathConstraintTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/PhysicsDeterminismTests.cpp
 	${UNIT_TESTS_ROOT}/Physics/PhysicsDeterminismTests.cpp