1. Arne Babenhauserheide
  2. runner-hd-fork

Commits

crt32  committed 8814253 Merge

merge collision into main

  • Participants
  • Parent commits 7e74fc4, 445351b
  • Branches default

Comments (0)

Files changed (24)

File src/CMakeLists.txt

View file
  • Ignore whitespace
 	GameObjects/LevelDataReader.h
 	GameObjects/LevelDataWriter.h
 	GameObjects/LevelObjects.h
+	GameObjects/Geometry.h
 	Core/Singleton.h
 	Core/CLog.h
+	testlog.h
 )
  
 SET(SRCS
 	GameObjects/LevelDataReader.cpp
 	GameObjects/LevelDataWriter.cpp
 	GameObjects/LevelObjects.cpp
+	GameObjects/Geometry.cpp
 	main.cpp	
 )
 
 	ADD_EXECUTABLE(Game ${HDRS} ${SRCS})
 endif()
 
-#ADD_DEFINITIONS(-DTESTING)
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+	# uncoment for debug output to console
+	#ADD_DEFINITIONS(-DTESTING)
+endif()
 
 SET_TARGET_PROPERTIES(Game PROPERTIES DEBUG_POSTFIX _d)
-SET_TARGET_PROPERTIES(Game PROPERTIES LINK_FLAGS -Wl,--allow-multiple-definition,--enable-auto-import)
+
+if(WIN32)
+	SET_TARGET_PROPERTIES(Game PROPERTIES LINK_FLAGS -Wl,--allow-multiple-definition,--enable-auto-import)
+endif()
+
 TARGET_LINK_LIBRARIES(Game ${OGRE_LIBRARIES} ${OIS_LIBRARIES} ${BOOST_LIBRARIES} ${EXTRA_LIBS})

File src/Core/CSettingsManager.cpp

View file
  • Ignore whitespace
 	}
     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
  • Ignore whitespace
 #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
  • Ignore whitespace
 		boundingRect.top = position.y + bounds.top;
 		boundingRect.bottom = position.y - bounds.bottom;
 
+		// make temporary levelobject
+		GameObjects::PLevelObject temp(new GameObjects::CLevelObject(GetPosition()));
+		Ogre::AxisAlignedBox bbox;
+		bbox.setExtents(-1, boundingRect.bottom, boundingRect.left, 1, boundingRect.top, boundingRect.right );
+		temp->SetBoundingBox(bbox);
+
+
 		if (!bJumping) {
 			// EXPERIMENTAL: stairs
 
-			translation = goLevel->ResolveCollision(boundingRect, Ogre::Vector2(0, 4.0f));
-			boundingRect.left += translation.x;
-			boundingRect.right += translation.x;
-			boundingRect.top += translation.y;
-			boundingRect.bottom += translation.y;
+			translation = goLevel->ResolveCollision(temp, Ogre::Vector2(0, 4.0f));
+			temp->SetPosition(temp->GetPosition() + Ogre::Vector3(0.0f, translation.y, translation.x));
 
-			Ogre::Vector2 trans = goLevel->ResolveCollision(boundingRect, Ogre::Vector2(deltaTime*fSpeed, 0));
-			boundingRect.left += trans.x;
-			boundingRect.right += trans.x;
-			boundingRect.top += trans.y;
-			boundingRect.bottom += trans.y;
+			Ogre::Vector2 trans = goLevel->ResolveCollision(temp, Ogre::Vector2(deltaTime*fSpeed, 0));
+			temp->SetPosition(temp->GetPosition() + Ogre::Vector3(0.0f, trans.y, trans.x));
+
 			translation = translation+trans;
 
-			trans = goLevel->ResolveCollision(boundingRect, Ogre::Vector2(0, -translation.y + deltaTime*fVerticalVelocity));
+			trans = goLevel->ResolveCollision(temp, Ogre::Vector2(0, -translation.y + deltaTime*fVerticalVelocity));
 			translation = translation + trans;
 		} else {
 			translation = Ogre::Vector2(deltaTime * fSpeed, deltaTime * fVerticalVelocity);
-			translation = goLevel->ResolveCollision(boundingRect, translation);
+			translation = goLevel->ResolveCollision(temp, translation);
 		}
 
 		ogreBodyNode->translate(0.0f, translation.y, translation.x, Ogre::Node::TS_LOCAL);

File src/GameObjects/CLevelData.cpp

View file
  • Ignore whitespace
 
 	for (LevelObjectListItor it = staticLevelData.begin(); it != staticLevelData.end(); ++it) {
 		PLevelObject obj(*it);
-		obj->Draw(ogreScene, node);
+		if (obj->IsOgreObject())
+			static_cast<COgreLevelObject*>(obj.get())->Draw(ogreScene, node);
 	}
 
 	for (LevelObjectListItor it = backgroundLevelData.begin(); it != backgroundLevelData.end(); ++it) {
 		PLevelObject obj(*it);
-		obj->Draw(ogreScene, node);
+		if (obj->IsOgreObject())
+			static_cast<COgreLevelObject*>(obj.get())->Draw(ogreScene, node);
 	}
 
 	for (LevelObjectListItor it = dynamicLevelData.begin(); it != dynamicLevelData.end(); ++it) {
 		PLevelObject obj(*it);
-		obj->Draw(ogreScene, node);
+		if (obj->IsOgreObject())
+			static_cast<COgreLevelObject*>(obj.get())->Draw(ogreScene, node);
 	}
 
 	return node;
 }
 
-Ogre::Rectangle CoordinateTransform(const Ogre::AxisAlignedBox& box) {
-	Ogre::Rectangle result;
-	result.left = box.getMinimum().z;
-	result.right = box.getMaximum().z;
-	result.top = box.getMaximum().y;
-	result.bottom = box.getMinimum().y;
-	return result;
-}
 
-CLevelData::LevelObjectList CLevelData::GetObjectRectangle(const Ogre::Rectangle& rect) {
-	// TODO: use space partitioning here
+CLevelData::LevelObjectList CLevelData::GetObjectRectangle(const Ogre::Rectangle& rect, LevelObjectList& list) {
 	LevelObjectList results;
-	for (LevelObjectListItor it = staticLevelData.begin(); it != staticLevelData.end(); ++it) {
+	for (LevelObjectListItor it = list.begin(); it != list.end(); ++it) {
 		const PLevelObject& obj = *it;
-		if (Intersect(CoordinateTransform(obj->GetBoundingBox()), rect) || Contains(rect, CoordinateTransform(obj->GetBoundingBox())))
+		if (Intersect(obj->GetBoundingRectangle(), rect))
 			results.push_back(obj);
 	}
 	return results;
 }
 
-std::vector<Ogre::Vector2> MakePolygon(const Ogre::Rectangle& rect) {
-	std::vector<Ogre::Vector2> polygon;
-	polygon.push_back(Ogre::Vector2(rect.left, rect.top));
-	polygon.push_back(Ogre::Vector2(rect.right, rect.top));
-	polygon.push_back(Ogre::Vector2(rect.right, rect.bottom));
-	polygon.push_back(Ogre::Vector2(rect.left, rect.bottom));
-	return polygon;
+CLevelData::LevelObjectList CLevelData::GetObjectRectangle(const Ogre::Rectangle& rect) {
+	// TODO: use space partitioning here
+	LevelObjectList results = GetObjectRectangle(rect, this->staticLevelData);
+	LevelObjectList temp = GetObjectRectangle(rect, this->dynamicLevelData);
+	results.insert(results.end(), temp.begin(), temp.end());
+	return results;
 }
 
-std::vector<Ogre::Vector2> MakePolygon(const Ogre::Rectangle& rect, const Ogre::Vector2& translation) {
-	std::vector<Ogre::Vector2> polygon;
+Game::Polygon MakePolygon(const Ogre::Rectangle& rect, const Ogre::Vector2& translation) {
+	Game::Polygon polygon;
 	if (translation.x > 0 && translation.y > 0) { // top right
-		polygon.push_back(Ogre::Vector2(rect.left, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.top+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.top+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.bottom+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.bottom));
-		polygon.push_back(Ogre::Vector2(rect.left, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.top+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.top+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.bottom+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.bottom));
 	} else if (translation.x > 0 && translation.y == 0) { // right
-		polygon.push_back(Ogre::Vector2(rect.left, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.bottom));
-		polygon.push_back(Ogre::Vector2(rect.left, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.bottom));
 	} else if (translation.x > 0 && translation.y < 0) { // bottom right
-		polygon.push_back(Ogre::Vector2(rect.left, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.top+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.bottom+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.bottom+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.left, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.top+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.bottom+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.bottom+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.bottom));
 	} else if (translation.x == 0 && translation.y > 0) { // top
-		polygon.push_back(Ogre::Vector2(rect.left, rect.top + translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.top + translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.bottom));
-		polygon.push_back(Ogre::Vector2(rect.left, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.top + translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.top + translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.bottom));
 	} else if (translation.x == 0 && translation.y < 0) { // bottom
-		polygon.push_back(Ogre::Vector2(rect.left, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.bottom + translation.y));
-		polygon.push_back(Ogre::Vector2(rect.left, rect.bottom + translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.bottom + translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.bottom + translation.y));
 	} else if (translation.x < 0 && translation.y > 0) { // top left
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.top+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.top+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.bottom));
-		polygon.push_back(Ogre::Vector2(rect.left, rect.bottom));
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.bottom+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.top+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.top+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.bottom+translation.y));
 	} else if (translation.x < 0 && translation.y == 0) { // left
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.bottom));
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.bottom));
 	} else if (translation.x < 0 && translation.y < 0) { // bottom left
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.top+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.left, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.top));
-		polygon.push_back(Ogre::Vector2(rect.right, rect.bottom));
-		polygon.push_back(Ogre::Vector2(rect.right+translation.x, rect.bottom+translation.y));
-		polygon.push_back(Ogre::Vector2(rect.left+translation.x, rect.bottom+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.top+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.top));
+		polygon.AddPoint(Ogre::Vector2(rect.right, rect.bottom));
+		polygon.AddPoint(Ogre::Vector2(rect.right+translation.x, rect.bottom+translation.y));
+		polygon.AddPoint(Ogre::Vector2(rect.left+translation.x, rect.bottom+translation.y));
 	}
 	return polygon;
 }
 
-/** Check if axis separates polygon1 and polygon2 */
-bool IsSeparatingAxis(const std::vector<Ogre::Vector2>& polygon1, const std::vector<Ogre::Vector2>& polygon2, const Ogre::Vector2& axis) {
-	// get normal of axis
-	Ogre::Vector2 normal( -axis.y, axis.x);
-	//Ogre::Vector2 normal = axis;
-	normal.normalise();
+Ogre::Vector2 CLevelData::ResolveCollision(PLevelObject object, const Ogre::Vector2& translation) {
 
-	// project polygon1
-	float min1, max1; // projected intervall of polygon1
-	float dp;
-	min1 = max1 = (normal.dotProduct(polygon1[0]));
-	for (int i = 1; i < polygon1.size(); i++) {
-		dp = (normal.dotProduct(polygon1[i]));
-		if (dp < min1) min1 = dp;
-		if (dp > max1) max1 = dp;
-	}
+	bool bResolveFinished = false;
 
-	// project polygon2
-	float min2, max2; // projected intervall of polygon2
-	min2 = max2 = (normal.dotProduct(polygon2[0]));
-	for (int i = 1; i < polygon2.size(); i++) {
-		dp = (normal.dotProduct(polygon2[i]));
-		if (dp < min2) min2 = dp;
-		if (dp > max2) max2 = dp;
-	}
+	// get Rectangle and corresponding Polygon once; change manually if needed
+	Ogre::Rectangle rect = object->GetBoundingRectangle();
+	Game::Polygon objectPolygon = object->GetBoundingBoxPolygon();
 
-#ifdef TESTING
-	std::cout << min1 << ", " << max1 << "; " << min2 << ", " << max2 << std::endl;
-#endif
+	Ogre::Vector2 currentTranslation = translation; // translation we are performing currently
+	Ogre::Vector2 result(0,0); 	// all performed translations until any given point
 
-	// check for overlap
-	//return ( (min1 < min2 && max1 <= min2) || (min1 >= max2 && max1 > max2) );
-	return (min1 - max2 > 0) || (min2 - max1 > 0);
-}
+	// stores translation vector for each face we already moved along
+	std::map<Game::Face, Ogre::Vector2> visitedFaces;
 
-Ogre::Vector2 CLevelData::ResolveCollision(const Ogre::Rectangle& rect, const Ogre::Vector2& translation) {
-	if (translation.x == 0 && translation.y == 0)
-		return translation;
+	LevelObjectList ignoreObjects; // ignored objects - objects which already returned non-blocking collision
 
-#ifdef TESTING
-	std::cout << "ResolveCollision: (" << rect.left << ", " << rect.top;
-	std::cout << ", " << rect.right << ", " << rect.bottom << ") " << translation << std::endl;
-#endif
+	while (!bResolveFinished) {
 
-	// I. get all objects inside the smallest rectangle containing rect and rect+translation
-	Ogre::Rectangle broadRect( rect );
-	if (translation.x > 0)
-		broadRect.right = rect.right + translation.x;
-	else
-		broadRect.left = rect.left + translation.x;
-	if (translation.y > 0)
-		broadRect.top = rect.top + translation.y;
-	else
-		broadRect.bottom = rect.bottom + translation.y;
-	LevelObjectList objects = GetObjectRectangle(broadRect);
-
-	// if no objects are in range, we will not collide
-	if (objects.empty()) {
-		return translation;
-	}
-
-	// II. get objects which actually intersect with the parallelogram described
-	//     by rect and translation (separating axis test)
-	//	   (also check for collision)
-
-	std::vector<Ogre::Vector2> polygon = MakePolygon(rect, translation);
-	assert(!polygon.empty());
-
-	std::vector<Ogre::Vector2> boundingRectPoly;
-
-	LevelObjectList collisionObjects;
-	LevelObjectList intersectingObjects;
-	Ogre::Rectangle boundingRect;
-	for (LevelObjectListItor it = objects.begin(); it != objects.end(); ++it) {
-		const PLevelObject& obj = *it;
-		boundingRect = CoordinateTransform(obj->GetBoundingBox());
-
-		if (Intersect(rect, boundingRect)) {
-			intersectingObjects.push_back(obj);
+		if (approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f)) {
+			bResolveFinished = true;
+			continue;
 		}
 
-		// skip separating axis test if we already have collision
-		if (!intersectingObjects.empty())
+		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 (currentTranslation.x > 0)
+			broadRect.right = rect.right + currentTranslation.x;
+		else
+			broadRect.left = rect.left + currentTranslation.x;
+		if (currentTranslation.y > 0)
+			broadRect.top = rect.top + currentTranslation.y;
+		else
+			broadRect.bottom = rect.bottom + currentTranslation.y;
+		LevelObjectList objects = GetObjectRectangle(broadRect);
+
+		// if no objects are in range, we will not collide
+		if (objects.empty()) {
+			result += currentTranslation;
+			currentTranslation = translation - result;
+			TEST_LOG("objects.empty()");
 			continue;
-
-		boundingRectPoly = MakePolygon(boundingRect);
-
-		// test separating axes
-		if (!IsSeparatingAxis(polygon, boundingRectPoly, Ogre::Vector2::UNIT_X)) {
-			if (!IsSeparatingAxis(polygon, boundingRectPoly, Ogre::Vector2::UNIT_Y)) {
-				if (!IsSeparatingAxis(polygon, boundingRectPoly, translation))
-				{
-					collisionObjects.push_back(obj);
-				}
-			}
-		}
-	}
-
-	// in case of collision, resolve
-	if (!intersectingObjects.empty()) {
-#ifdef TESTING
-		std::cout << "COLLISION" << std::endl;
-#endif
-
-		const PLevelObject& obj = intersectingObjects[0];
-		boundingRect = CoordinateTransform(obj->GetBoundingBox());
-		Ogre::Vector2 translationResolve(0,0);
-
-#ifdef TESTING
-		std::cout << "(" << boundingRect.left << ", " << boundingRect.top;
-		std::cout << ", " << boundingRect.right << ", " << boundingRect.bottom << ")" <<  std::endl;
-#endif
-
-		// check if there is a _very_ short way out (aka rounding error)
-		if (fabs(boundingRect.left - rect.right) <= 0.0001f)
-			translationResolve.x = boundingRect.left - rect.right;
-		else if (fabs(boundingRect.right - rect.left) <= 0.0001f)
-			translationResolve.x = boundingRect.right - rect.left;
-		else if (fabs(boundingRect.bottom - rect.top) <= 0.0001f)
-			translationResolve.y = boundingRect.bottom - rect.top;
-		else if (fabs(boundingRect.top - rect.bottom) <= 0.0001f)
-			translationResolve.y = boundingRect.top - rect.bottom;
-		else {
-			// there is no short way out, choose the shortest in opposite translation direction
-			if (translation.x > 0) {
-				translationResolve.x = boundingRect.left - rect.right;
-			} else {
-				translationResolve.x = boundingRect.right - rect.left;
-			}
-
-			if (translation.y > 0) {
-				translationResolve.y = boundingRect.bottom - rect.top;
-			} else {
-				translationResolve.y = boundingRect.top - rect.bottom;
-			}
-
-			if (fabs(translationResolve.x) > fabs(translationResolve.y))
-				translationResolve.x = 0;
-			else
-				translationResolve.y = 0;
 		}
 
-#ifdef TESTING
-		std::cout << translationResolve << std::endl;
-#endif
+		// II. get objects which actually intersect with the parallelogram described
+		//     by rect and translation (separating axis test)
+		//	   (also check for collision)
 
-		// now translationResolve will resolve the collision
-		// perform this and repeat translation
+		Game::Polygon movementParallelogram = MakePolygon(rect, currentTranslation);
 
-		Ogre::Rectangle transRect;
-		transRect.left = rect.left + translationResolve.x;
-		transRect.right = rect.right + translationResolve.x;
-		transRect.top = rect.top + translationResolve.y;
-		transRect.bottom = rect.bottom + translationResolve.y;
-		return translationResolve + ResolveCollision(transRect, translation);
-	}
+		LevelObjectList collisionObjects; // objects we could collide with
+		LevelObjectList intersectingObjects; // objects we currently are colliding with
 
-	if (collisionObjects.empty()) {
-		return translation; // no collision
-	}
+		for (LevelObjectListItor it = objects.begin(); it != objects.end(); ++it) {
+			const PLevelObject& obj = *it;
+			if (std::find(ignoreObjects.begin(), ignoreObjects.end(), obj) != ignoreObjects.end())
+				continue;
 
-	// III. get maximum translation without collision
+			Ogre::Rectangle boundingRect = obj->GetBoundingRectangle();
+			Game::Polygon objPolygon = obj->GetPolygon();
 
-	// get faces inside parallelogram
-	Ogre::Vector2 maxTranslation(0,0);
-	//bool bFirst = true;
-	bool bFirstX = true; bool bFirstY = true;
-	std::vector<Ogre::Vector2> face;
-	for (LevelObjectListItor it = collisionObjects.begin(); it != collisionObjects.end(); ++it) {
-		const PLevelObject& obj = *it;
-		boundingRect = CoordinateTransform(obj->GetBoundingBox());
-
-		// get faces in x direction
-		face.clear();
-		if (translation.x > 0) {
-			face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.top));
-			face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.bottom));
-		} else if (translation.x < 0) {
-			face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.top));
-			face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.bottom));
-		}
-		if (!face.empty()) {
-			if (!IsSeparatingAxis(face, polygon, Ogre::Vector2::UNIT_X) &&
-				!IsSeparatingAxis(face, polygon, Ogre::Vector2::UNIT_Y) &&
-				!IsSeparatingAxis(face, polygon, translation))
-			{
-#ifdef TESTING
-				std::cout << "FC_X: " << face[0] << ", " << face[1] << std::endl;
-#endif
-				if (translation.x > 0 && boundingRect.left - rect.right >= 0) {
-					if (bFirstX || maxTranslation.x > boundingRect.left - rect.right) {
-						maxTranslation.x = boundingRect.left - rect.right;
-						bFirstX = false;
-					}
-				} else if (translation.x < 0 && boundingRect.right - rect.left <= 0) {
-					if (bFirstX || maxTranslation.x < boundingRect.right - rect.left) {
-						maxTranslation.x = boundingRect.left - rect.left;
-						bFirstX = false;
+			// check for intersection
+			if (Intersect(rect, boundingRect)) {
+				std::pair<bool, Ogre::Vector2> result = objPolygon.SeparatingAxesTest(objectPolygon);
+				if (!result.first) {
+					if (obj->HandleCollision(object, currentTranslation) == CLevelObject::ctBlocking) {
+						intersectingObjects.push_back(obj);
+					} else {
+						ignoreObjects.push_back(obj);
 					}
 				}
 			}
+
+			// skip if we already have collision
+			if (!intersectingObjects.empty())
+				continue;
+
+			// test separating axes
+			std::pair<bool, Ogre::Vector2> result = objPolygon.SeparatingAxesTest(movementParallelogram);
+			if (!result.first) {
+				collisionObjects.push_back(obj);
+			}
 		}
 
-		// get faces in y direction
-		face.clear();
-		if (translation.y > 0) {
-			face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.bottom));
-			face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.bottom));
-		} else if (translation.y < 0) {
-			face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.top));
-			face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.top));
+		// in case of collision, resolve
+		if (!intersectingObjects.empty()) {
+			TEST_LOG("COLLISION");
+
+			const PLevelObject& obj = intersectingObjects[0];
+
+			// get minimum translation vector
+			std::pair<bool, Ogre::Vector2> SATresult = objectPolygon.SeparatingAxesTest(obj->GetPolygon());
+			Ogre::Vector2 translationResolve = SATresult.second * (1.0f + TOLERANCE);
+
+			/* TODO: only use MTV if it is very short, find better solution otherwise
+
+			// always use MTV if it is _very_ short (usually a rounding error)
+			if (translationResolve.length() > 0.001f) {
+				// MTV is "big" - find solution in opposite moving direction
+				// ...?
+			}
+			*/
+
+			TEST_LOG(translationResolve);
+
+			/*
+			// now translationResolve will resolve the collision
+			// perform this and repeat translation
+			*/
+
+			rect.left = rect.left + translationResolve.x;
+			rect.right = rect.right + translationResolve.x;
+			rect.top = rect.top + translationResolve.y;
+			rect.bottom = rect.bottom + translationResolve.y;
+			objectPolygon.Translate(translationResolve);
+
+			result = result + translationResolve;
+			currentTranslation = translation - result;
+			continue;
 		}
-		if (!face.empty()) {
-			if (!IsSeparatingAxis(face, polygon, Ogre::Vector2::UNIT_X) &&
-				!IsSeparatingAxis(face, polygon, Ogre::Vector2::UNIT_Y) &&
-				!IsSeparatingAxis(face, polygon, translation))
-			{
-#ifdef TESTING
-				std::cout << "FC_Y: " << face[0] << ", " << face[1] << std::endl;
-#endif
-				if (translation.y > 0 && boundingRect.bottom - rect.top >= 0) {
-					if (bFirstY || maxTranslation.y > boundingRect.bottom - rect.top) {
-						maxTranslation.y = boundingRect.bottom - rect.top;
-						bFirstY = false;
-					}
-				} else if (translation.y < 0 && boundingRect.top - rect.bottom <= 0) {
-					if (bFirstY || maxTranslation.y < boundingRect.top - rect.bottom) {
-						maxTranslation.y = boundingRect.top - rect.bottom;
-						bFirstY = false;
-					}
-				}
-			}
+
+		// no collision
+		if (collisionObjects.empty()) {
+			result += currentTranslation;
+			currentTranslation = translation - result;
+			TEST_LOG("collisionObjects.empty()");
+
+			continue;
 		}
-	}
 
-#ifdef TESTING
-	std::cout << maxTranslation << "; " << translation << std::endl;
-#endif
+		// III. get maximum translation without collision
 
-	// IV. get maximum translation in a multiple of translation vector without collision
-	float factorX = maxTranslation.x / translation.x;
-	float factorY = maxTranslation.y / translation.y;
+		Game::Face nearestFace;
+		PLevelObject nearestObj;
+		float nearestFactor;
+		bool bNotFound = true;
+		bool LastCollisionNonBlocking = false;
 
-	factorX = Ogre::Math::Clamp(factorX, 0.0f, 1.0f);
-	factorY = Ogre::Math::Clamp(factorY, 0.0f, 1.0f);
+		do {
+			std::vector<Game::Face> objFaces;
+			bNotFound = true;
+			LastCollisionNonBlocking = false;
 
-	Ogre::Vector2 currTranslation;
-	bool bTranslationXDone;
-	if (bFirstX)
-		bTranslationXDone = false;
-	else if (bFirstY)
-		bTranslationXDone = true;
-	else
-		bTranslationXDone = factorX > 0 && factorX < factorY;
-	if (bTranslationXDone)
-		currTranslation = factorX * translation;
-	else
-		currTranslation = factorY * translation;
+			for (LevelObjectListItor it = collisionObjects.begin(); it != collisionObjects.end(); ++it) {
+				const PLevelObject& obj = *it;
+				if (std::find(ignoreObjects.begin(), ignoreObjects.end(), obj) != ignoreObjects.end())
+					continue;
 
-#ifdef TESTING
-	std::cout << factorX << "/ " << factorY << "; X Done = " << bTranslationXDone;
-	std::cout << " firstX = " << bFirstX << " firstY = " << bFirstY << std::endl;
-#endif
+				// check all faces for possible collision
+				objFaces = obj->GetPolygon().GetFaces(false);
+				for (int i = 0; i < objFaces.size(); i++) {
+					// only process faces facing against movement direction
+					if (objFaces[i].normal.dotProduct(currentTranslation) >= 0)
+						continue;
 
-	Ogre::Rectangle tBounds;
-	if (bTranslationXDone) {
-		// find maximum translation in Y direction
-		tBounds.left = rect.left + currTranslation.x;
-		tBounds.right = rect.right + currTranslation.x;
-		if (translation.y > 0) {
-			tBounds.bottom = rect.bottom + currTranslation.y;
-			tBounds.top = rect.top + translation.y;
-		} else {
-			tBounds.top = rect.top + currTranslation.y;
-			tBounds.bottom = rect.bottom + translation.y;
-		}
-	} else {
-		// find maximum translation in X direction
-		tBounds.top = rect.top + currTranslation.y;
-		tBounds.bottom = rect.bottom + currTranslation.y;
-		if (translation.x > 0) {
-			tBounds.left = rect.left + currTranslation.x;
-			tBounds.right = rect.right + translation.x;
-		} else {
-			tBounds.left = rect.left + translation.x;
-			tBounds.right = rect.right + currTranslation.x;
-		}
-	}
-
-#ifdef TESTING
-	std::cout << tBounds.left << ", " << tBounds.top << ", " << tBounds.right << ", " << tBounds.bottom << std::endl;
-#endif
-
-	maxTranslation = Ogre::Vector2::ZERO;
-	bool bFirst = true;
-	boundingRectPoly = MakePolygon(tBounds);
-	for (LevelObjectListItor it = objects.begin(); it != objects.end(); ++it) {
-		const PLevelObject& obj = *it;
-		boundingRect = CoordinateTransform(obj->GetBoundingBox());
-
-		if (Intersect(boundingRect, tBounds)) {
-			face.clear();
-			if (bTranslationXDone) {
-				if (translation.y > 0) {
-					face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.bottom));
-					face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.bottom));
-				} else if (translation.y < 0) {
-					face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.top));
-					face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.top));
-				}
-			} else {
-				if (translation.x > 0) {
-					face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.top));
-					face.push_back(Ogre::Vector2(boundingRect.left, boundingRect.bottom));
-				} else if (translation.x < 0) {
-					face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.top));
-					face.push_back(Ogre::Vector2(boundingRect.right, boundingRect.bottom));
-				}
-			}
-			if (!IsSeparatingAxis(face, boundingRectPoly, Ogre::Vector2::UNIT_X) &&
-				!IsSeparatingAxis(face, boundingRectPoly, Ogre::Vector2::UNIT_Y)) {
-				if (bTranslationXDone) {
-					if (translation.y > 0) {
-						if (bFirst || maxTranslation.y > boundingRect.bottom - rect.top - currTranslation.y) {
-							maxTranslation.y = boundingRect.bottom - rect.top - currTranslation.y;
-							bFirst = false;
-						}
-					} else {
-						if (bFirst || maxTranslation.y > boundingRect.top - rect.bottom - currTranslation.y) {
-							maxTranslation.y = boundingRect.top - rect.bottom - currTranslation.y;
-							bFirst = false;
-						}
-					}
-				} else {
-					if (translation.x > 0) {
-						if (bFirst || maxTranslation.x > boundingRect.left - rect.right - currTranslation.x) {
-							maxTranslation.x = boundingRect.left - rect.right - currTranslation.x;
-							bFirst = false;
-						}
-					} else {
-						if (bFirst || maxTranslation.x > boundingRect.right - rect.left - currTranslation.x) {
-							maxTranslation.x = boundingRect.right - rect.left - currTranslation.x;
-							bFirst = false;
+					Game::Polygon facePoly(objFaces[i]);
+					std::pair<bool, float> result = objectPolygon.TranslationIntersect(facePoly, currentTranslation);
+					TEST_LOG(result.first << ", " << result.second << " " << objFaces[i]);
+					if (result.first && result.second >= -0.0f) {
+						 // find nearest collision
+						if (bNotFound || fabs(nearestFactor) > fabs(result.second)) {
+							bNotFound = false;
+							nearestFace = objFaces[i];
+							nearestFactor = result.second;
+							nearestObj = obj;
+							TEST_LOG("found collision");
 						}
 					}
 				}
 			}
+
+			if (!bNotFound) {
+				/*if (approximatelyEqual(nearestFactor, 0.0f)) {
+					// can't move; abort
+					TEST_LOG("nearestFactor = 0");
+					currentTranslation = Ogre::Vector2::ZERO;
+					bResolveFinished = true;
+					break;
+				}*/
+
+				// translate towards nearest object
+				Ogre::Vector2 maxTranslation = nearestFactor*currentTranslation*(1.0f - TOLERANCE);
+				result += maxTranslation;
+				objectPolygon.Translate(maxTranslation);
+				rect.left = rect.left + maxTranslation.x;
+				rect.right = rect.right + maxTranslation.x;
+				rect.top = rect.top + maxTranslation.y;
+				rect.bottom = rect.bottom + maxTranslation.y;
+
+				currentTranslation -= maxTranslation;
+
+				// check if collision is blocking
+				if (nearestObj->HandleCollision(object, maxTranslation) != CLevelObject::ctBlocking) {
+					ignoreObjects.push_back(nearestObj);
+					nearestObj.reset();
+					LastCollisionNonBlocking = true;
+					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;
+			TEST_LOG("currentTranslation.isZeroLength()");
+			continue;
 		}
+
+		if (bNotFound) {
+			// no objects are blocking our way
+			result += currentTranslation;
+			currentTranslation = translation - result;
+			TEST_LOG("bNotFound=true");
+			continue;
+		}
+
+		// IV. move along nearest Face
+		/* We now arrived at a face which blocks our way
+		 * since we alreade moved towards it, there should be exactly one separating axis
+		 * out of the possible {X, Y, face_direction}
+		 * if we move along the separating axis, it is impossible for us to collide with the face.
+		 * thus, we check the remaining axes (which are non-separating) and test how far along
+		 * the separating axis we'd have to move that one of these axes become separating.
+		 * at last only one axis remains which is non-separating. now we move along the newly
+		 * discovered separating axis until all three axes are separating.
+		 * now we should be able to continue towards the goal.
+		 * if any collision occurs along our way, we cancel and try to resolve that collision.
+		 */
+
+		TEST_LOG("nearestface= " << nearestFace);
+
+		Ogre::Vector2 faceDirection = nearestFace.v2 - nearestFace.v1;
+		Game::Polygon facePoly(nearestFace);
+
+		// possible separating axes are UNIT_X, UNIT_Y, facedirection
+		std::vector<Ogre::Vector2> separatingAxes;
+		separatingAxes.push_back(Ogre::Vector2::UNIT_X);
+		separatingAxes.push_back(Ogre::Vector2::UNIT_Y);
+
+		// make sure faceDirection is not a mulitple of UNIT_X or UNIT_Y
+		if (!approximatelyEqual(faceDirection.x, 0.0f) && !approximatelyEqual(faceDirection.y, 0.0f))
+			separatingAxes.push_back(faceDirection);
+
+		// find separating axis
+		Ogre::Vector2 separatingAxis(0.0f, 0.0f);
+		for (int i = 0; i < separatingAxes.size(); i++) {
+			SATResult satResult = objectPolygon.IsSeparatingAxis(facePoly, separatingAxes[i]);
+			if (satResult.IsSeparating || approximatelyEqual(satResult.MinimumOverlap, 0.0f)) {
+				separatingAxis = separatingAxes[i].normalisedCopy();
+				separatingAxes.erase(separatingAxes.begin() + i);
+				TEST_LOG("sep axis = " << separatingAxis);
+				TEST_LOG("SAT Result: " << satResult.IsSeparating << "; " << satResult.MinimumOverlap << "; " << satResult.MaximumOverlap);
+				break;
+			}
+		}
+
+		TEST_LOG("remaining axes: " << separatingAxes.size());
+
+		// make separatingAxis face in same direction as currentTranslation
+		if (separatingAxis.dotProduct(currentTranslation) < 0.0f)
+			separatingAxis = -separatingAxis;
+
+		// calculate maximum allowed translation along separatingAxis
+		float maxFactor = separatingAxis.dotProduct(currentTranslation);
+
+		// find minimum translation to "create" a new separating axis
+		float minFactor = std::numeric_limits<float>::infinity();
+		for (int i = 0; i < separatingAxes.size(); i++) {
+			SATResult satResult = objectPolygon.IsSeparatingAxis(facePoly, separatingAxes[i]);
+			if (!satResult.IsSeparating) {
+				// get minimum translation vector in direction of currentTranslation
+				Ogre::Vector2 axisNormal = LeftHandNormal(separatingAxes[i]);
+				Ogre::Vector2 MTV = satResult.MinimumOverlap * axisNormal;
+				if (MTV.dotProduct(currentTranslation) < 0)
+					MTV = satResult.MaximumOverlap * axisNormal;
+
+				float factor;
+				if (approximatelyEqual(separatingAxis.y, 0.0f) || (MTV.x > MTV.y && !approximatelyEqual(separatingAxis.x, 0.0f)))
+					factor = MTV.x / separatingAxis.x;
+				else
+					factor = MTV.y / separatingAxis.y;
+
+				if (fabs(minFactor) > fabs(factor))
+					minFactor = factor;
+			} else {
+				TEST_LOG("found another sep. axis: " << separatingAxes[i]);
+				TEST_LOG("SAT Result: " << satResult.IsSeparating << "; " << satResult.MinimumOverlap << "; " << satResult.MaximumOverlap);
+			}
+		}
+
+		if (minFactor == std::numeric_limits<float>::infinity()) {
+			// error, abort
+			TEST_LOG("minFactor=inf");
+			bResolveFinished = true;
+			continue;
+		}
+
+		float factor = (fabs(minFactor) < fabs(maxFactor)) ? minFactor : maxFactor;
+
+		currentTranslation = factor*separatingAxis*(1.0f + TOLERANCE);
+
+		// if we already visited that face, we dont want to move in opposite direction
+		if (visitedFaces.find(nearestFace) != visitedFaces.end()) {
+			// check if we are moving backwards
+			float dp = visitedFaces[nearestFace].dotProduct(currentTranslation);
+			if (dp < 0) {
+				// cancel
+				TEST_LOG("dotProduct < 0");
+				return result;
+			}
+		} else {
+			visitedFaces[nearestFace] = currentTranslation;
+		}
+
+		TEST_LOG("currentTranslation = " << currentTranslation);
 	}
 
-	if (bFirst) {
-		// no faces found
-		if (bTranslationXDone)
-			maxTranslation.y = translation.y - currTranslation.y;
-		else
-			maxTranslation.x = translation.x - currTranslation.x;
-	}
-
-#ifdef TESTING
-	std::cout << maxTranslation << std::endl;
-#endif
-
-	maxTranslation = currTranslation + maxTranslation;
-
-
-#ifdef TESTING
-	std::cout << maxTranslation << std::endl;
-#endif
-
-	return maxTranslation;
+	TEST_LOG("result=" << result);
+	return result;
 }
 
 }

File src/GameObjects/CLevelData.h

View file
  • Ignore whitespace
 #define LEVELDATA_H_
 
 #include "LevelObjects.h"
+#include "../testlog.h"
 #include "../Core/CLog.h"
 
 #include "Ogre.h"
 	@param range Maximum distance to check for constraints */
 	Ogre::Rectangle Collision(const Ogre::Rectangle& rect, float range);
 
-	/** Resolve Collision for a Rectangle rect which moves along translation
+	/** Resolve Collision for a given LevelObject which moves along translation (handles levelobject as its 2D bounding box)
 	@return Translation vector which avoids collision; returns translation if no collision occurs; a multiple of translation otherwise */
-	Ogre::Vector2 ResolveCollision(const Ogre::Rectangle& rect, const Ogre::Vector2& translation);
+	Ogre::Vector2 ResolveCollision(PLevelObject object, const Ogre::Vector2& translation);
 
 	/** Removes all objects */
 	void ClearData();
 	typedef std::vector<PLevelObject> LevelObjectList;
 	typedef std::vector<PLevelObject>::iterator LevelObjectListItor;
 
+	LevelObjectList GetObjectRectangle(const Ogre::Rectangle& rect, LevelObjectList& list);
 	LevelObjectList GetObjectRectangle(const Ogre::Rectangle& rect);
 
 	LevelObjectList staticLevelData;
 	LevelObjectList dynamicLevelData;
 };
 
-std::vector<Ogre::Vector2> MakePolygon(const Ogre::Rectangle& rect, const Ogre::Vector2& translation);
+Game::Polygon MakePolygon(const Ogre::Rectangle& rect, const Ogre::Vector2& translation);
 bool IsSeparatingAxis(const std::vector<Ogre::Vector2>& polygon1, const std::vector<Ogre::Vector2>& polygon2, const Ogre::Vector2& axis);
 
 }

File src/GameObjects/Geometry.cpp

View file
  • Ignore whitespace
+/*
+ * Geometry.cpp
+ *
+ *  Created on: Sep 28, 2010
+ *      Author: crt
+ */
+
+#include "Geometry.h"
+
+namespace Game {
+
+
+/*------------------
+ * Face
+ *------------------*/
+
+Face::Face() : v1(0.0f, 0.0f), v2(0.0f, 0.0f), normal(0.0f, 0.0f)
+{ }
+
+Face::Face(const Ogre::Vector2& vector1, const Ogre::Vector2& vector2)
+	: v1(vector1), v2(vector2)
+{
+	normal = RightHandNormal(v2-v1);
+}
+
+Face::Face(const Ogre::Vector2& vector1, const Ogre::Vector2& vector2, const Ogre::Vector2& normal)
+	: v1(vector1), v2(vector2), normal(normal)
+{
+	this->normal.normalise();
+}
+
+bool Face::operator==(const Face& face2) const {
+	// dont compare normal
+	return ( (v1 == face2.v1 && v2 == face2.v2) ||
+		     (v1 == face2.v2 && v2 == face2.v1) );
+}
+
+std::ostream& operator<< (std::ostream& os, const Face &face) {
+	os << "Face( " << face.v1 << "; " << face.v2 << "; " << face.normal << " )";
+	return os;
+}
+
+
+/*------------------
+ * Polygon
+ *------------------*/
+
+Polygon::Polygon() {
+}
+
+
+Polygon::Polygon(const Face& face) {
+	this->Points.push_back(face.v1);
+	this->Points.push_back(face.v2);
+}
+
+Polygon::Polygon(const Ogre::Rectangle& rect) {
+	Points.push_back(Ogre::Vector2(rect.left, rect.bottom));
+	Points.push_back(Ogre::Vector2(rect.right, rect.bottom));
+	Points.push_back(Ogre::Vector2(rect.right, rect.top));
+	Points.push_back(Ogre::Vector2(rect.left, rect.top));
+}
+
+Polygon::~Polygon() {
+}
+
+bool Polygon::operator==(const Polygon& poly2) const {
+	if (Points.size() != poly2.Points.size())
+		return false;
+	// search for each point of this in poly2
+	for (int i = 0; i < Points.size(); i++) {
+		if (std::find(poly2.Points.begin(), poly2.Points.end(), Points[i]) == poly2.Points.end())
+			return false;
+	}
+	return true;
+}
+
+void Polygon::AddPoint(const Ogre::Vector2& vector) {
+	if (std::find(Points.begin(), Points.end(), vector) == Points.end()) {
+		Points.push_back(vector);
+	}
+}
+
+void Polygon::RemovePoint(const Ogre::Vector2& vector) {
+	VectorList::iterator it = std::find(Points.begin(), Points.end(), vector);
+	if (it != Points.end())
+		Points.erase(it);
+}
+
+void Polygon::Clear() {
+	Points.clear();
+}
+
+int Polygon::FaceCount() const {
+	if (Points.size() == 2)
+		return 1;
+	else if (Points.size() > 2)
+		return Points.size();
+	else
+		return 0;
+}
+
+std::vector<Face> Polygon::GetFaces(bool NoNormals) const {
+	std::vector<Face> result;
+	if (Points.size() == 2) {
+		result.push_back(Face(Points[0], Points[1]));
+	} else if (Points.size() > 2) {
+		for (int i = 0; i < Points.size(); i++) {
+			if (i != Points.size()-1) {
+				if (NoNormals)
+					result.push_back(Face(Points[i], Points[i+1], Ogre::Vector2::ZERO));
+				else
+					result.push_back(Face(Points[i], Points[i+1]));
+			} else {
+				if (NoNormals)
+					result.push_back(Face(Points[i], Points[0], Ogre::Vector2::ZERO));
+				else
+					result.push_back(Face(Points[i], Points[0]));
+			}
+		}
+	}
+	return result;
+}
+
+void Polygon::Translate(const Ogre::Vector2& translation) {
+	// translate all points
+	for (VectorList::iterator it = Points.begin(); it != Points.end(); ++it) {
+		*it = *it + translation;
+	}
+}
+
+/** Performs separating axis test for a given axis and polygon
+@return Test Result; see Polygon::SATResult */
+SATResult Polygon::IsSeparatingAxis(const Polygon& polygon, const Ogre::Vector2& axis) const {
+	if (this->Points.size() < 1 || polygon.Points.size() < 1) {
+		return SATResult(true, 0.0f, 0.0f);
+	}
+
+	// get normal of axis
+	Ogre::Vector2 normal = LeftHandNormal(axis);
+
+	// project this
+	float min1, max1; // projected intervall of this
+	float dp;
+	min1 = max1 = normal.dotProduct(Points[0]);
+	for (int i = 1; i < Points.size(); i++) {
+		dp = normal.dotProduct(Points[i]);
+		if (dp < min1) min1 = dp;
+		if (dp > max1) max1 = dp;
+	}
+
+	// project polygon2
+	float min2, max2; // projected intervall of polygon2
+	min2 = max2 = normal.dotProduct(polygon.Points[0]);
+	for (int i = 1; i < polygon.Points.size(); i++) {
+		dp = normal.dotProduct(polygon.Points[i]);
+		if (dp < min2) min2 = dp;
+		if (dp > max2) max2 = dp;
+	}
+
+
+	SATResult result;
+	result.IsSeparating = definitelyGreaterThan(min1 - max2, 0.0f) || definitelyGreaterThan(min2 - max1, 0.0f);
+
+	if (min2 <= min1) {
+		result.MinimumOverlap = min2 - max1;
+		result.MaximumOverlap = max2 - min1;
+	} else {
+		result.MinimumOverlap = min2 - max1;
+		result.MaximumOverlap = max2 - min1;
+	}
+
+	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 minAxis(0,0);
+
+	bool FoundAxis = false;
+	for (int i = 0; i < faces.size(); i++) {
+		Ogre::Vector2 axis = faces[i].GetVector();
+		axis.normalise();
+		SATResult sat = this->IsSeparatingAxis(polygon, axis);
+		if (!sat.IsSeparating) {
+			if (fabs(minOverlap) > fabs(sat.MinimumOverlap)) {
+				minOverlap = sat.MinimumOverlap;
+				minAxis = axis;
+			}
+		} else {
+			FoundAxis = true;
+		}
+	}
+
+
+	if (FoundAxis) {
+		return std::pair<bool, Ogre::Vector2>(true, Ogre::Vector2::ZERO);
+	} 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
+@return true if a separating axis was found
+        in case of intersection, also returns minimum translation vector */
+std::pair<bool, Ogre::Vector2> Polygon::SeparatingAxesTest(const Polygon& polygon) const {
+	float minOverlap = std::numeric_limits<float>::infinity();
+
+	// check all faces of this
+	std::vector<Face> faces = this->GetFaces(true);
+	std::pair<bool, Ogre::Vector2> SATRes = SeparatingAxesTest(polygon, faces, minOverlap);
+
+	// check all faces of polygon
+	faces = polygon.GetFaces(true);
+	float minOverlap2 = minOverlap;
+	std::pair<bool, Ogre::Vector2> SATRes2 = SeparatingAxesTest(polygon, faces, minOverlap2);
+
+	if (SATRes.first || SATRes2.first)
+		return std::pair<bool, Ogre::Vector2>(true, Ogre::Vector2::ZERO);
+	else {
+		if (fabs(minOverlap) <= fabs(minOverlap2))
+			return std::pair<bool, Ogre::Vector2>(false, SATRes.second);
+		else
+			return std::pair<bool, Ogre::Vector2>(false, SATRes2.second);
+	}
+}
+
+/** Checks for intersection when translating this by direction
+	@return false if no intersection occurs
+	        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 {
+	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], direction);
+		std::pair<bool, float> intersection = ray.Intersects(polygon);
+		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], -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);
+}
+
+std::ostream& operator<< (std::ostream& os, const Polygon& polygon) {
+	os << "Polygon( NumFaces=" << polygon.FaceCount() << " )";
+	return os;
+}
+
+
+/*------------------
+ * Ray
+ *------------------*/
+
+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)
+{}
+
+/** Checks for intersection with given ray
+@returns Second element indicates distance from origin of intersection point */
+std::pair<bool, float> Ray::Intersects(const Ray& ray) const {
+	/*
+	 * Solve t*v1 + o1 = u*v2 + o2   <=>  t*v1 - u*v2 = o2 - o1
+	 * <=>  Ax = y  with  y=o2-o1,  A = (v1, -v2),  x = (t, u)^t
+	 * <=> x = A^-1 y
+	 * <=> t = A^-1[1,1] * y[1] + A^-1[1,2] * y[2]
+	 *
+	 */
+
+	// calculate A^-1 using Cramer's rule
+
+	// det(A) = a*d - b*c
+	float det =  -(this->Direction.x * ray.Direction.y) + (this->Direction.y * ray.Direction.x);
+
+	// if det(A) is 0 the rays are parallel
+	if (approximatelyEqual(det, 0.0f))
+		return std::pair<bool, float>(false, 0.0f);
+
+	Ogre::Vector2 y = ray.Origin - this->Origin;
+
+	// first row of A^-1 is 1/det(A)*(d,-b)
+	float t = (1.0f / det) * ( - ray.Direction.y * y.x  + ray.Direction.x * y.y);
+
+	return std::pair<bool, float>(true, t);
+}
+
+/** Checks for intersection with given polygon
+@returns Second element indicates distance from origin of intersection point */
+std::pair<bool, float> Ray::Intersects(const Face& face) const {
+	// construct ray through both points of face and check for intersection
+	Ogre::Vector2 faceDirection = face.v2 - face.v1;
+	Ray faceRay(face.v1, faceDirection);
+	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 <= 1.0f) {
+		return this->Intersects(faceRay);
+	} else {
+		return std::pair<bool, float>(false, 0.0f);
+	}
+}
+
+/** Checks for intersection with given polygon
+@returns Second element indicates distance from origin of intersection point
+@remarks Returns intersection point which is closest to ray's origin*/
+std::pair<bool, float> Ray::Intersects(const Polygon& polygon) const {
+	float nearestFactor = std::numeric_limits<float>::infinity();
+	bool bIntersection = false;
+
+	// check all faces of polygon for intersection; return the nearest
+	std::vector<Face> faces = polygon.GetFaces(true);
+	for (int i = 0; i < faces.size(); i++) {
+		std::pair<bool, float> intersection = this->Intersects(faces[i]);
+		if (intersection.first) {
+			bIntersection = true;
+			if (fabs(nearestFactor) > fabs(intersection.second))
+				nearestFactor = intersection.second;
+		}
+	}
+
+	return std::pair<bool, float>(bIntersection, nearestFactor);
+}
+
+}

File src/GameObjects/Geometry.h

View file
  • Ignore whitespace
+/*
+ * Geometry.h
+ *
+ *  Created on: Sep 28, 2010
+ *      Author: crt
+ */
+
+#ifndef GEOMETRY_H_
+#define GEOMETRY_H_
+
+#include "../testlog.h"
+#include "Ogre.h"
+#include <vector>
+
+namespace Game {
+
+/** Maximum allowed absolute rounding error */
+const float TOLERANCE = 0.001f;
+
+/** 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;
+	Ogre::Vector2 v2;
+	Ogre::Vector2 normal;
+
+	Face();
+	Face(const Ogre::Vector2& vector1, const Ogre::Vector2& vector2);
+	Face(const Ogre::Vector2& vector1, const Ogre::Vector2& vector2, const Ogre::Vector2& normal);
+	bool operator==(const Face& face2) const;
+	bool operator!=(const Face& face2) const { return !(*this == face2); }
+	Ogre::Vector2 GetVector() const { return v2 - v1; }
+
+	// do some random comparison to be able to use Face as a Key in a std::map
+	bool operator< (const Face &face) const { return v1.x < face.v1.x; }
+};
+
+std::ostream& operator<< (std::ostream& os, const Face &face);
+
+/** Defines Result of a Separating Axes Test */
+struct SATResult {
+	/** True, if given Axis is separating */
+	bool IsSeparating;
+	/** Smallest translation along axis which resolves intersection (MinimumOverlap*axis)
+	 *  Undefined if IsSeparating==true*/
+	float MinimumOverlap;
+	/** Smallest translation along axis which resolves intersection in opposite direction of MinimumOverlap
+	 * Undefined if IsSeparating==true*/
+	float MaximumOverlap;
+
+	SATResult() : IsSeparating(true), MinimumOverlap(0.0f), MaximumOverlap(0.0f) {}
+	SATResult(bool isSeparating, float minOverlap, float maxOverlap)
+		: IsSeparating(isSeparating), MinimumOverlap(minOverlap), MaximumOverlap(maxOverlap) {}
+};
+
+/** Defines a Polygon in 2D Space */
+class Polygon {
+public:
+	/** Create empty Polygon */
+	Polygon();
+	/** Create Polygon from one Face */
+	Polygon(const Face& face);
+	/** Create Polygon from Rectangle */
+	Polygon(const Ogre::Rectangle& rect);
+	virtual ~Polygon();
+
+	bool operator==(const Polygon& poly2) const;
+	bool operator!=(const Polygon& poly2) const { return !(*this == poly2); }
+
+	/** Add Point to Polygon
+	@remarks Normals will be calculated wrong if points are not added counter-clockwise */
+	void AddPoint(const Ogre::Vector2& vector);
+	/** Remove Point from Polygon */
+	void RemovePoint(const Ogre::Vector2& vector);
+	/** Remove all Points */
+	void Clear();
+	/** Get Number of Faces */
+	int FaceCount() const;
+	/** Get all faces
+	@param NoNormals Set true to not calculate any normals (faster)
+	@see AddPoint */
+	std::vector<Face> GetFaces(bool NoNormals = false) const;
+	/** Moves all Points by translation */
+	void Translate(const Ogre::Vector2& translation);
+
+	/** 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
+	@return true if a separating axis was found
+	        in case of intersection, also returns minimum translation vector
+	@remarks Only works if polygon is convex */
+	std::pair<bool, Ogre::Vector2> SeparatingAxesTest(const Polygon& polygon) const;
+
+	/** Checks for intersection when translating this by direction
+	@return false if no intersection occurs
+	        in case of intersection also returns maximum factor that a translation of facter*direction leads to no collision */
+	std::pair<bool, float> TranslationIntersect(const Polygon& polygon, const Ogre::Vector2& direction) const;
+private:
+	typedef std::vector<Ogre::Vector2> VectorList;
+	VectorList Points;
+
+	std::pair<bool, Ogre::Vector2> SeparatingAxesTest(const Polygon& polygon, const std::vector<Face>& faces, float &minOverlap) const;
+};
+
+std::ostream& operator<< (std::ostream& os, const Polygon &polygon);
+
+/** Defines a Ray in 2D Space
+ *  Note: direction vector is not neccessarily normalized */
+class Ray {
+public:
+	Ray();
+	Ray(const Ogre::Vector2& origin, const Ogre::Vector2& direction);
+
+	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; }
+	inline const Ogre::Vector2& GetDirection() const { return Direction; }
+	inline Ogre::Vector2 GetPoint(float t) const { return Origin + t*Direction; }
+
+	/** Checks for intersection with given ray
+	@returns Second element indicates distance from origin of intersection point */
+	std::pair<bool, float> Intersects(const Ray& ray) const;
+
+	/** Checks for intersection with given polygon
+	@returns Second element indicates distance from origin of intersection point */
+	std::pair<bool, float> Intersects(const Face& face) const;
+
+	/** Checks for intersection with given polygon
+	@returns Second element indicates distance from origin of intersection point
+	@remarks Returns intersection point which is closest to ray's origin*/
+	std::pair<bool, float> Intersects(const Polygon& polygon) const;
+
+private:
+ 	Ogre::Vector2 Origin;
+ 	Ogre::Vector2 Direction;
+};
+
+}
+
+#endif /* GEOMETRY_H_ */

File src/GameObjects/LevelDataReader.cpp

View file
  • Ignore whitespace
 	try {
 		config.open(sFilename.c_str(), std::ios::in);
 
-		PCustomAABBObject cube;
-		PCustomAABBObject house;
+		POgreLevelObject cube;
+		POgreLevelObject house;
 
 		std::string line;
 		std::string material;
 						position.x = 2.0f;
 						position.y = y - 45.0f;
 						position.z = z + 30.0f;
-						house = PCustomAABBObject(new CCustomAABBObject("tudorhouse.mesh", position));
+						house = POgreLevelObject(new COgreLevelObject("tudorhouse.mesh", position));
 						house->SetAABBSize(hsize);
 						house->SetScale(Ogre::Vector3(0.1,0.1,0.1));
 						target->InsertObject(house, CLevelData::otStatic);
 							position.x = 45.0f;
 							position.y = y + 5.0f;
 							position.z = z + 30.0f;
-							house = PCustomAABBObject(new CCustomAABBObject("tudorhouse.mesh", position));
+							house = POgreLevelObject(new COgreLevelObject("tudorhouse.mesh", position));
 							house->SetAABBSize(hsize);
 							house->SetScale(Ogre::Vector3(0.1,0.1,0.1));
 							target->InsertObject(house, CLevelData::otBackground);
 						position.y = y;
 						position.z = z;
 
-						cube = PCustomAABBObject(new CCustomAABBObject("cube.mesh", position));
+						cube = POgreLevelObject(new COgreLevelObject("cube.mesh", position));
 						cube->SetMaterial(material);
 						cube->SetScale(Ogre::Vector3(0.1,0.1,0.1));
 						cube->SetAABBSize(size);
 
 						position.x = 5.0f;
 
-						cube = PCustomAABBObject(new CCustomAABBObject("cube.mesh", position));
+						cube = POgreLevelObject(new COgreLevelObject("cube.mesh", position));
 						cube->SetMaterial(material);
 						cube->SetScale(Ogre::Vector3(0.1,0.1,0.1));
 						cube->SetAABBSize(size);
 	return result;
 }
 
-void CXMLReader::ReadLevelObject(const ticpp::Element& node, PCustomAABBObject object) {
+void CXMLReader::ReadLevelObject(const ticpp::Element& node, POgreLevelObject object) {
 	assert(object != NULL);
 
 	ticpp::Element* position = node.FirstChildElement("position", false);
 		std::string sName = ReadString(node);
 		std::string sType = ReadString(node, "type");
 
-		PCustomAABBObject object;
+		POgreLevelObject object;
 
 		if (sType != "CustomAABB") {
 			object = templates[sType];
 			if (object != NULL) {
 				// copy template
-				object = PCustomAABBObject(new CCustomAABBObject(*(object.get())));
+				object = POgreLevelObject(new COgreLevelObject(*(object.get())));
 			} else {
 				return;
 			}
 		} else {
-			object = PCustomAABBObject(new CCustomAABBObject("", Ogre::Vector3::ZERO));
+			object = POgreLevelObject(new COgreLevelObject("", Ogre::Vector3::ZERO));
 		}
 
 		ReadLevelObject(node, object);
 		std::string sLayer = ReadString(node, "layer");
 		Ogre::Vector3 position = ReadVector(node);
 
-		PCustomAABBObject object;
+		POgreLevelObject object;
 
 		if (sType == "CustomAABB") {
 			// create new
-			object = PCustomAABBObject(new CCustomAABBObject("", Ogre::Vector3::ZERO));
+			object = POgreLevelObject(new COgreLevelObject("", Ogre::Vector3::ZERO));
 		} else if (templates[sType] != NULL) {
 			// copy template
 			object = templates[sType];
-			object = PCustomAABBObject(new CCustomAABBObject(*(object.get())));
+			object = POgreLevelObject(new COgreLevelObject(*(object.get())));
 		} else {
 			// template not found
 			return;
 	}
 }
 
-void CXMLReader::InsertObject(PCustomAABBObject obj, CLevelData::ObjectType target) {
+void CXMLReader::InsertObject(POgreLevelObject obj, CLevelData::ObjectType target) {
 	if (leveldata != NULL)
 		leveldata->InsertObject(obj, target);
 	if (pStatic != NULL && pBackground != NULL && pDynamic != NULL) {

File src/GameObjects/LevelDataReader.h

View file
  • Ignore whitespace
 
 class CXMLReader {
 public:
-	typedef std::map<std::string, PCustomAABBObject> TemplateMap;
-	typedef std::vector<PCustomAABBObject> ObjectList;
+	typedef std::map<std::string, POgreLevelObject> TemplateMap;
+	typedef std::vector<POgreLevelObject> ObjectList;
 
 	CXMLReader();
 	virtual ~CXMLReader();
 
 	TemplateMap templates;
 private:
-	void ReadLevelObject(const ticpp::Element& node, PCustomAABBObject object);
+	void ReadLevelObject(const ticpp::Element& node, POgreLevelObject object);
 	void ReadTemplate(const ticpp::Element& node);
 	void ReadObject(const ticpp::Element& node);
 	bool Read();
-	void InsertObject(PCustomAABBObject obj, CLevelData::ObjectType target);
+	void InsertObject(POgreLevelObject obj, CLevelData::ObjectType target);
 
 	Ogre::Quaternion ReadQuaternion(const ticpp::Element& node);
 	std::string ReadString(const ticpp::Element& node, const std::string& name = "name");

File src/GameObjects/LevelDataWriter.cpp

View file
  • Ignore whitespace
 	node.LinkEndChild(element);
 }
 
-void CXMLWriter::WriteObject(Element node, PCustomAABBObject obj, const std::string& layer) {
+void CXMLWriter::WriteObject(Element node, POgreLevelObject obj, const std::string& layer) {
 	Element* element = new Element("object");
 
 	element->SetAttribute("type", "CustomAABB");

File src/GameObjects/LevelDataWriter.h

View file
  • Ignore whitespace
 
 class CXMLWriter {
 public:
-	typedef std::vector<PCustomAABBObject> LevelObjectList;
+	typedef std::vector<POgreLevelObject> LevelObjectList;
 
 	CXMLWriter();
 	virtual ~CXMLWriter();
 	void WriteVector(ticpp::Element& node, const std::string& name, const Ogre::Vector3& vec);
 	void WriteAABB(ticpp::Element& node, const std::string& name, const Ogre::AxisAlignedBox& box);
 	void WriteString(ticpp::Element& node, const std::string& name, const std::string& value);
-	void WriteObject(ticpp::Element node, PCustomAABBObject obj, const std::string& layer);
+	void WriteObject(ticpp::Element node, POgreLevelObject obj, const std::string& layer);
 };
 
 typedef boost::shared_ptr<CXMLWriter> PXMLWriter;

File src/GameObjects/LevelObjects.cpp

View file
  • Ignore whitespace
 		   (r1.bottom < r2.top);
 }
 
-Ogre::Rectangle IAABBObject::GetConstraints(const Ogre::Rectangle& rect) {
+Ogre::Rectangle CLevelObject::GetBoundingRectangle() const {
+	Ogre::Rectangle rect;
+	rect.left = aabBounds.getMinimum().z;
+	rect.bottom = aabBounds.getMinimum().y;
+	rect.right = aabBounds.getMaximum().z;
+	rect.top = aabBounds.getMaximum().y;
+	return rect;
+}
+
+Game::Polygon CLevelObject::GetPolygon() const {
+	return Game::Polygon(GetBoundingRectangle());
+}
+
+Ogre::Rectangle CLevelObject::GetConstraints(const Ogre::Rectangle& rect) const {
 	Ogre::Rectangle constraints;
 
 	// map 3D bounding box to 2D Rectangle
 	return constraints;
 }
 
-CCustomAABBObject::CCustomAABBObject(std::string ogreEntityName, const Ogre::Vector3& position)
-	: IAABBObject( position - Ogre::Vector3::UNIT_SCALE*5.0, position + Ogre::Vector3::UNIT_SCALE*5.0),
+COgreLevelObject::COgreLevelObject(std::string ogreEntityName, const Ogre::Vector3& position)
+	: CLevelObject(position),
 	  sEntityName(ogreEntityName),
 	  vScale(1.0, 1.0, 1.0),
-	  vPosition(position),
 	  sMaterial("") {
-
+	this->SetAABBSize(Ogre::Vector3::UNIT_SCALE*10.0f);
 }
 
-CCustomAABBObject::~CCustomAABBObject() {
+COgreLevelObject::~COgreLevelObject() {
 }
 
-void CCustomAABBObject::Draw(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
+void COgreLevelObject::Draw(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
 	oEntity = ogreScene->createEntity(sEntityName);
 	if (sMaterial != "")
 		oEntity->setMaterialName(sMaterial);

File src/GameObjects/LevelObjects.h

View file
  • Ignore whitespace
 #ifndef LEVELOBJECTS_H_
 #define LEVELOBJECTS_H_
 
+#include "Geometry.h"
 #include "Ogre.h"
 #include "boost/smart_ptr.hpp"
 
 namespace Game {
 namespace GameObjects {
 
-/** Defines a level object with boundaries and collision */
-class ILevelObject {
+
+class CLevelObject;
+typedef boost::shared_ptr<CLevelObject> PLevelObject;
+
+/** Basic Level Object with Convex Polygon Collision */
+class CLevelObject {
 public:
-	ILevelObject() {}
-	virtual ~ILevelObject() {}
+	enum CollisionType {ctBlocking, ctNonBlocking};
+	typedef std::vector<Game::Face> FaceList;
+
+	CLevelObject() : vPosition(0.0f, 0.0f, 0.0f), collisionType(ctBlocking) {}
+	CLevelObject(const Ogre::Vector3& position) : vPosition(position), collisionType(ctBlocking) {}
+	CLevelObject(const Ogre::Vector3& vMin, const Ogre::Vector3& vMax) : vPosition(0.0f, 0.0f, 0.0f), aabBounds(vMin, vMax), collisionType(ctBlocking) {}
+	virtual ~CLevelObject() {}
 
 	/** Returns bounding box of object */
-	virtual Ogre::AxisAlignedBox GetBoundingBox() = 0;
+	virtual inline const Ogre::AxisAlignedBox& GetBoundingBox() const { return aabBounds; }
+	/** Set bounding box of object */
+	virtual inline void SetBoundingBox(const Ogre::AxisAlignedBox& box) { aabBounds = box; }
+	/** Returns 2D Projection of Bounding Box */
+	virtual Ogre::Rectangle GetBoundingRectangle() const;
 
-	/** Return constraints for given rectangle */
-	virtual Ogre::Rectangle GetConstraints(const Ogre::Rectangle& rect) = 0;
+	/** Return constraints for given rectangle (obsolete?) */
+	virtual Ogre::Rectangle GetConstraints(const Ogre::Rectangle& rect) const;
 
-	/** Draw object in ogre scene */
-	virtual void Draw(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) = 0;
-};
-typedef boost::shared_ptr<ILevelObject> PLevelObject;
+	/** Returns Collision Polygon */
+	virtual Game::Polygon GetPolygon() const;
+	/** Returns Bounding Box as Polygon */
+	inline Game::Polygon GetBoundingBoxPolygon() const { return Game::Polygon(this->GetBoundingRectangle()); }
 
+	/** Return true if object is of type COgreLevelObject*/
+	virtual bool IsOgreObject() { return false; }
 
-/** ILevelObject with axis-aligned bounding box collision */
-class IAABBObject : public ILevelObject {
-public:
-	IAABBObject(const Ogre::Vector3& vMin, const Ogre::Vector3& vMax) : aabBounds(vMin, vMax) {}
-	virtual ~IAABBObject() {}
+	virtual CollisionType GetCollisionType() const { return collisionType; };
+	virtual void SetCollisionType(CollisionType collisionType) { this->collisionType = collisionType; };
 
-	virtual inline Ogre::AxisAlignedBox GetBoundingBox() { return aabBounds; }
-	virtual inline void SetBoundingBox(const Ogre::AxisAlignedBox& box) { aabBounds = box; }
+	/** Called when another object collides
+	@param sender Origin of collision
+	@param direction Direction of collision
+	@return Type of Collision
+	@remarks Overwrite return value to get dynamic bahviour; returns this->collisionType as default */
+	virtual CollisionType HandleCollision(PLevelObject sender, const Ogre::Vector2& direction) { return collisionType; }
 
-	virtual Ogre::Rectangle GetConstraints(const Ogre::Rectangle& rect);
+	/** Set Position (also updates bounding box) */
+	inline void SetPosition(const Ogre::Vector3& position) {
+		if (vPosition != position) {
+			Ogre::Vector3 translate = position - vPosition;
+			if (!aabBounds.isNull())
+				aabBounds.setExtents(aabBounds.getMinimum()+translate, aabBounds.getMaximum()+translate);
+			vPosition = position;
+		}
+	}
+	/** Returns Center Position */
+	inline const Ogre::Vector3& GetPosition() const { return vPosition; }
+
+	/** Set AABB Size (symmetric to center) */
+	inline void SetAABBSize(const Ogre::Vector3& size) { aabBounds.setExtents(vPosition - 0.5f*size, vPosition + 0.5f*size); }
+
 protected:
+	/** Bounding Box */
 	Ogre::AxisAlignedBox aabBounds;
+	/** Position of Center */
+	Ogre::Vector3 vPosition;
+	/** Define if Collision is blocking */
+	CollisionType collisionType;
 };
 
 
-/** Custom ogre entity with AABB collision */
-class CCustomAABBObject : public IAABBObject {
+
+/** Level Object bound to Ogre Entity */
+class COgreLevelObject : public CLevelObject {
 public:
 	/** Create Object from ogre entity name
 	@param ogreEntityName ogre entity name
 	@param position Position in world coordinates
 	@remarks Initializes to 10x10x10 bounding box*/
-	CCustomAABBObject(std::string ogreEntityName, const Ogre::Vector3& position);
-	virtual ~CCustomAABBObject();
+	COgreLevelObject(std::string ogreEntityName, const Ogre::Vector3& position);
+	virtual ~COgreLevelObject();
 
 	/** Set ogre entity name */
 	inline void SetEntityName(const std::string& name) { sEntityName = name; }
-	inline const std::string& GetEntityName() { return sEntityName; }
+	inline const std::string& GetEntityName() const { return sEntityName; }
 
 	/** Set scale factor */
 	inline void SetScale(const Ogre::Vector3& scale) { vScale = scale; }
-	inline Ogre::Vector3 GetScale() { return vScale; }
-
-	/** Set Position (also updates bounding box) */
-	inline void SetPosition(const Ogre::Vector3& position) {
-		Ogre::Vector3 translate = position - vPosition;
-		aabBounds.setExtents(aabBounds.getMinimum()+translate, aabBounds.getMaximum()+translate);
-		vPosition = position;
-	}
-	inline Ogre::Vector3 GetPosition() { return vPosition; }
+	inline const Ogre::Vector3& GetScale() const { return vScale; }
 
 	/** Set Material */
 	inline void SetMaterial(const std::string& material) { sMaterial = material; }
-	inline std::string GetMaterial() { return sMaterial; }
+	inline const std::string& GetMaterial() const { return sMaterial; }
 
 	/** Set Orientation */
 	inline void SetOrientation(const Ogre::Quaternion& orientation) { qOrientation = orientation; }
-	inline const Ogre::Quaternion& GetOrientation() { return qOrientation; }
-
-
-	/** Set AABB Size (symmetric) */
-	inline void SetAABBSize(const Ogre::Vector3& size) { aabBounds.setExtents(vPosition - 0.5f*size, vPosition + 0.5f*size); }
+	inline const Ogre::Quaternion& GetOrientation() const { return qOrientation; }
 
 	inline Ogre::Entity* GetOgreEntity() { return oEntity; }
 	inline Ogre::SceneNode* GetOgreNode() { return oNode; }
 
+	virtual bool IsOgreObject() { return true; }
+
 	virtual void Draw(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode);
 protected:
+	/** ogre entity name*/
 	std::string sEntityName;
+	/** scale factor of ogre entity */
 	Ogre::Vector3 vScale;
-	Ogre::Vector3 vPosition;
+	/** material for ogre entity */
 	std::string sMaterial;
+
+	/** Pointer to actual ogre entity*/
 	Ogre::Entity* oEntity;
+	/** Pointer to ogre node */
 	Ogre::SceneNode* oNode;
+
+	/** orientation of ogre entity */
 	Ogre::Quaternion qOrientation;
 };
-typedef boost::shared_ptr<CCustomAABBObject> PCustomAABBObject;
+typedef boost::shared_ptr<COgreLevelObject> POgreLevelObject;
 
 
 

File src/GameObjects/Messaging.h

View file
  • Ignore whitespace
+/*
+ * Messaging.h
+ *
+ *  Created on: Sep 26, 2010
+ *      Author: crt
+ */
+
+#ifndef MESSAGING_H_
+#define MESSAGING_H_
+
+#include "boost/smart_ptr.hpp"
+#include "boost/any.hpp"
+
+namespace Game {
+namespace Gameobjects {
+
+class IMessagingObject;
+typedef boost::shared_ptr<IMessagingObject> PMessagingObject;
+
+/** Object containing Message Information */
+struct SMessage {
+	enum MessageType {mtKill, mtGetItem};
+	PMessagingObject Sender;
+	PMessagingObject Target;
+	std::vector<boost::any> Parameters;
+};
+
+/** Interface for objects which can receive messages*/
+class IMessagingObject {
+public:
+	IMessagingObject() {}
+	virtual ~IMessagingObject() {}
+
+	virtual void ReceiveMessage(SMessage message) = 0;
+};
+
+}
+}
+
+#endif /* MESSAGING_H_ */

File src/States/CEditorState.h

View file
  • Ignore whitespace
 	}
 
 private:
-	typedef GameObjects::CCustomAABBObject LevelObject;
-	typedef GameObjects::PCustomAABBObject LevelObjectPtr;
+	typedef GameObjects::COgreLevelObject LevelObject;
+	typedef GameObjects::POgreLevelObject LevelObjectPtr;
 	typedef GameObjects::CLevelData::ObjectType ObjectLayer;
 	static const CEditorState::ObjectLayer otStatic  = GameObjects::CLevelData::otStatic;
 	static const CEditorState::ObjectLayer otDynamic = GameObjects::CLevelData::otDynamic;

File src/States/CGameState.cpp

View file
  • Ignore whitespace
 namespace Game {
 
 CGameState::CGameState()
-	: lTimeShift(0), lTimePaused(0), bRotateCamera(false), fSkyRotation(-90), fAcceleration(1.0)
+	: lTimeShift(0), lTimePaused(0), bRotateCamera(false), fSkyRotation(-90), fAcceleration(1.0), bSkipOneFrame(false)
 {
 }
 
 	// Fast-forward the rain so it looks more natural
 	pSys->fastForward(10);
 
+	bSkipOneFrame = true;
 	return true;
 }
 
 }
 
 void CGameState::Update() {
+	if (bSkipOneFrame) {
+		bSkipOneFrame = false;