Commits

crt32  committed 9d220fa

moved collision logic into level object model; split levelobjects files

  • Participants
  • Parent commits 918a26d

Comments (0)

Files changed (17)

File src/CMakeLists.txt

 	GameObjects/LevelDataReader.h
 	GameObjects/LevelDataWriter.h
 	GameObjects/LevelObjects.h
+	GameObjects/OgreViews.h
+	GameObjects/LevelObjectManager.h
+	GameObjects/ObjectType.h
 	GameObjects/Geometry.h
 	Core/Singleton.h
 	Core/CLog.h
 	GameObjects/LevelDataReader.cpp
 	GameObjects/LevelDataWriter.cpp
 	GameObjects/LevelObjects.cpp
+	GameObjects/OgreViews.cpp
 	GameObjects/Geometry.cpp
 	main.cpp	
 )

File src/GameObjects/CCharacter.cpp

 		boundingRect.SetExtends(position.z - bounds.left, position.y - bounds.bottom,
 				position.z + bounds.right, position.y + bounds.top);
 
-		// make temporary levelobject
-		GameObjects::PLevelObject temp(new GameObjects::CLevelObject(Ogre::Vector2(GetPosition().z, GetPosition().y)));
+		// make temporary levelobject (workaround)
+		GameObjects::PMovableObject temp = CMovableObject::Create();
+		temp->SetPosition(Ogre::Vector2(position.z, position.y));
 		temp->SetBoundingBox(boundingRect);
+		temp->SetParent(goLevel);
 
 		translation = Ogre::Vector2(deltaTime * fSpeed, deltaTime * fVerticalVelocity);
-		translation = goLevel->ResolveCollision(temp, translation);
+		temp->Move(translation);
+
+		translation = temp->GetPosition() - Ogre::Vector2(position.z, position.y);
 
 		ogreBodyNode->translate(0.0f, translation.y, translation.x, Ogre::Node::TS_LOCAL);
 		CheckFloatZero(translation.x); CheckFloatZero(translation.y);

File src/GameObjects/CCharacter.h

 #ifndef CCHARACTER_H_
 #define CCHARACTER_H_
 
+#include "LevelObjects.h"
 #include "CLevelData.h"
 
 #include "Ogre.h"

File src/GameObjects/CLevelData.cpp

 	return node;
 }
 
+typedef ILevelObjectManager::LevelObjectList LevelObjectList;
 
-CLevelData::LevelObjectList CLevelData::GetObjectRectangle(const Game::Rectangle& rect, LevelObjectList& list) {
+ILevelObjectManager::LevelObjectList CLevelData::GetObjectRectangle(const Game::Rectangle& rect, ILevelObjectManager::LevelObjectList& list) {
 	LevelObjectList results;
 	for (LevelObjectList::iterator it = list.begin(); it != list.end(); ++it) {
 		const PLevelObject& obj = *it;
 	return results;
 }
 
-CLevelData::LevelObjectList CLevelData::GetObjectRectangle(const Game::Rectangle& rect) {
+ILevelObjectManager::LevelObjectList CLevelData::GetObjectRectangle(const Game::Rectangle& rect) {
 	// TODO: use space partitioning here
 	LevelObjectList results = GetObjectRectangle(rect, this->staticLevelData);
 	LevelObjectList temp = GetObjectRectangle(rect, this->dynamicLevelData);
 	return results;
 }
 
-Game::Polygon MakePolygon(const Game::Rectangle& rect, const Ogre::Vector2& translation) {
-	Game::Polygon polygon;
-	if (translation.x > 0 && translation.y > 0) { // top right
-		polygon.AddPoint(rect.TopLeft());
-		polygon.AddPoint(rect.TopLeft() + translation);
-		polygon.AddPoint(rect.TopRight() + translation);
-		polygon.AddPoint(rect.BottomRight() + translation);
-		polygon.AddPoint(rect.BottomRight());
-		polygon.AddPoint(rect.BottomLeft());
-	} else if (translation.x > 0 && translation.y == 0) { // right
-		polygon.AddPoint(rect.TopLeft());
-		polygon.AddPoint(rect.TopRight() + Ogre::Vector2(translation.x, 0.0f));
-		polygon.AddPoint(rect.BottomRight() + Ogre::Vector2(translation.x, 0.0f));
-		polygon.AddPoint(rect.BottomLeft());
-	} else if (translation.x > 0 && translation.y < 0) { // bottom right
-		polygon.AddPoint(rect.TopLeft());
-		polygon.AddPoint(rect.TopRight());
-		polygon.AddPoint(rect.TopRight() + translation);
-		polygon.AddPoint(rect.BottomRight() + translation);
-		polygon.AddPoint(rect.BottomLeft() + translation);
-		polygon.AddPoint(rect.BottomLeft());
-	} else if (translation.x == 0 && translation.y > 0) { // top
-		polygon.AddPoint(rect.TopLeft() + Ogre::Vector2(0.0f, translation.y));
-		polygon.AddPoint(rect.TopRight() + Ogre::Vector2(0.0f, translation.y));
-		polygon.AddPoint(rect.BottomRight());
-		polygon.AddPoint(rect.BottomLeft());
-	} else if (translation.x == 0 && translation.y < 0) { // bottom
-		polygon.AddPoint(rect.TopLeft());
-		polygon.AddPoint(rect.TopRight());
-		polygon.AddPoint(rect.BottomRight() + Ogre::Vector2(0.0f, translation.y));
-		polygon.AddPoint(rect.BottomLeft() + Ogre::Vector2(0.0f, translation.y));
-	} else if (translation.x < 0 && translation.y > 0) { // top left
-		polygon.AddPoint(rect.TopLeft() + translation);
-		polygon.AddPoint(rect.TopRight() + translation);
-		polygon.AddPoint(rect.TopRight());
-		polygon.AddPoint(rect.BottomRight());
-		polygon.AddPoint(rect.BottomLeft());
-		polygon.AddPoint(rect.BottomLeft() + translation);
-	} else if (translation.x < 0 && translation.y == 0) { // Left()
-		polygon.AddPoint(rect.TopLeft() + Ogre::Vector2(translation.x, 0.0f));
-		polygon.AddPoint(rect.TopRight());
-		polygon.AddPoint(rect.BottomRight());
-		polygon.AddPoint(rect.BottomLeft() + Ogre::Vector2(translation.x, 0.0f));
-	} else if (translation.x < 0 && translation.y < 0) { // Bottom() Left()
-		polygon.AddPoint(rect.TopLeft() + translation);
-		polygon.AddPoint(rect.TopLeft());
-		polygon.AddPoint(rect.TopRight());
-		polygon.AddPoint(rect.BottomRight());
-		polygon.AddPoint(rect.BottomRight() + translation);
-		polygon.AddPoint(rect.BottomLeft() + translation);
-	}
-	return polygon;
 }
-
-float GetTranslationFactorFromDirection(const Ogre::Vector2& translation, const Ogre::Vector2& direction) {
-	if (approximatelyEqual(direction.y, 0.0f))
-		return translation.x / direction.x;
-	else if (approximatelyEqual(direction.x, 0.0f))
-		return translation.y / direction.y;
-	else {
-		if (fabs(translation.x) > fabs(translation.y))
-			return translation.x / direction.x;
-		else
-			return translation.y / direction.y;
-	}
 }
-
-Ogre::Vector2 CLevelData::ResolveCollision(PLevelObject object, const Ogre::Vector2& translation) {
-
-	// TODO: Make sure that length of resulting translation does not exceed the lenght of the translation
-	// TODO: Possible infinite loop if object is intersecting with two objects at the same time
-
-	bool bResolveFinished = false;
-
-	// get Rectangle and corresponding Polygon once; change manually if needed
-	Game::Rectangle rect = object->GetBoundingBox();
-	Game::Polygon objectPolygon = object->GetBoundingBoxPolygon();
-
-	Ogre::Vector2 currentTranslation = translation; // translation we are performing currently
-	Ogre::Vector2 result(0,0); 	// all performed translations until any given point
-
-	float maxLength = translation.length(); // maximum allowed transformation length
-
-	// stores translation vector for each face we already moved along
-	std::map<Game::Face, Ogre::Vector2> visitedFaces;
-
-	LevelObjectList ignoreObjects; // ignored objects - objects which already returned non-blocking collision
-
-	while (!bResolveFinished) {
-
-		// check if we move with zero length
-		if ( (approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f))
-			 || approximatelyEqual(maxLength, 0.0f) || (maxLength <= 0.0f))  {
-			bResolveFinished = true;
-			continue;
-		}
-
-		// limit current translation by maxlength
-		float currentLength = currentTranslation.length();
-		if (currentLength > maxLength) {
-			currentTranslation = (maxLength / currentLength) * currentTranslation;
-		}
-
-		// check again if we are now close to zero
-		if (approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f)) {
-			bResolveFinished = true;
-			continue;
-		}
-
-		TEST_LOG("ResolveCollision: (" << rect << ") " << currentTranslation);
-
-		// I. get all objects inside the smallest rectangle containing rect and rect+translation
-
-		// expand bounding rect by current translation
-		Game::Rectangle broadRect( rect );
-		if (currentTranslation.x > 0)
-			broadRect.SetRight( rect.Right() + currentTranslation.x );
-		else
-			broadRect.SetLeft( rect.Left() + currentTranslation.x );
-		if (currentTranslation.y > 0)
-			broadRect.SetTop( rect.Top() + currentTranslation.y );
-		else
-			broadRect.SetBottom( rect.Bottom() + currentTranslation.y );
-
-		LevelObjectList objects = GetObjectRectangle(broadRect); // objects we could collide with
-
-		// if no objects are in range, we will not collide
-		if (objects.empty()) {
-			result += currentTranslation;
-			maxLength -= currentTranslation.length();
-			currentTranslation = translation - result;
-			TEST_LOG("objects.empty()");
-			continue;
-		}
-
-		// II. get objects which actually intersect with the parallelogram described
-		//     by rect and translation (separating axis test)
-		//	   (also check for collision)
-
-		Game::Polygon movementParallelogram = MakePolygon(rect, currentTranslation);
-
-		LevelObjectList collisionObjects; // objects we could collide with
-		LevelObjectList intersectingObjects; // objects we currently are colliding with
-
-		// do more accurate intersection test for objects that are in range
-		for (LevelObjectList::iterator it = objects.begin(); it != objects.end(); ++it) {
-			const PLevelObject& obj = *it;
-			// dont check non-blocking objects twice
-			if (std::find(ignoreObjects.begin(), ignoreObjects.end(), obj) != ignoreObjects.end())
-				continue;
-
-			Game::Rectangle boundingRect = obj->GetBoundingBox();
-			Game::Polygon objPolygon = obj->GetCollisionPolygon();
-
-			// check for intersection with object
-			if (rect.Intersects(boundingRect)) {
-				std::pair<bool, Ogre::Vector2> result = objPolygon.SeparatingAxesTest(objectPolygon);
-				if (!result.first && !approximatelyEqual(result.second.length(), 0.0f)) {
-					if (obj->HandleCollision(object, currentTranslation) == CLevelObject::ctBlocking) {
-						intersectingObjects.push_back(obj);
-					} else {
-						ignoreObjects.push_back(obj);
-					}
-				}
-			}
-
-			// skip if we already have intersection
-			if (!intersectingObjects.empty())
-				continue;
-
-			// test separating axes with movement parallelogram
-			std::pair<bool, Ogre::Vector2> result = objPolygon.SeparatingAxesTest(movementParallelogram);
-			if (!result.first && !approximatelyEqual(result.second.length(), 0.0f)) {
-				collisionObjects.push_back(obj);
-			}
-		}
-
-		// in case of collision, resolve:
-		/* - Find minimum translation vector
-		 * - translate -> collision is resolved
-		 * - try again to move along translation; this time hopefully without collision
-		 */
-		if (!intersectingObjects.empty()) {
-			TEST_LOG("COLLISION");
-
-			const PLevelObject& obj = intersectingObjects[0];
-			Game::Polygon objPoly = obj->GetCollisionPolygon();
-
-			// get minimum translation vector
-			std::pair<bool, Ogre::Vector2> SATresult = objectPolygon.SeparatingAxesTest(objPoly);
-			Ogre::Vector2 translationResolve = SATresult.second;
-
-			float translationLength = translationResolve.length();
-
-			// always use MTV if it is _very_ short (usually a rounding error)
-			/*if (translationLength > 0.01f) {
-				// find solution in opposite moving direction
-				Ogre::Vector2 currentTranslationDir = currentTranslation.normalisedCopy();
-				Game::SATResult SATres = objectPolygon.IsSeparatingAxis(objPoly, Ogre::Vector2(currentTranslationDir.y, -currentTranslationDir.x));
-				if (SATres.MinimumOverlap < 0.0f)
-					translationResolve = SATres.MinimumOverlap * currentTranslationDir;
-				else
-					translationResolve = SATres.MaximumOverlap * currentTranslationDir;
-				translationLength = translationResolve.length();
-			}*/
-
-			if (approximatelyEqual(translationLength, 0.0f)) {
-				translationResolve.x = copysign(TOLERANCE_ABS, translationResolve.x);
-				translationResolve.y = copysign(TOLERANCE_ABS, translationResolve.y);
-			}
-
-			translationResolve *= (1.0f + TOLERANCE);
-
-
-
-			/*
-			// now translationResolve will resolve the collision
-			// perform and repeat translation
-			*/
-
-			// limit translation by maxLength
-			if (translationLength > maxLength) {
-				translationResolve = (maxLength / translationLength) * translationResolve;
-				maxLength = 0;
-			} else {
-				maxLength -= translationLength;
-			}
-
-			TEST_LOG(translationResolve);
-
-			rect.Translate(translationResolve);
-			objectPolygon.Translate(translationResolve);
-
-			result = result + translationResolve;
-			currentTranslation = translation - result;
-			continue;
-		}
-
-		// no objects inside parallelogram
-		if (collisionObjects.empty()) {
-			result += currentTranslation;
-			maxLength -= currentTranslation.length();
-			currentTranslation = translation - result;
-			TEST_LOG("collisionObjects.empty()");
-
-			continue;
-		}
-
-		// III. get maximum translation without collision
-		/* - get for each object inside parallelogram maximum translation vector
-		 *   along current translation (vector by which we can translate without collision)
-		 * - perform maximum translation
-		 * - check if collision is blocking; if not ignore object and repeat
-		 */
-
-		Game::Face nearestFace; // face we are colliding with
-		PLevelObject nearestObj; // object we are colliding with
-		float nearestFactor; // maximum factor that nearestFactor*currentTranslation can be performed without collision
-		bool bNotFound = true; // no collision found?
-		bool LastCollisionNonBlocking = false;
-
-		do {
-			std::vector<Game::Face> objFaces;
-			bNotFound = true;
-			LastCollisionNonBlocking = false;
-
-			for (LevelObjectList::iterator it = collisionObjects.begin(); it != collisionObjects.end(); ++it) {
-				const PLevelObject& obj = *it;
-				// ignore non-blocking objects we already processed
-				if (std::find(ignoreObjects.begin(), ignoreObjects.end(), obj) != ignoreObjects.end())
-					continue;
-
-				// check all faces for possible collision
-				objFaces = obj->GetCollisionPolygon().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;
-
-					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 && (approximatelyEqual(result.second, 0.0f) || 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");
-						}
-					}
-				}
-			}
-
-			// an object has been found - perform maximum translation
-			if (!bNotFound) {
-				// translate towards nearest object
-				Ogre::Vector2 maxTranslation = nearestFactor*currentTranslation*(1.0f - TOLERANCE);
-
-				// limit by maxLength
-				float maxTranslationLength = maxTranslation.length();
-				if (maxTranslationLength > maxLength) {
-					maxTranslation = (maxLength/maxTranslationLength) * maxTranslation;
-					maxLength = 0.0f;
-				} else {
-					maxLength -= maxTranslationLength;
-				}
-
-				result += maxTranslation;
-				objectPolygon.Translate(maxTranslation);
-				rect.Translate(maxTranslation);
-				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 (LastCollisionNonBlocking &&
-				 !approximatelyEqual(maxLength, 0.0f) &&
-				 !(approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f)) );
-
-		if (approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f)) {
-			// we have been able to move to our goal; continue
-			currentTranslation = translation - result;
-			TEST_LOG("currentTranslation = 0");
-			continue;
-		}
-
-		if (approximatelyEqual(maxLength, 0.0f)) {
-			// dont move any further
-			bResolveFinished = true;
-			TEST_LOG("maxLength = 0");
-			continue;
-		}
-
-		if (bNotFound) {
-			// no objects are blocking our way
-			result += currentTranslation;
-			maxLength -= currentTranslation.length();
-			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(faceDirection);
-		if (!LinearlyDependent(faceDirection, Ogre::Vector2::UNIT_X))
-			separatingAxes.push_back(Ogre::Vector2::UNIT_X);
-		if (!LinearlyDependent(faceDirection, Ogre::Vector2::UNIT_Y))
-			separatingAxes.push_back(Ogre::Vector2::UNIT_Y);
-
-		// find separating axis
-		Ogre::Vector2 separatingAxis(0.0f, 0.0f);
-
-		int separatingAxisIndex = -1;
-		float maxDp = 0.0f;
-		for (int i = 0; i < separatingAxes.size(); i++) {
-
-			SATResult satResult = objectPolygon.IsSeparatingAxis(facePoly, separatingAxes[i]);
-
-			TEST_LOG("sep axis = " << separatingAxes[i]);
-			TEST_LOG("SAT Result: " << satResult.IsSeparating << "; " << satResult.MinimumOverlap << "; " << satResult.MaximumOverlap);
-
-			if (satResult.IsSeparating || approximatelyEqual(satResult.MinimumOverlap, 0.0f)) {
-				float dp = currentTranslation.dotProduct(separatingAxes[i]);
-				if (fabs(maxDp) <= fabs(dp)) {
-					maxDp = dp;
-					separatingAxis = separatingAxes[i].normalisedCopy();
-					TEST_LOG("^ choose");
-					separatingAxisIndex = i;
-				}
-			}
-		}
-
-		TEST_LOG("remaining axes: " << separatingAxes.size());
-
-		// no axis found - cancel
-		if (separatingAxis == Ogre::Vector2::ZERO) {
-			TEST_LOG("sep.axis = 0");
-			bResolveFinished = true;
-			continue;
-		}
-
-		separatingAxes.erase(separatingAxes.begin() + separatingAxisIndex);
-
-		// make separatingAxis face in same direction as translation
-		if (separatingAxis.dotProduct(currentTranslation) < 0.0f)
-			separatingAxis = -separatingAxis;
-
-		TEST_LOG("sep.axis = " << separatingAxis);
-
-		// calculate maximum allowed translation along separatingAxis
-		float maxFactor = separatingAxis.dotProduct(translation);
-
-		// dont allow movement against translation direction
-		if (maxFactor < 0.0f || approximatelyEqual(maxFactor, 0.0f)) {
-			TEST_LOG("maxFactor <= 0  ("<< maxFactor <<")");
-			bResolveFinished = true;
-			continue;
-		}
-
-
-		// 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 && !approximatelyEqual(satResult.MinimumOverlap, 0.0f)) {
-				// get minimum translation vector in direction of currentTranslation
-				Ogre::Vector2 axisNormal = LeftHandNormal(separatingAxes[i]);
-				Ogre::Vector2 MTV = satResult.MinimumOverlap * axisNormal;
-
-				MTV *= (1.0f + TOLERANCE);
-				TEST_LOG("MTV: "<<MTV);
-
-				float factor = GetTranslationFactorFromDirection(MTV, separatingAxis);
-
-				if (factor < 0.0f) {
-					MTV = satResult.MaximumOverlap * axisNormal;
-					MTV *= (1.0f + TOLERANCE);
-					TEST_LOG("MTV: "<<MTV);
-
-					factor = GetTranslationFactorFromDirection(MTV, separatingAxis);
-				}
-
-				TEST_LOG("->factor="<<factor);
-
-				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;
-
-		TEST_LOG("minFactor="<<minFactor<<"; maxFactor="<<maxFactor);
-
-		Ogre::Vector2 nextTranslation = factor*separatingAxis;
-		if (approximatelyEqual(nextTranslation.x, currentTranslation.x) && approximatelyEqual(nextTranslation.y, currentTranslation.y)) {
-			TEST_LOG("next=current");
-			bResolveFinished = true;
-			continue;
-		} else {
-			currentTranslation = nextTranslation;
-		}
-
-		// 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.0f) {
-				// cancel
-				TEST_LOG("dotProduct < 0");
-				bResolveFinished = true;
-				continue;
-			}
-		} else {
-			visitedFaces[nearestFace] = currentTranslation;
-		}
-
-
-		TEST_LOG("currentTranslation = " << currentTranslation);
-	}
-
-	TEST_LOG("result=" << result);
-	TEST_LOG("========================"<<std::endl);
-	return result;
-}
-
-}
-}

File src/GameObjects/CLevelData.h

 #include "Geometry.h"
 #include "LevelObjects.h"
 #include "LevelDataReader.h"
+#include "LevelObjectManager.h"
 #include "../testlog.h"
 #include "../Core/CLog.h"
 
 #include "Ogre.h"
-#include <fstream>
-#include <sstream>
-#include <string>
-#include <vector>
-#include <math.h>
 
 #include "boost/smart_ptr.hpp"
 
 class CLevelData;
 typedef boost::shared_ptr<CLevelData> PLevelData;
 
-class CLevelData : public LevelDataReaderListener {
+class CLevelData : public CXMLReader::Listener, public ILevelObjectManager {
 public:
 	CLevelData();
 	virtual ~CLevelData();
 	@return newly created scene node */
 	Ogre::SceneNode* Draw(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode = NULL);
 
-	/** 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(PLevelObject object, const Ogre::Vector2& translation);
-
 	/** Removes all objects */
 	void ClearData();
 
 		return leveldataPtr;
 	}
 
-	/** Implementation of LevelDataReaderListener */
+	/** Implementation of CXMLReader::Listener */
 	virtual void ReadLevelObject(PLevelObject object, ObjectType layer) {
 		InsertObject(object, static_cast<ObjectType>(layer) );
 	}
-	/** Implementation of LevelDataReaderListener */
+	/** Implementation of CXMLReader::Listener */
 	virtual void ReadOgreEntityView(POgreEntityView view, ObjectType layer) {
 		InsertView(view, static_cast<ObjectType>(layer));
 	}
+	/** Implementation of ILevelObjectManager */
+	virtual ILevelObjectManager::LevelObjectList GetObjectRectangle(const Game::Rectangle& rect);
 private:
-	typedef std::vector<PLevelObject> LevelObjectList;
 	typedef std::vector<POgreEntityView> OgreViewList;
 
-	LevelObjectList GetObjectRectangle(const Game::Rectangle& rect, LevelObjectList& list);
-	LevelObjectList GetObjectRectangle(const Game::Rectangle& rect);
+	ILevelObjectManager::LevelObjectList GetObjectRectangle(const Game::Rectangle& rect, ILevelObjectManager::LevelObjectList& list);
 
-	LevelObjectList staticLevelData;
-	LevelObjectList backgroundLevelData;
-	LevelObjectList dynamicLevelData;
+	ILevelObjectManager::LevelObjectList staticLevelData;
+	ILevelObjectManager::LevelObjectList backgroundLevelData;
+	ILevelObjectManager::LevelObjectList dynamicLevelData;
 	OgreViewList ogreViews;
 };
 

File src/GameObjects/LevelDataReader.cpp

 	}
 }
 
-void CXMLReader::ReadTemplate(const ticpp::Element& node, LevelDataReaderListener* listener) {
+void CXMLReader::ReadTemplate(const ticpp::Element& node, Listener* listener) {
 	try {
 		std::string sName = ReadString(node);
 		std::string sType = ReadString(node, "type");
 				return;
 			}
 		} else {
-			object = PLevelObject(new CLevelObject());
+			object = CLevelObject::Create();
 			view = COgreEntityView::Create(object);
 		}
 
 	}
 }
 
-void CXMLReader::ReadObject(const ticpp::Element& node, LevelDataReaderListener* listener) {
+void CXMLReader::ReadObject(const ticpp::Element& node, Listener* listener) {
 	try {
 		//<object type="house" layer="static" x="2" y="-50" z="115" />
 		std::string sType = ReadString(node, "type");
 
 		if (sType == "CustomAABB") {
 			// create new
-			object = PLevelObject(new CLevelObject());
+			object = CLevelObject::Create();
 			view = COgreEntityView::Create(object);
 		} else {
 			// copy template
 }
 
 
-bool CXMLReader::Read(const std::string& filename, LevelDataReaderListener* listener, bool TemplatesOnly) {
+bool CXMLReader::Read(const std::string& filename, Listener* listener, bool TemplatesOnly) {
 	try {
 		ticpp::Document document(filename);
 		document.LoadFile();

File src/GameObjects/LevelDataReader.h

 #include "../Core/CLog.h"
 #include "ObjectType.h"
 #include "LevelObjects.h"
+#include "OgreViews.h"
 
 #define TIXML_USE_TICPP
 #include "ticpp.h"
 namespace Game {
 namespace GameObjects {
 
-class LevelDataReaderListener {
-public:
-	LevelDataReaderListener() { }
-	virtual ~LevelDataReaderListener() { }
-
-	virtual void ReadTemplate(std::string name, PLevelObject object, POgreEntityView view) { }
-	virtual void ReadLevelObject(PLevelObject object, ObjectType layer) { }
-	virtual void ReadOgreEntityView(POgreEntityView view, ObjectType layer) { }
-};
-
 class CXMLReader {
 public:
+	class Listener {
+	public:
+		Listener() { }
+		virtual ~Listener() { }
+
+		virtual void ReadTemplate(std::string name, PLevelObject object, POgreEntityView view) { }
+		virtual void ReadLevelObject(PLevelObject object, ObjectType layer) { }
+		virtual void ReadOgreEntityView(POgreEntityView view, ObjectType layer) { }
+	};
+
 	typedef std::pair<PLevelObject, POgreEntityView> ModelViewPair;
 	typedef std::map<std::string, ModelViewPair> TemplateMap;
 
 	CXMLReader();
 	virtual ~CXMLReader();
 
-	bool Read(const std::string& filename, LevelDataReaderListener* listener, bool TemplatesOnly = false);
+	bool Read(const std::string& filename, Listener* listener, bool TemplatesOnly = false);
 private:
 	TemplateMap templates;
 
 	void ReadLevelObject(const ticpp::Element& node, PLevelObject object, POgreEntityView view);
-	void ReadTemplate(const ticpp::Element& node, LevelDataReaderListener* listener);
-	void ReadObject(const ticpp::Element& node, LevelDataReaderListener* listener);
+	void ReadTemplate(const ticpp::Element& node, Listener* listener);
+	void ReadObject(const ticpp::Element& node, Listener* listener);
 
 	Ogre::Quaternion ReadQuaternion(const ticpp::Element& node);
 	std::string ReadString(const ticpp::Element& node, const std::string& name = "name");

File src/GameObjects/LevelDataWriter.h

 #include "../Core/CLog.h"
 #include "ObjectType.h"
 #include "LevelObjects.h"
+#include "OgreViews.h"
 
 #define TIXML_USE_TICPP
 #include "ticpp.h"

File src/GameObjects/LevelObjectManager.h

+/*
+ * LevelObjectManager.h
+ *
+ *  Created on: Oct 4, 2010
+ *      Author: crt
+ */
+
+#ifndef LEVELOBJECTMANAGER_H_
+#define LEVELOBJECTMANAGER_H_
+
+#include "Geometry.h"
+#include "boost/shared_ptr.hpp"
+
+namespace Game {
+namespace GameObjects {
+
+class CLevelObject;
+
+class ILevelObjectManager {
+public:
+	typedef std::vector<boost::shared_ptr<CLevelObject> > LevelObjectList;
+
+	ILevelObjectManager() { }
+	virtual ~ILevelObjectManager() { }
+
+	/** Return all Objects inside given Rectangle */
+	virtual LevelObjectList GetObjectRectangle(const Game::Rectangle& rect) { }
+
+	/** Called by a level object when it changes its position or boundaries */
+	virtual void ObjectPositionChanged(boost::shared_ptr<CLevelObject> object) { }
+};
+
+typedef boost::shared_ptr<ILevelObjectManager> PLevelObjectManager;
+typedef boost::weak_ptr<ILevelObjectManager> WLevelObjectManager;
+
+}
+}
+
+#endif /* LEVELOBJECTMANAGER_H_ */

File src/GameObjects/LevelObjects.cpp

 	BoundingBoxCollision = obj.BoundingBoxCollision;
 }
 
-PLevelObject CLevelObject::Copy() {
+PLevelObject CLevelObject::Create() {
+	CLevelObject* obj = new CLevelObject();
+	PLevelObject objPtr(obj);
+	obj->self = objPtr;
+	return objPtr;
+}
+
+PLevelObject CLevelObject::Copy() const {
 	CLevelObject* copy = new CLevelObject(*this);
 	PLevelObject copyPtr(copy);
+	copy->self = copyPtr;
 	return copyPtr;
 }
 
 void CLevelObject::notifyPositionChanged() {
+	PLevelObjectManager manager = parent.lock();
+	if (manager) manager->ObjectPositionChanged(self.lock());
+
 	for (ListenerList::iterator it = Listeners.begin(); it != Listeners.end(); ++it) {
 		(*it)->PositionChanged(this);
 	}
 }
 
 void CLevelObject::notifyBoundsChanged() {
+	PLevelObjectManager manager = parent.lock();
+	if (manager) manager->ObjectPositionChanged(self.lock());
+
 	for (ListenerList::iterator it = Listeners.begin(); it != Listeners.end(); ++it) {
 		(*it)->PositionChanged(this);
 	}
 	}
 }
 
-COgreEntityView::COgreEntityView(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode)
-	: sEntityName(""),
-	  vScale(1.0, 1.0, 1.0),
-	  sMaterial(""),
-	  vPositionOffset(Ogre::Vector3::ZERO),
-	  oScene(ogreScene),
-	  oEntity(NULL),
-	  oParentNode(ogreNode),
-	  oNode(NULL),
-	  qOrientation(Ogre::Quaternion::IDENTITY),
-	  Model(model)
-{
-	assert(model != NULL);
-	UpdateGeometry();
-}
 
-POgreEntityView COgreEntityView::Create(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
-	COgreEntityView* obj = new COgreEntityView(model, ogreScene, ogreNode);
-	POgreEntityView objPtr( obj );
+PMovableObject CMovableObject::Create() {
+	CMovableObject* obj = new CMovableObject();
+	PMovableObject objPtr(obj);
 	obj->self = objPtr;
-	model->AddListener(objPtr);
 	return objPtr;
 }
 
-COgreEntityView::~COgreEntityView() {
-	PLevelObject model = Model.lock();
-	if (model)
-		model->RemoveListener(self.lock());
-}
-
-POgreEntityView COgreEntityView::Copy(PLevelObject model) {
-	COgreEntityView* copy = new COgreEntityView(model);
-	POgreEntityView copyPtr( copy );
+PLevelObject CMovableObject::Copy() const {
+	CMovableObject* copy = new CMovableObject(*this);
+	PMovableObject copyPtr(copy);
 	copy->self = copyPtr;
-	model->AddListener(copyPtr);
-	copy->qOrientation = this->qOrientation;
-	copy->sEntityName = this->sEntityName;
-	copy->sMaterial = this->sMaterial;
-	copy->vPositionOffset = this->vPositionOffset;
-	copy->vScale = this->vScale;
 	return copyPtr;
 }
 
-void COgreEntityView::UpdateGeometry() {
-	PLevelObject model = Model.lock();
-	if (oScene) {
-		if (!oParentNode) {
-			oParentNode = oScene->getRootSceneNode();
-		}
-		if (!oNode) {
-			oNode = oParentNode->createChildSceneNode(Ogre::Vector3::ZERO);
-		}
-		if (oNode) {
-			if (!oEntity && !sEntityName.empty()) {
-				oEntity = oScene->createEntity(sEntityName);
-				if (sMaterial != "")
-					oEntity->setMaterialName(sMaterial);
-				oNode->attachObject(oEntity);
-			}
-			Ogre::Vector2 modelPosition(0.0f, 0.0f);
-			if (model)
-				modelPosition = model->GetPosition();
-			oNode->setPosition(vPositionOffset + Ogre::Vector3(0.0f, modelPosition.y, modelPosition.x));
-			oNode->setScale(vScale);
-			oNode->setOrientation(qOrientation);
-		}
+Game::Polygon CMovableObject::MakePolygon(const Game::Rectangle& rect, const Ogre::Vector2& translation) {
+	Game::Polygon polygon;
+	if (translation.x > 0 && translation.y > 0) { // top right
+		polygon.AddPoint(rect.TopLeft());
+		polygon.AddPoint(rect.TopLeft() + translation);
+		polygon.AddPoint(rect.TopRight() + translation);
+		polygon.AddPoint(rect.BottomRight() + translation);
+		polygon.AddPoint(rect.BottomRight());
+		polygon.AddPoint(rect.BottomLeft());
+	} else if (translation.x > 0 && translation.y == 0) { // right
+		polygon.AddPoint(rect.TopLeft());
+		polygon.AddPoint(rect.TopRight() + Ogre::Vector2(translation.x, 0.0f));
+		polygon.AddPoint(rect.BottomRight() + Ogre::Vector2(translation.x, 0.0f));
+		polygon.AddPoint(rect.BottomLeft());
+	} else if (translation.x > 0 && translation.y < 0) { // bottom right
+		polygon.AddPoint(rect.TopLeft());
+		polygon.AddPoint(rect.TopRight());
+		polygon.AddPoint(rect.TopRight() + translation);
+		polygon.AddPoint(rect.BottomRight() + translation);
+		polygon.AddPoint(rect.BottomLeft() + translation);
+		polygon.AddPoint(rect.BottomLeft());
+	} else if (translation.x == 0 && translation.y > 0) { // top
+		polygon.AddPoint(rect.TopLeft() + Ogre::Vector2(0.0f, translation.y));
+		polygon.AddPoint(rect.TopRight() + Ogre::Vector2(0.0f, translation.y));
+		polygon.AddPoint(rect.BottomRight());
+		polygon.AddPoint(rect.BottomLeft());
+	} else if (translation.x == 0 && translation.y < 0) { // bottom
+		polygon.AddPoint(rect.TopLeft());
+		polygon.AddPoint(rect.TopRight());
+		polygon.AddPoint(rect.BottomRight() + Ogre::Vector2(0.0f, translation.y));
+		polygon.AddPoint(rect.BottomLeft() + Ogre::Vector2(0.0f, translation.y));
+	} else if (translation.x < 0 && translation.y > 0) { // top left
+		polygon.AddPoint(rect.TopLeft() + translation);
+		polygon.AddPoint(rect.TopRight() + translation);
+		polygon.AddPoint(rect.TopRight());
+		polygon.AddPoint(rect.BottomRight());
+		polygon.AddPoint(rect.BottomLeft());
+		polygon.AddPoint(rect.BottomLeft() + translation);
+	} else if (translation.x < 0 && translation.y == 0) { // Left()
+		polygon.AddPoint(rect.TopLeft() + Ogre::Vector2(translation.x, 0.0f));
+		polygon.AddPoint(rect.TopRight());
+		polygon.AddPoint(rect.BottomRight());
+		polygon.AddPoint(rect.BottomLeft() + Ogre::Vector2(translation.x, 0.0f));
+	} else if (translation.x < 0 && translation.y < 0) { // Bottom() Left()
+		polygon.AddPoint(rect.TopLeft() + translation);
+		polygon.AddPoint(rect.TopLeft());
+		polygon.AddPoint(rect.TopRight());
+		polygon.AddPoint(rect.BottomRight());
+		polygon.AddPoint(rect.BottomRight() + translation);
+		polygon.AddPoint(rect.BottomLeft() + translation);
+	}
+	return polygon;
+}
+
+float CMovableObject::GetTranslationFactorFromDirection(const Ogre::Vector2& translation, const Ogre::Vector2& direction) {
+	if (approximatelyEqual(direction.y, 0.0f))
+		return translation.x / direction.x;
+	else if (approximatelyEqual(direction.x, 0.0f))
+		return translation.y / direction.y;
+	else {
+		if (fabs(translation.x) > fabs(translation.y))
+			return translation.x / direction.x;
+		else
+			return translation.y / direction.y;
 	}
 }
 
-void COgreEntityView::SetOgreScene(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
-	oScene = ogreScene;
-	if (oParentNode != ogreNode) {
-		oParentNode = ogreNode;
-		oNode = NULL; oEntity = NULL;
+void CMovableObject::Move(const Ogre::Vector2& translation) {
+	PLevelObject object = self.lock();
+	PLevelObjectManager manager = parent.lock();
+	if (!object || !manager) {
+		this->SetPosition(this->GetPosition() + translation);
+		return;
 	}
-	if (ogreScene && !oParentNode)
-		oParentNode = ogreScene->getRootSceneNode();
-	UpdateGeometry();
+	Ogre::Vector2 resolve = ResolveCollision(object, translation);
+	if (!std::isnan(resolve.x) && !std::isnan(resolve.y) && !std::isinf(resolve.x) && !std::isinf(resolve.y))
+		this->SetPosition(this->GetPosition() + resolve);
 }
 
+typedef ILevelObjectManager::LevelObjectList LevelObjectList;
 
-void COgreEntityView::PositionChanged(CLevelObject*) {
-	UpdateGeometry();
-}
+Ogre::Vector2 CMovableObject::ResolveCollision(PLevelObject object, const Ogre::Vector2& translation) {
+	PLevelObjectManager manager = parent.lock();
+	if (!manager)
+		return translation;
 
-void COgreEntityView::SetPositionOffset(const Ogre::Vector3& offset) {
-	if (vPositionOffset != offset) {
-		vPositionOffset = offset;
-		UpdateGeometry();
-	}
-}
 
-void COgreEntityView::SetEntityName(const std::string& name) {
-	if (sEntityName != name) {
-		if (oEntity) {
-			oNode->detachObject(oEntity);
-			oScene->destroyEntity(oEntity);
-		}
-		oEntity = NULL;
-		sEntityName = name;
-		UpdateGeometry();
-	}
-}
+	// TODO: Make sure that length of resulting translation does not exceed the lenght of the translation
+	// TODO: Possible infinite loop if object is intersecting with two objects at the same time
 
-void COgreEntityView::SetScale(const Ogre::Vector3& scale) {
-	vScale = scale;
-	UpdateGeometry();
-}
+	bool bResolveFinished = false;
 
-void COgreEntityView::SetMaterial(const std::string& material) {
-	if (sMaterial != material) {
-		sMaterial = material;
-		if (oEntity && sMaterial != "") {
-			oEntity->setMaterialName(sMaterial);
-		}
-		UpdateGeometry();
-	}
-}
+	// get Rectangle and corresponding Polygon once; change manually if needed
+	Game::Rectangle rect = object->GetBoundingBox();
+	Game::Polygon objectPolygon = object->GetBoundingBoxPolygon();
 
-void COgreEntityView::SetOrientation(const Ogre::Quaternion& orientation) {
-	qOrientation = orientation;
-	UpdateGeometry();
-}
+	Ogre::Vector2 currentTranslation = translation; // translation we are performing currently
+	Ogre::Vector2 result(0,0); 	// all performed translations until any given point
 
+	float maxLength = translation.length(); // maximum allowed transformation length
 
-COgreBoundsView::COgreBoundsView(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode)
-	: COgreEntityView(model, ogreScene, ogreNode)
-{
-	this->sEntityName = "cube.mesh";
-	UpdateGeometry();
-}
+	// stores translation vector for each face we already moved along
+	std::map<Game::Face, Ogre::Vector2> visitedFaces;
 
-POgreBoundsView COgreBoundsView::Create(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
-	COgreBoundsView* obj = new COgreBoundsView(model, ogreScene, ogreNode);
-	POgreBoundsView objPtr( obj );
-	obj->self = objPtr;
-	model->AddListener(objPtr);
-	return objPtr;
-}
+	LevelObjectList ignoreObjects; // ignored objects - objects which already returned non-blocking collision
 
-POgreEntityView COgreBoundsView::Copy(PLevelObject model) {
-	COgreBoundsView* copy = new COgreBoundsView(model);
-	POgreBoundsView copyPtr( copy );
-	copy->self = copyPtr;
-	model->AddListener(copyPtr);
-	copy->qOrientation = this->qOrientation;
-	copy->sEntityName = this->sEntityName;
-	copy->sMaterial = this->sMaterial;
-	copy->vPositionOffset = this->vPositionOffset;
-	copy->vScale = this->vScale;
-	return copyPtr;
-}
+	while (!bResolveFinished) {
 
-void COgreBoundsView::BoundsChanged(CLevelObject*) {
-	UpdateGeometry();
-}
-
-void COgreBoundsView::UpdateGeometry() {
-	PLevelObject model = Model.lock();
-	float angle = 0.0f;
-	if (model) {
-		Game::Rectangle bounds = model->GetBoundingBox();
-		Ogre::Vector2 center = 0.5f*(bounds.GetMaximum() + bounds.GetMinimum());
-		Ogre::Vector2 scale2d =  bounds.GetMaximum() - bounds.GetMinimum();
-
-		std::vector<Ogre::Vector2> poly = model->GetCleanCollisionPolygon().GetPoints();
-		// assume it's a oriented bounding box
-		if (poly.size() == 4) {
-			center = model->GetCleanCollisionPolygon().CalculateCenter();
-			scale2d = poly[2] - poly[0];
-			angle = model->GetRotation();
+		// check if we move with zero length
+		if ( (approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f))
+			 || approximatelyEqual(maxLength, 0.0f) || (maxLength <= 0.0f))  {
+			bResolveFinished = true;
+			continue;
 		}
 
-		// cube.mesh is 100x100x100
-		this->vScale.x = 1.0f * 0.01f;
-		this->vScale.y = scale2d.y * 0.01f;
-		this->vScale.z = scale2d.x * 0.01f;
+		// limit current translation by maxlength
+		float currentLength = currentTranslation.length();
+		if (currentLength > maxLength) {
+			currentTranslation = (maxLength / currentLength) * currentTranslation;
+		}
 
-		this->vPositionOffset = Ogre::Vector3(0.0f, center.y, center.x);
+		// check again if we are now close to zero
+		if (approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f)) {
+			bResolveFinished = true;
+			continue;
+		}
+
+		TEST_LOG("ResolveCollision: (" << rect << ") " << currentTranslation);
+
+		// I. get all objects inside the smallest rectangle containing rect and rect+translation
+
+		// expand bounding rect by current translation
+		Game::Rectangle broadRect( rect );
+		if (currentTranslation.x > 0)
+			broadRect.SetRight( rect.Right() + currentTranslation.x );
+		else
+			broadRect.SetLeft( rect.Left() + currentTranslation.x );
+		if (currentTranslation.y > 0)
+			broadRect.SetTop( rect.Top() + currentTranslation.y );
+		else
+			broadRect.SetBottom( rect.Bottom() + currentTranslation.y );
+
+		LevelObjectList objects = manager->GetObjectRectangle(broadRect); // objects we could collide with
+
+		// if no objects are in range, we will not collide
+		if (objects.empty()) {
+			result += currentTranslation;
+			maxLength -= currentTranslation.length();
+			currentTranslation = translation - result;
+			TEST_LOG("objects.empty()");
+			continue;
+		}
+
+		// II. get objects which actually intersect with the parallelogram described
+		//     by rect and translation (separating axis test)
+		//	   (also check for collision)
+
+		Game::Polygon movementParallelogram = MakePolygon(rect, currentTranslation);
+
+		LevelObjectList collisionObjects; // objects we could collide with
+		LevelObjectList intersectingObjects; // objects we currently are colliding with
+
+		// do more accurate intersection test for objects that are in range
+		for (LevelObjectList::iterator it = objects.begin(); it != objects.end(); ++it) {
+			const PLevelObject& obj = *it;
+			// dont check non-blocking objects twice
+			if (std::find(ignoreObjects.begin(), ignoreObjects.end(), obj) != ignoreObjects.end())
+				continue;
+
+			Game::Rectangle boundingRect = obj->GetBoundingBox();
+			Game::Polygon objPolygon = obj->GetCollisionPolygon();
+
+			// check for intersection with object
+			if (rect.Intersects(boundingRect)) {
+				std::pair<bool, Ogre::Vector2> result = objPolygon.SeparatingAxesTest(objectPolygon);
+				if (!result.first && !approximatelyEqual(result.second.length(), 0.0f)) {
+					if (obj->HandleCollision(object, currentTranslation) == CLevelObject::ctBlocking) {
+						intersectingObjects.push_back(obj);
+					} else {
+						ignoreObjects.push_back(obj);
+					}
+				}
+			}
+
+			// skip if we already have intersection
+			if (!intersectingObjects.empty())
+				continue;
+
+			// test separating axes with movement parallelogram
+			std::pair<bool, Ogre::Vector2> result = objPolygon.SeparatingAxesTest(movementParallelogram);
+			if (!result.first && !approximatelyEqual(result.second.length(), 0.0f)) {
+				collisionObjects.push_back(obj);
+			}
+		}
+
+		// in case of collision, resolve:
+		/* - Find minimum translation vector
+		 * - translate -> collision is resolved
+		 * - try again to move along translation; this time hopefully without collision
+		 */
+		if (!intersectingObjects.empty()) {
+			TEST_LOG("COLLISION");
+
+			const PLevelObject& obj = intersectingObjects[0];
+			Game::Polygon objPoly = obj->GetCollisionPolygon();
+
+			// get minimum translation vector
+			std::pair<bool, Ogre::Vector2> SATresult = objectPolygon.SeparatingAxesTest(objPoly);
+			Ogre::Vector2 translationResolve = SATresult.second;
+
+			float translationLength = translationResolve.length();
+
+			// always use MTV if it is _very_ short (usually a rounding error)
+			/*if (translationLength > 0.01f) {
+				// find solution in opposite moving direction
+				Ogre::Vector2 currentTranslationDir = currentTranslation.normalisedCopy();
+				Game::SATResult SATres = objectPolygon.IsSeparatingAxis(objPoly, Ogre::Vector2(currentTranslationDir.y, -currentTranslationDir.x));
+				if (SATres.MinimumOverlap < 0.0f)
+					translationResolve = SATres.MinimumOverlap * currentTranslationDir;
+				else
+					translationResolve = SATres.MaximumOverlap * currentTranslationDir;
+				translationLength = translationResolve.length();
+			}*/
+
+			if (approximatelyEqual(translationLength, 0.0f)) {
+				translationResolve.x = copysign(TOLERANCE_ABS, translationResolve.x);
+				translationResolve.y = copysign(TOLERANCE_ABS, translationResolve.y);
+			}
+
+			translationResolve *= (1.0f + TOLERANCE);
+
+
+
+			/*
+			// now translationResolve will resolve the collision
+			// perform and repeat translation
+			*/
+
+			// limit translation by maxLength
+			if (translationLength > maxLength) {
+				translationResolve = (maxLength / translationLength) * translationResolve;
+				maxLength = 0;
+			} else {
+				maxLength -= translationLength;
+			}
+
+			TEST_LOG(translationResolve);
+
+			rect.Translate(translationResolve);
+			objectPolygon.Translate(translationResolve);
+
+			result = result + translationResolve;
+			currentTranslation = translation - result;
+			continue;
+		}
+
+		// no objects inside parallelogram
+		if (collisionObjects.empty()) {
+			result += currentTranslation;
+			maxLength -= currentTranslation.length();
+			currentTranslation = translation - result;
+			TEST_LOG("collisionObjects.empty()");
+
+			continue;
+		}
+
+		// III. get maximum translation without collision
+		/* - get for each object inside parallelogram maximum translation vector
+		 *   along current translation (vector by which we can translate without collision)
+		 * - perform maximum translation
+		 * - check if collision is blocking; if not ignore object and repeat
+		 */
+
+		Game::Face nearestFace; // face we are colliding with
+		PLevelObject nearestObj; // object we are colliding with
+		float nearestFactor; // maximum factor that nearestFactor*currentTranslation can be performed without collision
+		bool bNotFound = true; // no collision found?
+		bool LastCollisionNonBlocking = false;
+
+		do {
+			std::vector<Game::Face> objFaces;
+			bNotFound = true;
+			LastCollisionNonBlocking = false;
+
+			for (LevelObjectList::iterator it = collisionObjects.begin(); it != collisionObjects.end(); ++it) {
+				const PLevelObject& obj = *it;
+				// ignore non-blocking objects we already processed
+				if (std::find(ignoreObjects.begin(), ignoreObjects.end(), obj) != ignoreObjects.end())
+					continue;
+
+				// check all faces for possible collision
+				objFaces = obj->GetCollisionPolygon().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;
+
+					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 && (approximatelyEqual(result.second, 0.0f) || 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");
+						}
+					}
+				}
+			}
+
+			// an object has been found - perform maximum translation
+			if (!bNotFound) {
+				// translate towards nearest object
+				Ogre::Vector2 maxTranslation = nearestFactor*currentTranslation*(1.0f - TOLERANCE);
+
+				// limit by maxLength
+				float maxTranslationLength = maxTranslation.length();
+				if (maxTranslationLength > maxLength) {
+					maxTranslation = (maxLength/maxTranslationLength) * maxTranslation;
+					maxLength = 0.0f;
+				} else {
+					maxLength -= maxTranslationLength;
+				}
+
+				result += maxTranslation;
+				objectPolygon.Translate(maxTranslation);
+				rect.Translate(maxTranslation);
+				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 (LastCollisionNonBlocking &&
+				 !approximatelyEqual(maxLength, 0.0f) &&
+				 !(approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f)) );
+
+		if (approximatelyEqual(currentTranslation.x, 0.0f) && approximatelyEqual(currentTranslation.y, 0.0f)) {
+			// we have been able to move to our goal; continue
+			currentTranslation = translation - result;
+			TEST_LOG("currentTranslation = 0");
+			continue;
+		}
+
+		if (approximatelyEqual(maxLength, 0.0f)) {
+			// dont move any further
+			bResolveFinished = true;
+			TEST_LOG("maxLength = 0");
+			continue;
+		}
+
+		if (bNotFound) {
+			// no objects are blocking our way
+			result += currentTranslation;
+			maxLength -= currentTranslation.length();
+			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(faceDirection);
+		if (!LinearlyDependent(faceDirection, Ogre::Vector2::UNIT_X))
+			separatingAxes.push_back(Ogre::Vector2::UNIT_X);
+		if (!LinearlyDependent(faceDirection, Ogre::Vector2::UNIT_Y))
+			separatingAxes.push_back(Ogre::Vector2::UNIT_Y);
+
+		// find separating axis
+		Ogre::Vector2 separatingAxis(0.0f, 0.0f);
+
+		int separatingAxisIndex = -1;
+		float maxDp = 0.0f;
+		for (int i = 0; i < separatingAxes.size(); i++) {
+
+			SATResult satResult = objectPolygon.IsSeparatingAxis(facePoly, separatingAxes[i]);
+
+			TEST_LOG("sep axis = " << separatingAxes[i]);
+			TEST_LOG("SAT Result: " << satResult.IsSeparating << "; " << satResult.MinimumOverlap << "; " << satResult.MaximumOverlap);
+
+			if (satResult.IsSeparating || approximatelyEqual(satResult.MinimumOverlap, 0.0f)) {
+				float dp = currentTranslation.dotProduct(separatingAxes[i]);
+				if (fabs(maxDp) <= fabs(dp)) {
+					maxDp = dp;
+					separatingAxis = separatingAxes[i].normalisedCopy();
+					TEST_LOG("^ choose");
+					separatingAxisIndex = i;
+				}
+			}
+		}
+
+		TEST_LOG("remaining axes: " << separatingAxes.size());
+
+		// no axis found - cancel
+		if (separatingAxis == Ogre::Vector2::ZERO) {
+			TEST_LOG("sep.axis = 0");
+			bResolveFinished = true;
+			continue;
+		}
+
+		separatingAxes.erase(separatingAxes.begin() + separatingAxisIndex);
+
+		// make separatingAxis face in same direction as translation
+		if (separatingAxis.dotProduct(currentTranslation) < 0.0f)
+			separatingAxis = -separatingAxis;
+
+		TEST_LOG("sep.axis = " << separatingAxis);
+
+		// calculate maximum allowed translation along separatingAxis
+		float maxFactor = separatingAxis.dotProduct(translation);
+
+		// dont allow movement against translation direction
+		if (maxFactor < 0.0f || approximatelyEqual(maxFactor, 0.0f)) {
+			TEST_LOG("maxFactor <= 0  ("<< maxFactor <<")");
+			bResolveFinished = true;
+			continue;
+		}
+
+
+		// 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 && !approximatelyEqual(satResult.MinimumOverlap, 0.0f)) {
+				// get minimum translation vector in direction of currentTranslation
+				Ogre::Vector2 axisNormal = LeftHandNormal(separatingAxes[i]);
+				Ogre::Vector2 MTV = satResult.MinimumOverlap * axisNormal;
+
+				MTV *= (1.0f + TOLERANCE);
+				TEST_LOG("MTV: "<<MTV);
+
+				float factor = GetTranslationFactorFromDirection(MTV, separatingAxis);
+
+				if (factor < 0.0f) {
+					MTV = satResult.MaximumOverlap * axisNormal;
+					MTV *= (1.0f + TOLERANCE);
+					TEST_LOG("MTV: "<<MTV);
+
+					factor = GetTranslationFactorFromDirection(MTV, separatingAxis);
+				}
+
+				TEST_LOG("->factor="<<factor);
+
+				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;
+
+		TEST_LOG("minFactor="<<minFactor<<"; maxFactor="<<maxFactor);
+
+		Ogre::Vector2 nextTranslation = factor*separatingAxis;
+		if (approximatelyEqual(nextTranslation.x, currentTranslation.x) && approximatelyEqual(nextTranslation.y, currentTranslation.y)) {
+			TEST_LOG("next=current");
+			bResolveFinished = true;
+			continue;
+		} else {
+			currentTranslation = nextTranslation;
+		}
+
+		// 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.0f) {
+				// cancel
+				TEST_LOG("dotProduct < 0");
+				bResolveFinished = true;
+				continue;
+			}
+		} else {
+			visitedFaces[nearestFace] = currentTranslation;
+		}
+
+
+		TEST_LOG("currentTranslation = " << currentTranslation);
 	}
 
-	if (oScene) {
-		if (!oParentNode) {
-			oParentNode = oScene->getRootSceneNode();
-		}
-		if (!oNode) {
-			oNode = oParentNode->createChildSceneNode(Ogre::Vector3::ZERO);
-		}
-		if (oNode) {
-			if (!oEntity && !sEntityName.empty()) {
-				oEntity = oScene->createEntity(sEntityName);
-				if (sMaterial != "")
-					oEntity->setMaterialName(sMaterial);
-				oNode->attachObject(oEntity);
-			}
-			oNode->setPosition(vPositionOffset);
-			oNode->setScale(vScale);
-			oNode->setOrientation(Ogre::Quaternion(Ogre::Radian(angle), Ogre::Vector3::NEGATIVE_UNIT_X));
-		}
-	}
+	TEST_LOG("result=" << result);
+	TEST_LOG("========================"<<std::endl);
 
-	if (oEntity) {
-		oEntity->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY - 1);
-	}
+	return result;
 }
 
 }

File src/GameObjects/LevelObjects.h

 #define LEVELOBJECTS_H_
 
 #include "Geometry.h"
+#include "LevelObjectManager.h"
+
+#include "../testlog.h"
+
 #include "Ogre.h"
 #include "boost/smart_ptr.hpp"
 #include "boost/utility.hpp"
 namespace Game {
 namespace GameObjects {
 
-
 class CLevelObject;
 typedef boost::shared_ptr<CLevelObject> PLevelObject;
 typedef boost::weak_ptr<CLevelObject> WLevelObject;
 	typedef boost::shared_ptr<Listener> PListener;
 
 public:
-	CLevelObject();
-	CLevelObject(const Ogre::Vector2& position);
-	CLevelObject(const Game::Rectangle& bounds);
-	CLevelObject(const CLevelObject& obj);
 	virtual ~CLevelObject() {}
 
-	virtual PLevelObject Copy();
+	/** Factory Method */
+	static PLevelObject Create();
+
+	/** Creates a copy of this object; does not copy listeners */
+	virtual PLevelObject Copy() const;
 
 	virtual void AddListener(PListener listener);
 	virtual void RemoveListener(PListener listener);
 
+	/** Set Parent Object Manager */
+	virtual void SetParent(PLevelObjectManager manager) { parent = manager; }
+	/** Get Parent Object Manager */
+	virtual PLevelObjectManager GetParent() const { return parent.lock(); }
+
 	/** Returns bounding box of object */
 	virtual const Game::Rectangle& GetBoundingBox() const { return aabBounds; }
 	/** Set bounding box of object */
 	/** Set AABB Size (symmetric to center) */
 	virtual void SetAABBSize(const Ogre::Vector2& size);
 protected:
+	CLevelObject();
+	CLevelObject(const Ogre::Vector2& position);
+	CLevelObject(const Game::Rectangle& bounds);
+	CLevelObject(const CLevelObject& obj);
+
 	/** Bounding Box */
 	Game::Rectangle aabBounds;
 	/** Position of Center */
 	Game::Polygon cachedCollisionData;
 	/** Rotation */
 	float angle;
-
+	/** Indicates if object has complex collision data */
 	bool BoundingBoxCollision;
+	/** Parent Level Object Manager */
+	WLevelObjectManager parent;
+	/** Smart Pointer to this */
+	WLevelObject self;
 
 	typedef std::set<PListener> ListenerList;
 	ListenerList Listeners;
 };
 
 
-class COgreEntityView;
-typedef boost::shared_ptr<COgreEntityView> POgreEntityView;
-typedef boost::weak_ptr<COgreEntityView> WOgreEntityView;
+class CMovableObject;
+typedef boost::shared_ptr<CMovableObject> PMovableObject;
 
-/** Level Object View bound to Ogre Entity
+/** Level Object which can be moved with collision check */
+class CMovableObject : public CLevelObject {
+public:
+	virtual ~CMovableObject() { }
 
-@remarks If you don't need to know about the view object, you can just create one and forget about it.
-         The view object will be deleted when the Model is deleted. */
-class COgreEntityView : boost::noncopyable, public CLevelObject::Listener {
-public:
 	/** Factory Method */
-	static POgreEntityView Create(PLevelObject model, Ogre::SceneManager* ogreScene = NULL, Ogre::SceneNode* ogreNode = NULL);
+	static PMovableObject Create();
 
-	virtual ~COgreEntityView();
+	/** Copy this object */
+	virtual PLevelObject Copy() const;
 
-	/** Copy object, set new model
-	@remarks Sets Ogre Scene Manager and Node to NULL */
-	virtual POgreEntityView Copy(PLevelObject model);
+	/** Move object by translation with collision (needs Parent to be set) */
+	virtual void Move(const Ogre::Vector2& translation);
 
-	/** Implementation of CLevelObject::Listener::PositionChanged */
-	virtual void PositionChanged(CLevelObject*);
+protected:
+	CMovableObject() { }
 
-	/** Set Ogre SceneManager and Node. Use rootSceneNode if no Scene Node given
-	@remarks Existing Ogre Entity and Ogre Node will not be deleted */
-	void SetOgreScene(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode = NULL);
-
-	virtual void SetPositionOffset(const Ogre::Vector3& offset);
-	virtual const Ogre::Vector3& GetPositionOffset() const { return vPositionOffset; }
-
-	/** Set ogre entity name */
-	virtual void SetEntityName(const std::string& name);
-	virtual const std::string& GetEntityName() const { return sEntityName; }
-
-	/** Set scale factor */
-	virtual void SetScale(const Ogre::Vector3& scale);
-	virtual const Ogre::Vector3& GetScale() const { return vScale; }
-
-	/** Set Material */
-	virtual void SetMaterial(const std::string& material);
-	virtual const std::string& GetMaterial() const { return sMaterial; }
-
-	/** Set Orientation */
-	virtual void SetOrientation(const Ogre::Quaternion& orientation);
-	virtual const Ogre::Quaternion& GetOrientation() const { return qOrientation; }
-
-	/** Returns corresponding model (can return NULL) */
-	virtual PLevelObject GetModel() { return Model.lock(); }
-
-	virtual Ogre::Entity* GetOgreEntity() { return oEntity; }
-	virtual Ogre::SceneNode* GetOgreNode() { return oNode; }
-
-	/** Returns true if object should be updated every frame */
-	virtual bool IsUpdateable() const { return false; }
-
-	/** Override this to do some updating every frame
-	@param deltaTime Time since last update */
-	virtual void Update(float deltaTime) { }
-protected:
-	COgreEntityView(PLevelObject model, Ogre::SceneManager* ogreScene = NULL, Ogre::SceneNode* ogreNode = NULL);
-	virtual void UpdateGeometry();
-
-	/** ogre entity name*/
-	std::string sEntityName;
-	/** scale factor of ogre entity */
-	Ogre::Vector3 vScale;
-	/** material for ogre entity */
-	std::string sMaterial;
-	/** position offset */
-	Ogre::Vector3 vPositionOffset;
-
-	/** Ogre Scene Manager */
-	Ogre::SceneManager* oScene;
-	/** Pointer to actual ogre entity*/
-	Ogre::Entity* oEntity;
-	/** Pointer to parent ogre node */
-	Ogre::SceneNode* oParentNode;
-	/** Pointer to own ogre node */
-	Ogre::SceneNode* oNode;
-
-	/** orientation of ogre entity */
-	Ogre::Quaternion qOrientation;
-
-	/** Model */
-	WLevelObject Model;
-	/** Smart Pointer of this */
-	WOgreEntityView self;
+	float GetTranslationFactorFromDirection(const Ogre::Vector2& translation, const Ogre::Vector2& direction);
+	Game::Polygon MakePolygon(const Game::Rectangle& rect, const Ogre::Vector2& translation);
+	Ogre::Vector2 ResolveCollision(PLevelObject object, const Ogre::Vector2& translation);
 };
 
 
-class COgreBoundsView;
-typedef boost::shared_ptr<COgreBoundsView> POgreBoundsView;
-typedef boost::weak_ptr<COgreBoundsView> WOgreBoundsView;
-
-/** Level Object View which displays Bounding Box using cube.mesh */
-class COgreBoundsView : public COgreEntityView {
-public:
-	static POgreBoundsView Create(PLevelObject model, Ogre::SceneManager* ogreScene = NULL, Ogre::SceneNode* ogreNode = NULL);
-
-	virtual POgreEntityView Copy(PLevelObject model);
-
-	virtual void BoundsChanged(CLevelObject*);
-protected:
-	COgreBoundsView(PLevelObject model, Ogre::SceneManager* ogreScene = NULL, Ogre::SceneNode* ogreNode = NULL);
-
-	virtual void UpdateGeometry();
-};
 
 
 }

File src/GameObjects/ObjectType.h

 namespace Game {
 namespace GameObjects {
 
+// workaroung to solve circular dependency
+
 enum ObjectType {otStatic = 1, otDynamic = 2, otBackground = 3};
 
 }

File src/GameObjects/OgreViews.cpp

+/*
+ * OgreViews.cpp
+ *
+ *  Created on: Oct 4, 2010
+ *      Author: crt
+ */
+
+#include "OgreViews.h"
+
+namespace Game {
+namespace GameObjects {
+
+COgreEntityView::COgreEntityView(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode)
+	: sEntityName(""),
+	  vScale(1.0, 1.0, 1.0),
+	  sMaterial(""),
+	  vPositionOffset(Ogre::Vector3::ZERO),
+	  oScene(ogreScene),
+	  oEntity(NULL),
+	  oParentNode(ogreNode),
+	  oNode(NULL),
+	  qOrientation(Ogre::Quaternion::IDENTITY),
+	  Model(model)
+{
+	assert(model != NULL);
+	UpdateGeometry();
+}
+
+POgreEntityView COgreEntityView::Create(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
+	COgreEntityView* obj = new COgreEntityView(model, ogreScene, ogreNode);
+	POgreEntityView objPtr( obj );
+	obj->self = objPtr;
+	model->AddListener(objPtr);
+	return objPtr;
+}
+
+COgreEntityView::~COgreEntityView() {
+	PLevelObject model = Model.lock();
+	if (model)
+		model->RemoveListener(self.lock());
+}
+
+POgreEntityView COgreEntityView::Copy(PLevelObject model) {
+	COgreEntityView* copy = new COgreEntityView(model);
+	POgreEntityView copyPtr( copy );
+	copy->self = copyPtr;
+	model->AddListener(copyPtr);
+	copy->qOrientation = this->qOrientation;
+	copy->sEntityName = this->sEntityName;
+	copy->sMaterial = this->sMaterial;
+	copy->vPositionOffset = this->vPositionOffset;
+	copy->vScale = this->vScale;
+	return copyPtr;
+}
+
+void COgreEntityView::UpdateGeometry() {
+	PLevelObject model = Model.lock();
+	if (oScene) {
+		if (!oParentNode) {
+			oParentNode = oScene->getRootSceneNode();
+		}
+		if (!oNode) {
+			oNode = oParentNode->createChildSceneNode(Ogre::Vector3::ZERO);
+		}
+		if (oNode) {
+			if (!oEntity && !sEntityName.empty()) {
+				oEntity = oScene->createEntity(sEntityName);
+				if (sMaterial != "")
+					oEntity->setMaterialName(sMaterial);
+				oNode->attachObject(oEntity);
+			}
+			Ogre::Vector2 modelPosition(0.0f, 0.0f);
+			if (model)
+				modelPosition = model->GetPosition();
+			oNode->setPosition(vPositionOffset + Ogre::Vector3(0.0f, modelPosition.y, modelPosition.x));
+			oNode->setScale(vScale);
+			oNode->setOrientation(qOrientation);
+		}
+	}
+}
+
+void COgreEntityView::SetOgreScene(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
+	oScene = ogreScene;
+	if (oParentNode != ogreNode) {
+		oParentNode = ogreNode;
+		oNode = NULL; oEntity = NULL;
+	}
+	if (ogreScene && !oParentNode)
+		oParentNode = ogreScene->getRootSceneNode();
+	UpdateGeometry();
+}
+
+
+void COgreEntityView::PositionChanged(CLevelObject*) {
+	UpdateGeometry();
+}
+
+void COgreEntityView::SetPositionOffset(const Ogre::Vector3& offset) {
+	if (vPositionOffset != offset) {
+		vPositionOffset = offset;
+		UpdateGeometry();
+	}
+}
+
+void COgreEntityView::SetEntityName(const std::string& name) {
+	if (sEntityName != name) {
+		if (oEntity) {
+			oNode->detachObject(oEntity);
+			oScene->destroyEntity(oEntity);
+		}
+		oEntity = NULL;
+		sEntityName = name;
+		UpdateGeometry();
+	}
+}
+
+void COgreEntityView::SetScale(const Ogre::Vector3& scale) {
+	vScale = scale;
+	UpdateGeometry();
+}
+
+void COgreEntityView::SetMaterial(const std::string& material) {
+	if (sMaterial != material) {
+		sMaterial = material;
+		if (oEntity && sMaterial != "") {
+			oEntity->setMaterialName(sMaterial);
+		}
+		UpdateGeometry();
+	}
+}
+
+void COgreEntityView::SetOrientation(const Ogre::Quaternion& orientation) {
+	qOrientation = orientation;
+	UpdateGeometry();
+}
+
+
+COgreBoundsView::COgreBoundsView(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode)
+	: COgreEntityView(model, ogreScene, ogreNode)
+{
+	this->sEntityName = "cube.mesh";
+	UpdateGeometry();
+}
+
+POgreBoundsView COgreBoundsView::Create(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
+	COgreBoundsView* obj = new COgreBoundsView(model, ogreScene, ogreNode);
+	POgreBoundsView objPtr( obj );
+	obj->self = objPtr;
+	model->AddListener(objPtr);
+	return objPtr;
+}
+
+POgreEntityView COgreBoundsView::Copy(PLevelObject model) {
+	COgreBoundsView* copy = new COgreBoundsView(model);
+	POgreBoundsView copyPtr( copy );
+	copy->self = copyPtr;
+	model->AddListener(copyPtr);
+	copy->qOrientation = this->qOrientation;
+	copy->sEntityName = this->sEntityName;
+	copy->sMaterial = this->sMaterial;
+	copy->vPositionOffset = this->vPositionOffset;
+	copy->vScale = this->vScale;
+	return copyPtr;
+}
+
+void COgreBoundsView::BoundsChanged(CLevelObject*) {
+	UpdateGeometry();
+}
+
+void COgreBoundsView::UpdateGeometry() {
+	PLevelObject model = Model.lock();
+	float angle = 0.0f;
+	if (model) {
+		Game::Rectangle bounds = model->GetBoundingBox();
+		Ogre::Vector2 center = 0.5f*(bounds.GetMaximum() + bounds.GetMinimum());
+		Ogre::Vector2 scale2d =  bounds.GetMaximum() - bounds.GetMinimum();
+
+		std::vector<Ogre::Vector2> poly = model->GetCleanCollisionPolygon().GetPoints();
+		// assume it's a oriented bounding box
+		if (poly.size() == 4) {
+			center = model->GetCleanCollisionPolygon().CalculateCenter();
+			scale2d = poly[2] - poly[0];
+			angle = model->GetRotation();
+		}
+
+		// cube.mesh is 100x100x100
+		this->vScale.x = 1.0f * 0.01f;
+		this->vScale.y = scale2d.y * 0.01f;
+		this->vScale.z = scale2d.x * 0.01f;
+
+		this->vPositionOffset = Ogre::Vector3(0.0f, center.y, center.x);
+	}
+
+	if (oScene) {
+		if (!oParentNode) {
+			oParentNode = oScene->getRootSceneNode();
+		}
+		if (!oNode) {
+			oNode = oParentNode->createChildSceneNode(Ogre::Vector3::ZERO);
+		}
+		if (oNode) {
+			if (!oEntity && !sEntityName.empty()) {
+				oEntity = oScene->createEntity(sEntityName);
+				if (sMaterial != "")
+					oEntity->setMaterialName(sMaterial);
+				oNode->attachObject(oEntity);
+			}
+			oNode->setPosition(vPositionOffset);
+			oNode->setScale(vScale);
+			oNode->setOrientation(Ogre::Quaternion(Ogre::Radian(angle), Ogre::Vector3::NEGATIVE_UNIT_X));
+		}
+	}
+
+	if (oEntity) {
+		oEntity->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY - 1);
+	}
+}
+
+}
+}

File src/GameObjects/OgreViews.h

+/*
+ * OgreViews.h
+ *
+ *  Created on: Oct 4, 2010
+ *      Author: crt
+ */
+
+#ifndef OGREVIEWS_H_
+#define OGREVIEWS_H_
+
+#include "LevelObjects.h"
+#include "Ogre.h"
+#include "boost/shared_ptr.hpp"
+
+namespace Game {
+namespace GameObjects {
+
+class COgreEntityView;
+typedef boost::shared_ptr<COgreEntityView> POgreEntityView;
+typedef boost::weak_ptr<COgreEntityView> WOgreEntityView;
+
+/** Level Object View bound to Ogre Entity
+
+@remarks If you don't need to know about the view object, you can just create one and forget about it.