1. Arne Babenhauserheide
  2. runner-hd-fork

Commits

crt32  committed 41f2f27

some fixes

  • Participants
  • Parent commits 9ab5396
  • Branches collision

Comments (0)

Files changed (13)

File src/CMakeLists.txt

View file
 	GameObjects/Geometry.h
 	Core/Singleton.h
 	Core/CLog.h
+	testlog.h
 )
  
 SET(SRCS
 	ADD_EXECUTABLE(Game ${HDRS} ${SRCS})
 endif()
 
-ADD_DEFINITIONS(-DTESTING)
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+	ADD_DEFINITIONS(-DTESTING)
+endif()
 
 SET_TARGET_PROPERTIES(Game PROPERTIES DEBUG_POSTFIX _d)
 

File src/Core/CSettingsManager.cpp

View file
 	}
     catch (std::exception& e)
     {
-      std::cout << "Standard exception: " << e.what() << std::endl;
+     	TEST_LOG("Standard exception: " << e.what() );
     }
 
 	return output;

File src/Core/CSettingsManager.h

View file
 #ifndef CSETTINGSMANAGER_H_
 #define CSETTINGSMANAGER_H_
 
+#include "../testlog.h"
 #include "Ogre.h"
 
 #include "Singleton.h"
 				output = boost::any_cast<T>(temp);
 			}
 			catch (std::exception& e){
-			      std::cout << "Standard exception: " << e.what() << std::endl;
+			      TEST_LOG( "Standard exception: " << e.what() );
 			      output = defaultValue;
 			}
 

File src/GameObjects/CCharacter.cpp

View file
 		bbox.setExtents(-1, boundingRect.bottom, boundingRect.left, 1, boundingRect.top, boundingRect.right );
 		temp->SetBoundingBox(bbox);
 
-
+/*
 		if (!bJumping) {
 			// EXPERIMENTAL: stairs
 
 
 			trans = goLevel->ResolveCollision(temp, Ogre::Vector2(0, -translation.y + deltaTime*fVerticalVelocity));
 			translation = translation + trans;
-		} else {
+		} else {*/
 			translation = Ogre::Vector2(deltaTime * fSpeed, deltaTime * fVerticalVelocity);
 			translation = goLevel->ResolveCollision(temp, translation);
-		}
+		//}
 
 		ogreBodyNode->translate(0.0f, translation.y, translation.x, Ogre::Node::TS_LOCAL);
 		CheckFloatZero(translation.x); CheckFloatZero(translation.y);

File src/GameObjects/CLevelData.cpp

View file
 	while (!bResolveFinished) {
 
 		if (AlmostEqual(currentTranslation.x, 0.0f) && AlmostEqual(currentTranslation.y, 0.0f)) {
-			return result;
+			bResolveFinished = true;
+			continue;
 		}
 
-		#ifdef TESTING
-			std::cout << "ResolveCollision: (" << rect.left << ", " << rect.top;
-			std::cout << ", " << rect.right << ", " << rect.bottom << ") " << currentTranslation << std::endl;
-		#endif
+		TEST_LOG("ResolveCollision: (" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom << ") "
+				 << currentTranslation);
 
 		// I. get all objects inside the smallest rectangle containing rect and rect+translation
 		Ogre::Rectangle broadRect( rect );
 		if (objects.empty()) {
 			result += currentTranslation;
 			currentTranslation = translation - result;
-			#ifdef TESTING
-				std::cout << "objects.empty()" << std::endl;
-			#endif
+			TEST_LOG("objects.empty()");
 			continue;
 		}
 
 
 		// in case of collision, resolve
 		if (!intersectingObjects.empty()) {
-			#ifdef TESTING
-				std::cout << "COLLISION" << std::endl;
-			#endif
+			TEST_LOG("COLLISION");
 
 			const PLevelObject& obj = intersectingObjects[0];
 
 			}
 			*/
 
-			#ifdef TESTING
-				std::cout << translationResolve << std::endl;
-			#endif
+			TEST_LOG(translationResolve);
 
 			/*
 			// now translationResolve will resolve the collision
 		if (collisionObjects.empty()) {
 			result += currentTranslation;
 			currentTranslation = translation - result;
-			#ifdef TESTING
-				std::cout << "collisionObjects.empty()" << std::endl;
-			#endif
+			TEST_LOG("collisionObjects.empty()");
+
 			continue;
 		}
 
 
 					Game::Polygon facePoly(objFaces[i]);
 					std::pair<bool, float> result = objectPolygon.TranslationIntersect(facePoly, currentTranslation);
-					#ifdef TESTING
-						std::cout << result.first << ", " << result.second << std::endl;
-					#endif
+					TEST_LOG(result.first << ", " << result.second);
 					if (result.first && result.second >= 0.0f) {
 						 // find nearest collision
 						if (bNotFound || fabs(nearestFactor) > fabs(result.second)) {
 							nearestFace = &objFaces[i];
 							nearestFactor = result.second;
 							nearestObj = obj;
-							#ifdef TESTING
-								std::cout << "found collision" << std::endl;
-							#endif
+							TEST_LOG("found collision");
 						}
 					}
 				}
 			}
 
 			if (!bNotFound) {
-				if (AlmostEqual(nearestFactor, 0.0f)) {
+				/*if (AlmostEqual(nearestFactor, 0.0f)) {
 					// can't move; abort
-					#ifdef TESTING
-						std::cout << "nearestFactor = 0" << std::endl;
-					#endif
-					return result;
-				}
+					TEST_LOG("nearestFactor = 0");
+					currentTranslation = Ogre::Vector2::ZERO;
+					bResolveFinished = true;
+					break;
+				}*/
 
 				// translate towards nearest object
 				Ogre::Vector2 maxTranslation = nearestFactor*currentTranslation*(1.0f - TOLERANCE);
 					ignoreObjects.push_back(nearestObj);
 					nearestObj.reset();
 					LastCollisionNonBlocking = true;
-					#ifdef TESTING
-						std::cout << "non-blocking" << std::endl;
-					#endif
+					TEST_LOG("non-blocking");
 				}
 			}
 		} while (!currentTranslation.isZeroLength() && LastCollisionNonBlocking);
 
+		if (bResolveFinished) continue;
+
 		if (currentTranslation.isZeroLength()) {
 			// we have been able to move to our goal; continue
 			currentTranslation = translation - result;
-			#ifdef TESTING
-				std::cout << "currentTranslation.isZeroLength()" << std::endl;
-			#endif
+			TEST_LOG("currentTranslation.isZeroLength()");
 			continue;
 		}
 
 			// no objects are blocking our way
 			result += currentTranslation;
 			currentTranslation = translation - result;
-			#ifdef TESTING
-				std::cout << "bNotFound=true" << std::endl;
-			#endif
+			TEST_LOG("bNotFound=true");
 			continue;
 		}
 
 				break;
 			}
 		}
+
+		TEST_LOG("sep axis = " << separatingAxis);
+
 		// make separatingAxis face in same direction as currentTranslation
 		if (separatingAxis.dotProduct(currentTranslation) < 0.0f)
 			separatingAxis = -separatingAxis;
 			SATResult result = objectPolygon.IsSeparatingAxis(facePoly, separatingAxes[i]);
 			if (!result.IsSeparating) {
 				// get minimum translation vector in direction of currentTranslation
-				Ogre::Vector2 MTV = result.MinimumOverlap * separatingAxes[i];
+				Ogre::Vector2 axisNormal = LeftHandNormal(separatingAxes[i]);
+				Ogre::Vector2 MTV = result.MinimumOverlap * axisNormal;
 				if (MTV.dotProduct(currentTranslation) < 0)
-					MTV = result.MaximumOverlap * separatingAxes[i];
+					MTV = result.MaximumOverlap * axisNormal;
 
 				float factor;
 				if (AlmostEqual(separatingAxis.y, 0.0f) || (MTV.x > MTV.y && !AlmostEqual(separatingAxis.x, 0.0f)))
 
 		if (minFactor == std::numeric_limits<float>::infinity()) {
 			// error, abort
-			#ifdef TESTING
-				std::cout << "minFactur=inf" << std::endl;
-			#endif
-			return result;
+			TEST_LOG("minFactur=inf");
+			bResolveFinished = true;
+			continue;
 		}
 
 		float factor = (fabs(minFactor) < fabs(maxFactor)) ? minFactor : maxFactor;
 			float dp = visitedFaces[*nearestFace].dotProduct(currentTranslation);
 			if (dp < 0) {
 				// cancel
-				#ifdef TESTING
-					std::cout << "dotProduct < 0" << std::endl;
-				#endif
+				TEST_LOG("dotProduct < 0");
 				return result;
 			}
 		} else {
 			visitedFaces[*nearestFace] = currentTranslation;
 		}
 
-		#ifdef TESTING
-			std::cout << "currentTranslation = " << currentTranslation << std::endl;
-		#endif
+		TEST_LOG("currentTranslation = " << currentTranslation);
 	}
 
-	#ifdef TESTING
-		std::cout << "result=" << result << std::endl;
-	#endif
+	TEST_LOG("result=" << result);
 	return result;
 }
 

File src/GameObjects/CLevelData.h

View file
 #define LEVELDATA_H_
 
 #include "LevelObjects.h"
+#include "../testlog.h"
 #include "../Core/CLog.h"
 
 #include "Ogre.h"

File src/GameObjects/Geometry.cpp

View file
 Face::Face(const Ogre::Vector2& vector1, const Ogre::Vector2& vector2)
 	: v1(vector1), v2(vector2)
 {
-	// calculate right hand normal
-	Ogre::Vector2 direction = v2-v1;
-	normal.x = direction.y;
-	normal.y = -direction.x;
-	normal.normalise();
+	normal = RightHandNormal(v2-v1);
 }
 
 Face::Face(const Ogre::Vector2& vector1, const Ogre::Vector2& vector2, const Ogre::Vector2& normal)
 	}
 
 	// get normal of axis
-	Ogre::Vector2 normal( -axis.y, axis.x);
-	normal.normalise();
+	Ogre::Vector2 normal = LeftHandNormal(axis);
 
 	// project this
 	float min1, max1; // projected intervall of this
 
 
 	SATResult result;
-	result.IsSeparating = (min1 - max2 > 0) || (min2 - max1 > 0);
+	result.IsSeparating = definitelyGreaterThan(min1 - max2, 0.0f) || definitelyGreaterThan(min2 - max1, 0.0f);
 
-	if (min2 < max1)
+	if (min2 <= min1) {
 		result.MinimumOverlap = min2 - max1;
-	else if (min1 < max2)
-		result.MinimumOverlap = max2 - min1;
+		result.MaximumOverlap = max2 - min1;
+	} else {
+		result.MinimumOverlap = min2 - max1;
+		result.MaximumOverlap = max2 - min1;
+	}
 
-	if (min2 < max1)
-		result.MaximumOverlap = max2 - min1;
-	else if (min1 < max2)
-		result.MaximumOverlap = min2 - max1;
+	if (fabs(result.MinimumOverlap) > fabs(result.MaximumOverlap))
+		std::swap(result.MinimumOverlap, result.MaximumOverlap);
 
 	return result;
 }
 
 std::pair<bool, Ogre::Vector2> Polygon::SeparatingAxesTest(const Polygon& polygon, const std::vector<Face>& faces, float &minOverlap) const {
-	Ogre::Vector2 MTV(0,0); // minimum translation vector
+	Ogre::Vector2 minAxis(0,0);
 
 	bool FoundAxis = false;
 	for (int i = 0; i < faces.size(); i++) {
 		if (!sat.IsSeparating) {
 			if (fabs(minOverlap) > fabs(sat.MinimumOverlap)) {
 				minOverlap = sat.MinimumOverlap;
-				MTV = sat.MinimumOverlap * axis;
+				minAxis = axis;
 			}
 		} else {
 			FoundAxis = true;
 		}
 	}
 
-	if (FoundAxis)
+
+	if (FoundAxis) {
 		return std::pair<bool, Ogre::Vector2>(true, Ogre::Vector2::ZERO);
-	else
+	} else {
+		Ogre::Vector2 normal = LeftHandNormal(minAxis);
+		Ogre::Vector2 MTV = normal * minOverlap;
+
 		return std::pair<bool, Ogre::Vector2>(false, MTV);
+	}
 }
 
 /** Performs separating axis test for all axes parallel to any faces
 	        in case of intersection also returns maximum factor that a translation of factor*direction leads to no collision
 	        with 0 <= factor <= 1 */
 std::pair<bool, float> Polygon::TranslationIntersect(const Polygon& polygon, const Ogre::Vector2& direction) const {
-	Ogre::Vector2 dirNormal = direction;
-	float dirLength = dirNormal.normalise();
 	float factor = std::numeric_limits<float>::infinity();
 	bool bIntersection = false;
 
 	// check all rays in direction through all points of this
 	for (int i = 0; i < Points.size(); i++) {
-		Ray ray(Points[i], dirNormal);
+		Ray ray(Points[i], direction);
 		std::pair<bool, float> intersection = ray.Intersects(polygon);
-		if (intersection.first && intersection.second > 0.0f && intersection.second <= dirLength) {
+		if (intersection.first && intersection.second >= 0.0f && intersection.second <= 1.0f) {
 			bIntersection = true;
 			if (factor > intersection.second) {
 				factor = intersection.second;
 
 	// check all rays in direction through all points of polygon
 	for (int i = 0; i < polygon.Points.size(); i++) {
-		Ray ray(polygon.Points[i], -dirNormal);
-		std::pair<bool, float> intersection = ray.Intersects(polygon);
-		if (intersection.first && intersection.second > 0.0f && intersection.second <= dirLength) {
+		Ray ray(polygon.Points[i], -direction);
+		std::pair<bool, float> intersection = ray.Intersects(*this);
+		if (intersection.first && intersection.second >= 0.0f && intersection.second <= 1.0f) {
 			bIntersection = true;
 			if (factor > intersection.second) {
 				factor = intersection.second;
 		}
 	}
 
-	return std::pair<bool, float>(bIntersection, factor / dirLength);
+	return std::pair<bool, float>(bIntersection, factor);
 }
 
 std::ostream& operator<< (std::ostream& os, const Polygon& polygon) {
 Ray::Ray() : Origin(0.0f, 0.0f), Direction(0.0f, 0.0f)
 {}
 
-Ray::Ray(const Ogre::Vector2& origin, const Ogre::Vector2& direction) : Origin(origin)
-{
-	Direction = direction.normalisedCopy();
-}
+Ray::Ray(const Ogre::Vector2& origin, const Ogre::Vector2& direction) : Origin(origin), Direction(direction)
+{}
 
 /** Checks for intersection with given ray
 @returns Second element indicates distance from origin of intersection point */
 	std::pair<bool, float> rayIntersection = faceRay.Intersects(*this);
 
 	// check if intersection is between the two points of face
-	if (rayIntersection.first && rayIntersection.second >= 0.0f && rayIntersection.second <= faceDirection.length()) {
+	if (rayIntersection.first && rayIntersection.second >= 0.0f && rayIntersection.second <= 1.0f) {
 		return this->Intersects(faceRay);
 	} else {
 		return std::pair<bool, float>(false, 0.0f);

File src/GameObjects/Geometry.h

View file
     return false;
 }
 
+/** Source: The Art of computer programming Vol 2 */
+
+inline bool approximatelyEqual(float a, float b) {
+    return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * TOLERANCE);
+}
+
+inline bool essentiallyEqual(float a, float b) {
+    return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * TOLERANCE);
+}
+
+inline bool definitelyGreaterThan(float a, float b) {
+    return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * TOLERANCE);
+}
+
+inline bool definitelyLessThan(float a, float b) {
+    return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * TOLERANCE);
+}
+
+/** Calculate left hand normal */
+inline Ogre::Vector2 LeftHandNormal(const Ogre::Vector2& v) {
+	return Ogre::Vector2(-v.y, v.x).normalisedCopy();
+}
+
+/** Calculate right hand normal */
+inline Ogre::Vector2 RightHandNormal(const Ogre::Vector2& v) {
+	return Ogre::Vector2(v.y, -v.x).normalisedCopy();
+}
+
 
 struct Face {
 	Ogre::Vector2 v1;
 	/** Moves all Points by translation */
 	void Translate(const Ogre::Vector2& translation);
 
-	/** Performs separating axis test for a given axis and polygon
-	@return Test Result; see Polygon::SATResult */
+	/** Performs separating axis test for a given axis and polygon; uses left hand normal of axis
+	 *  Use result.MinimumOverlap * LeftHandNormal(axis) to get translation vector
+	@return Test Result
+	@see Polygon::SATResult */
 	SATResult IsSeparatingAxis(const Polygon& polygon, const Ogre::Vector2& axis) const;
 
 	/** Performs separating axis test for all axes parallel to any faces
 
 std::ostream& operator<< (std::ostream& os, const Polygon &polygon);
 
-/** Defines a Ray in 2D Space*/
+/** Defines a Ray in 2D Space
+ *  Note: direction vector is not neccessarily normalized */
 class Ray {
 public:
 	Ray();
 
 	inline void SetOrigin(const Ogre::Vector2& origin) { Origin = origin; }
 	inline const Ogre::Vector2& GetOrigin() const { return Origin; }
-	inline void SetDirection(const Ogre::Vector2& direction) { Direction = direction.normalisedCopy(); }
+	inline void SetDirection(const Ogre::Vector2& direction) { Direction = direction; }
 	inline const Ogre::Vector2& GetDirection() const { return Direction; }
 	inline Ogre::Vector2 GetPoint(float t) const { return Origin + t*Direction; }
 

File src/testlog.h

View file
+/*
+ * testlog.h
+ *
+ *  Created on: Sep 30, 2010
+ *      Author: crt
+ */
+
+#ifndef TESTLOG_H_
+#define TESTLOG_H_
+
+#ifdef TESTING
+	#ifdef BOOST_TEST
+		#include "boost/test/unit_test.hpp"
+		#include <sstream>
+		#define TEST_LOG(x) \
+			{ std::stringstream __tmp; __tmp << __LINE__ << ": " << x; \
+			BOOST_MESSAGE( __tmp.str() ); }
+	#else
+		#define TEST_LOG(x) std::cout << __LINE__ << ": " << x << std::endl ;
+	#endif
+#else
+	#define TEST_LOG(x)
+#endif
+
+
+#endif /* TESTLOG_H_ */

File test/CMakeLists.txt

View file
 SET(TEST_HDRS
+	test.h
 	../src/Core/CStateManager.h
 	../src/Core/CSettingsManager.h
 	../src/Core/IState.h
 	../src/GameObjects/LevelObjects.h
 	../src/GameObjects/CLevelData.h
 	../src/GameObjects/Geometry.h
+	../src/testlog.h
 )
 
 SET(TEST_SRCS
  
 if (CMAKE_BUILD_TYPE STREQUAL "Debug")
 	ADD_DEFINITIONS(-DTESTING)
+	ADD_DEFINITIONS(-DBOOST_TEST)
 	ADD_EXECUTABLE(Test_d ${TEST_HDRS} ${TEST_SRCS})		
 	TARGET_LINK_LIBRARIES(Test_d ${OGRE_LIBRARIES} ${OIS_LIBRARIES} ${BOOST_LIBRARIES} ${EXTRA_LIBS})
 endif()

File test/CollisionTest.cpp

View file
  *      Author: crt
  */
 
-#include "../src/GameObjects/Geometry.h"
+#include "test.h"
 #include "../src/GameObjects/CLevelData.h"
 #include "../src/GameObjects/LevelObjects.h"
 #include "Ogre.h"
-#include "boost/test/unit_test.hpp"
 
 using namespace Ogre;
 using namespace Game::GameObjects;
 using namespace std;
 
-BOOST_AUTO_TEST_SUITE(CollisionTest);
-/*
-BOOST_AUTO_TEST_CASE(separating_axis_test)
-{
-	std::vector<Ogre::Vector2> poly1, poly2;
-	Ogre::Vector2 axis;
+BOOST_AUTO_TEST_SUITE(Collision);
 
-	poly1.push_back(Ogre::Vector2(0,0));
-	poly1.push_back(Ogre::Vector2(1,0));
-	poly1.push_back(Ogre::Vector2(1,1));
-	poly1.push_back(Ogre::Vector2(0,1));
-
-	poly2.push_back(Ogre::Vector2(2,0));
-	poly2.push_back(Ogre::Vector2(2,1));
-	poly2.push_back(Ogre::Vector2(3,1));
-	poly2.push_back(Ogre::Vector2(3,0));
-
-	axis = Ogre::Vector2::UNIT_Y;
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), true);
-
-	axis = Ogre::Vector2::UNIT_X;
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-
-	axis = Ogre::Vector2(1,1);
-
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-
-	poly2.clear();
-	poly2.push_back(Ogre::Vector2(0.5,0.5));
-	poly2.push_back(Ogre::Vector2(2,0.5));
-	poly2.push_back(Ogre::Vector2(2,2));
-	poly2.push_back(Ogre::Vector2(0.5,2));
-
-	axis = Ogre::Vector2::UNIT_Y;
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-	axis = Ogre::Vector2::UNIT_X;
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-	axis = Ogre::Vector2(1,1);
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-
-	poly1.clear();
-	poly1.push_back(Ogre::Vector2(-1,-1));
-	poly1.push_back(Ogre::Vector2(1,-1));
-	poly1.push_back(Ogre::Vector2(1,1));
-	poly1.push_back(Ogre::Vector2(-1,1));
-
-	poly2.clear();
-	poly2.push_back(Ogre::Vector2(-1,4));
-	poly2.push_back(Ogre::Vector2(1,4));
-	poly2.push_back(Ogre::Vector2(1,0));
-	poly2.push_back(Ogre::Vector2(-1,0));
-
-	axis = Ogre::Vector2::UNIT_Y;
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-	axis = Ogre::Vector2::UNIT_X;
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-	axis = Ogre::Vector2(1,1);
-	BOOST_CHECK_EQUAL(IsSeparatingAxis(poly1, poly2, axis), false);
-}
-*/
 BOOST_AUTO_TEST_CASE( collision_test )
 {
 	// create 2x2x2 cube with center in origin
 	collider->SetAABBSize(Ogre::Vector3(1.0f, 2.0f, 2.0f));
 
 	Ogre::Vector2 translation = leveldata->ResolveCollision(collider, Ogre::Vector2::UNIT_Y*(-2.0f));
-	BOOST_CHECK_EQUAL(translation.x, 0.0f);
-	BOOST_CHECK_EQUAL(translation.y, -1.0f);
+	FLOAT_CHECK_EQUAL(translation, Ogre::Vector2(0.0f, -1.0f));
 
 	// move beyond cube
 	translation = leveldata->ResolveCollision(collider, Ogre::Vector2::UNIT_Y*(-10.0f));
-	BOOST_CHECK_EQUAL(translation.x, 0.0f);
-	BOOST_CHECK_EQUAL(translation.y, -1.0f);
-
+	FLOAT_CHECK_EQUAL(translation, Ogre::Vector2(0.0f, -1.0f));
 
 	// move down and right
 	translation = leveldata->ResolveCollision(collider, Ogre::Vector2(1.0f, -2.0f));
-	BOOST_CHECK_EQUAL( translation.x, 0.5f);
-	BOOST_CHECK_EQUAL( translation.y, -1.0f);
+	FLOAT_CHECK_EQUAL(translation, Ogre::Vector2(1.0f, -1.0f));
 
-	collider->SetPosition(Ogre::Vector3(0.0f, 1.0f, -1.0f));
+	// initial collision
+	// rectangles overlap at [(0,0),(1,1)]
+	collider->SetPosition(Ogre::Vector3(0.0f, 1.0f, 1.0f));
 	collider->SetAABBSize(Ogre::Vector3(1.0f, 2.0f, 2.0f));
 	translation = leveldata->ResolveCollision(collider, Ogre::Vector2(1.0f, 0.0f));
-	BOOST_CHECK_EQUAL(translation.x, 1.0f);
-	BOOST_CHECK_EQUAL(translation.y, 0.0f);
+	FLOAT_CHECK_EQUAL(translation, Ogre::Vector2(1.0f, 0.0f));
 }
 
 BOOST_AUTO_TEST_SUITE_END();

File test/GeometryTest.cpp

View file
  *      Author: crt
  */
 
-#include "../src/GameObjects/Geometry.h"
-#include "boost/test/unit_test.hpp"
+#include "test.h"
 #include <math.h>
 
 using namespace Game;
 BOOST_AUTO_TEST_CASE( face_test )
 {
 	Face emptyFace;
-	BOOST_CHECK(emptyFace.v1 == Ogre::Vector2::ZERO);
-	BOOST_CHECK(emptyFace.v2 == Ogre::Vector2::ZERO);
-	BOOST_CHECK_EQUAL(emptyFace.normal, Ogre::Vector2::ZERO);
+	FLOAT_CHECK_EQUAL(emptyFace.v1, Ogre::Vector2::ZERO);
+	FLOAT_CHECK_EQUAL(emptyFace.v2, Ogre::Vector2::ZERO);
+	FLOAT_CHECK_EQUAL(emptyFace.normal, Ogre::Vector2::ZERO);
 
 	Face normalTest(Ogre::Vector2::UNIT_X, Ogre::Vector2::ZERO);
-	BOOST_CHECK(normalTest.v1 == Ogre::Vector2::UNIT_X);
-	BOOST_CHECK(normalTest.v2 == Ogre::Vector2::ZERO);
-	BOOST_CHECK_EQUAL(normalTest.normal, Ogre::Vector2::NEGATIVE_UNIT_Y);
+	FLOAT_CHECK_EQUAL(normalTest.v1, Ogre::Vector2::UNIT_X);
+	FLOAT_CHECK_EQUAL(normalTest.v2, Ogre::Vector2::ZERO);
+	FLOAT_CHECK_EQUAL(normalTest.normal, Ogre::Vector2::UNIT_Y);
 
 	normalTest = Face(Ogre::Vector2::ZERO, Ogre::Vector2::UNIT_X);
-	BOOST_CHECK(normalTest.v2 == Ogre::Vector2::UNIT_X);
-	BOOST_CHECK(normalTest.v1 == Ogre::Vector2::ZERO);
-	BOOST_CHECK_EQUAL(normalTest.normal, Ogre::Vector2::UNIT_Y);
+	FLOAT_CHECK_EQUAL(normalTest.v2, Ogre::Vector2::UNIT_X);
+	FLOAT_CHECK_EQUAL(normalTest.v1, Ogre::Vector2::ZERO);
+	FLOAT_CHECK_EQUAL(normalTest.normal, Ogre::Vector2::NEGATIVE_UNIT_Y);
 
-	BOOST_CHECK(normalTest != emptyFace);
+	BOOST_CHECK_NE(normalTest, emptyFace);
 
 	emptyFace = Face(Ogre::Vector2::ZERO, Ogre::Vector2::UNIT_X);
-	BOOST_CHECK(normalTest == emptyFace);
+	BOOST_CHECK_EQUAL(normalTest, emptyFace);
 
 	emptyFace.normal = Ogre::Vector2(10, 5);
-	BOOST_CHECK(normalTest == emptyFace);
+	BOOST_CHECK_EQUAL(normalTest, emptyFace);
 }
 
 BOOST_AUTO_TEST_CASE( polygon_constructor_test )
 	std::vector<Face> faces = polygon.GetFaces();
 	BOOST_REQUIRE_EQUAL(faces.size(), 1);
 
-	BOOST_CHECK_EQUAL(faces[0].v1, Ogre::Vector2::ZERO);
-	BOOST_CHECK_EQUAL(faces[0].v2, Ogre::Vector2::UNIT_X);
+	FLOAT_CHECK_EQUAL(faces[0].v1, Ogre::Vector2::ZERO);
+	FLOAT_CHECK_EQUAL(faces[0].v2, Ogre::Vector2::UNIT_X);
 
 	Ogre::Rectangle rect;
 	rect.left = -1; rect.right = 1;
 	faces = polygon.GetFaces(false);
 	BOOST_REQUIRE_EQUAL(faces.size(), 4);
 
-	BOOST_CHECK_EQUAL(faces[0].v2, faces[1].v1);
-	BOOST_CHECK_EQUAL(faces[1].v2, faces[2].v1);
-	BOOST_CHECK_EQUAL(faces[2].v2, faces[3].v1);
-	BOOST_CHECK_EQUAL(faces[3].v2, faces[0].v1);
+	FLOAT_CHECK_EQUAL(faces[0].v2, faces[1].v1);
+	FLOAT_CHECK_EQUAL(faces[1].v2, faces[2].v1);
+	FLOAT_CHECK_EQUAL(faces[2].v2, faces[3].v1);
+	FLOAT_CHECK_EQUAL(faces[3].v2, faces[0].v1);
 
-	BOOST_CHECK_EQUAL(faces[0].v1, Ogre::Vector2(-1, -1));
-	BOOST_CHECK_EQUAL(faces[1].v1, Ogre::Vector2(1, -1));
-	BOOST_CHECK_EQUAL(faces[2].v1, Ogre::Vector2(1, 1));
-	BOOST_CHECK_EQUAL(faces[3].v1, Ogre::Vector2(-1, 1));
+	FLOAT_CHECK_EQUAL(faces[0].v1, Ogre::Vector2(-1, -1));
+	FLOAT_CHECK_EQUAL(faces[1].v1, Ogre::Vector2(1, -1));
+	FLOAT_CHECK_EQUAL(faces[2].v1, Ogre::Vector2(1, 1));
+	FLOAT_CHECK_EQUAL(faces[3].v1, Ogre::Vector2(-1, 1));
 
-	BOOST_CHECK_EQUAL(faces[0].normal, Ogre::Vector2(0.0f, -1.0f));
-	BOOST_CHECK_EQUAL(faces[1].normal, Ogre::Vector2(1.0f,  0.0f));
-	BOOST_CHECK_EQUAL(faces[2].normal, Ogre::Vector2(0.0f,  1.0f));
-	BOOST_CHECK_EQUAL(faces[3].normal, Ogre::Vector2(-1.0f,  0.0f));
+	FLOAT_CHECK_EQUAL(faces[0].normal, Ogre::Vector2(0.0f, -1.0f));
+	FLOAT_CHECK_EQUAL(faces[1].normal, Ogre::Vector2(1.0f,  0.0f));
+	FLOAT_CHECK_EQUAL(faces[2].normal, Ogre::Vector2(0.0f,  1.0f));
+	FLOAT_CHECK_EQUAL(faces[3].normal, Ogre::Vector2(-1.0f,  0.0f));
 }
 
 BOOST_AUTO_TEST_CASE( polygon_modifier_test )
 	BOOST_CHECK_EQUAL(result.IsSeparating, false);
 	std::pair<bool, Ogre::Vector2> result2 = poly1.SeparatingAxesTest(poly2);
 	BOOST_CHECK_EQUAL(result2.first, false);
-	BOOST_CHECK_EQUAL(result2.second.length(), 2.0f);
+	FLOAT_CHECK_EQUAL(result2.second.length(), 2.0f);
 
 	poly1.Translate(result2.second * (1.0f + TOLERANCE));
 	result2 = poly1.SeparatingAxesTest(poly2);
 	poly2.Translate(Ogre::Vector2(-1.0f, 0.0f));
 	result2 = poly1.SeparatingAxesTest(poly2);
 	BOOST_CHECK_EQUAL(result2.first, false);
-	BOOST_CHECK( AlmostEqual(result2.second.length(), 0.447f) );
+	FLOAT_CHECK_EQUAL( result2.second.length(), 0.447f );
+
+	// create two 2x2 rectangles which overlap at (0.5,0),(1,1)
+	rect.left = rect.bottom = -1.0f;
+	rect.top = rect.right = 1.0f;
+	poly1 = Game::Polygon(rect);
+	rect.left = 0.5f;
+	rect.bottom = 0.0f;
+	rect.top = 2.0f;
+	rect.right = 2.5f;
+	poly2 = Game::Polygon(rect);
+
+	result2 = poly2.SeparatingAxesTest(poly1);
+	BOOST_CHECK_EQUAL(result2.first, false);
+	FLOAT_CHECK_EQUAL(result2.second, Ogre::Vector2::UNIT_X*0.5f);
 }
 
 BOOST_AUTO_TEST_CASE( polygon_intersection_test2 )
 
 	std::pair<bool, float> result = poly2.TranslationIntersect(poly1, Ogre::Vector2(-2.0f, 0.0f));
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK( AlmostEqual(result.second, 0.5f) );
+	FLOAT_CHECK_EQUAL( result.second, 0.5f );
 
 	result = poly2.TranslationIntersect(poly1, Ogre::Vector2(-2.0f, 2.0f));
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK( AlmostEqual(result.second, 0.5f) );
+	FLOAT_CHECK_EQUAL( result.second, 0.5f );
 }
 
 BOOST_AUTO_TEST_CASE( ray_ray_intersection_test )
 	Game::Ray ray1( Ogre::Vector2::ZERO, Ogre::Vector2::UNIT_Y );
 	Game::Ray ray2( Ogre::Vector2(1.0f, 1.0f), Ogre::Vector2::NEGATIVE_UNIT_X );
 
+	// intersection point should be at (0, 1)
 	std::pair<bool, float> result = ray1.Intersects(ray2);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( result.second, 1.0f );
+	FLOAT_CHECK_EQUAL( result.second, 1.0f );
 
 	result = ray2.Intersects(ray1);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( result.second, 1.0f );
+	FLOAT_CHECK_EQUAL( result.second, 1.0f );
 
+	// testing identical rays should return no intersection
 	ray2 = ray1;
 	result = ray1.Intersects(ray2);
 	BOOST_CHECK_EQUAL(result.first, false);
 
+	// rays are now parallel; rare case of no intersection
 	ray2 = Game::Ray( Ogre::Vector2(1.0f, 0.0f), Ogre::Vector2::UNIT_Y );
 	result = ray1.Intersects(ray2);
 	BOOST_CHECK_EQUAL(result.first, false);
 	result = ray2.Intersects(ray1);
 	BOOST_CHECK_EQUAL(result.first, false);
 
+	// rays share same origin
 	ray1 = Game::Ray( Ogre::Vector2(2.0f, 5.0f), Ogre::Vector2(1.0f, 1.0f) );
 	ray2 = Game::Ray( Ogre::Vector2(2.0f, 5.0f), Ogre::Vector2(-1.0f, 3.0f) );
 	result = ray1.Intersects(ray2);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( result.second, 0.0f );
+	FLOAT_CHECK_EQUAL( result.second, 0.0f );
 	result = ray2.Intersects(ray1);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( result.second, 0.0f );
+	FLOAT_CHECK_EQUAL( result.second, 0.0f );
 
+	// rays will intersect at (1, 4) which is origin of ray2
 	ray2.SetOrigin(Ogre::Vector2(1.0f, 4.0f));
-	// rays will intersect at 1.0f/4.0f
 	result = ray1.Intersects(ray2);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( AlmostEqual(result.second, -sqrt(2.0f)), true );
+	FLOAT_CHECK_EQUAL( result.second, -1.0f );
 	result = ray2.Intersects(ray1);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( AlmostEqual(result.second, 0.0f), true );
+	FLOAT_CHECK_EQUAL( result.second, 0.0f );
 }
 
 BOOST_AUTO_TEST_CASE( ray_face_intersection_test )
 	Game::Ray ray(Ogre::Vector2::ZERO, Ogre::Vector2::UNIT_X);
 	Game::Face face(Ogre::Vector2(1.0f, 0.0f), Ogre::Vector2(1.0f, 1.0f));
 
+	// ray intersects at (1, 0)
 	std::pair<bool, float> result = ray.Intersects(face);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( AlmostEqual(result.second, 1.0f), true );
+	FLOAT_CHECK_EQUAL( result.second, 1.0f );
 
+	// no intersection
 	ray.SetOrigin(Ogre::Vector2(0.0f, 2.0f));
 	result = ray.Intersects(face);
 	BOOST_CHECK_EQUAL(result.first, false);
 
+	// ray intersects at (1, 2)
 	face = Game::Face(Ogre::Vector2(0.0f, 3.0f), Ogre::Vector2(1.0f, 2.0f));
 	result = ray.Intersects(face);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( AlmostEqual(result.second, 1.0f), true );
+	FLOAT_CHECK_EQUAL( result.second, 1.0f );
 
+	// ray intersects at (0.5, 2.5)
 	ray.SetDirection(Ogre::Vector2(1.0f, 1.0f));
 	result = ray.Intersects(face);
 	BOOST_CHECK_EQUAL(result.first, true);
-	BOOST_CHECK_EQUAL( AlmostEqual(result.second, sqrt(2.0f) / 2.0f), true );
+	FLOAT_CHECK_EQUAL( result.second, 0.5f );
 }
 
 BOOST_AUTO_TEST_SUITE_END();

File test/test.h

View file
+/*
+ * test.h
+ *
+ *  Created on: Sep 30, 2010
+ *      Author: crt
+ */
+
+#ifndef TEST_H_
+#define TEST_H_
+
+#include "../src/GameObjects/Geometry.h"
+#include "boost/test/unit_test.hpp"
+
+namespace Test {
+
+struct check_float_equality {
+    boost::test_tools::predicate_result operator() (float A, float B) const {
+        boost::test_tools::predicate_result result = Game::approximatelyEqual(A, B);
+        if (!result) result.message() << " (approx.)";
+        return result;
+    }
+
+    boost::test_tools::predicate_result operator() (const Ogre::Vector2& A, const Ogre::Vector2& B) const {
+        boost::test_tools::predicate_result result = Game::approximatelyEqual(A.x, B.x) && Game::approximatelyEqual(A.y, B.y);
+        if (!result) result.message() << " (approx.)";
+        return result;
+    }
+
+    boost::test_tools::predicate_result operator() (const Ogre::Vector3& A, const Ogre::Vector3& B) const {
+        boost::test_tools::predicate_result result = Game::approximatelyEqual(A.x, B.x) &&
+        		Game::approximatelyEqual(A.y, B.y) && Game::approximatelyEqual(A.z, B.z);
+        if (!result) result.message() << " (approx.)";
+        return result;
+    }
+};
+
+#define FLOAT_WARN_EQUAL(A, B) \
+    BOOST_WARN_WITH_ARGS_IMPL(::Test::check_float_equality(), \
+        "", WARN, WARN_EQUAL, (A)(B));
+
+#define FLOAT_CHECK_EQUAL(A, B) \
+    BOOST_CHECK_WITH_ARGS_IMPL(::Test::check_float_equality(), \
+        "", CHECK, CHECK_EQUAL, (A)(B));
+
+#define FLOAT_REQUIRE_EQUAL(A, B) \
+    BOOST_REQUIRE_WITH_ARGS_IMPL(::Test::check_float_equality(), \
+        "", REQUIRE, CHECK_EQUAL, (A)(B));
+
+}
+
+#endif /* TEST_H_ */