Commits

Anonymous committed 071680a

updated level object interfaces; added OgreBillboardView

  • Participants
  • Parent commits 9d220fa

Comments (0)

Files changed (16)

File media/leveldata/obbtest.xml

 <leveldata version="1">
     <templates>
     	<template name="GiantBox" type="CustomAABB">
-    		<material name="Textures/Rockwall" />
+    		<material name="Textures/Crate" />
     		<entity name="cube.mesh" />
 			<position x="0" y="0" z="0" />
-    		<scale x="0.1" y="1" z="1" />
+    		<scale x="1" y="1" z="1" />
     		<AABBsize x="100" y="100" />    		
     	</template>
+        <template name="coin" type="Billboard">
+        	<material name="Textures/Coin" />
+        	<direction x="0" y="1" z="0" />
+            <AABBsize x="5" y="5" />
+            <tags>
+            	<tag name="name" type="string" value="muenze" />
+            	<tag name="value" type="float" value="10" />
+            </tags>
+            <CollisionHandler type="Coin" />
+        </template>   
     </templates>
     <levelobjects>
     	<object type="GiantBox" layer="static" x="35" y="-61" />
         <object type="GiantBox" layer="static" x="142.5761" y="-57.0349">
-            <material name="Textures/Marmor" />
             <position x="0" y="5" z="0" />
             <orientation w="0.99747" x="-0.0711167" y="0" z="0" />
             <Collision rotation="0">
     	<object type="GiantBox" layer="static" x="234" y="0">
     		<position x="-0.1" y="0" z="0" />
     	</object>        
+        <object type="coin" layer="dynamic" x="71" y="10" />
+        <object type="coin" layer="dynamic" x="80" y="10" />
+        <object type="coin" layer="dynamic" x="90" y="10" />
     </levelobjects>
 </leveldata>

File media/main/starcoin.png

Added
New image

File media/main/textures.material

 	}
 }
 
+material Textures/Crate
+{
+	technique
+	{
+		pass
+		{
+			texture_unit
+			{
+				texture crate.png
+			}
+		}
+	}
+}
+
 material Textures/Marmor
 {
 	technique
 	}
 }
 
+material Textures/Coin
+{
+	technique
+	{
+		pass
+		{
+			depth_write off
+			scene_blend alpha_blend
+			lighting off
+			texture_unit
+			{
+				texture starcoin.png
+			}
+		}
+	}
+}
+
+
 material Textures/SkyBox
 {
 	technique

File src/GameObjects/CLevelData.cpp

 	}
 }
 
-void CLevelData::InsertView(POgreEntityView view, ObjectType type) {
+void CLevelData::InsertView(POgreView view, ObjectType type) {
 	ogreViews.push_back(view);
 }
 

File src/GameObjects/CLevelData.h

 	/** Insert view as specified type
 	@param object The object
 	@param type Specify where to insert (static, dynamic, background) */
-	virtual void InsertView(POgreEntityView view, ObjectType type);
+	virtual void InsertView(POgreView view, ObjectType type);
 
 	static PLevelData Create() {
 		CLevelData* leveldata = new CLevelData();
 		InsertObject(object, static_cast<ObjectType>(layer) );
 	}
 	/** Implementation of CXMLReader::Listener */
-	virtual void ReadOgreEntityView(POgreEntityView view, ObjectType layer) {
-		InsertView(view, static_cast<ObjectType>(layer));
+	virtual void ReadOgreView(POgreView view, ObjectType layer) {
+		InsertView(view, layer);
 	}
 	/** Implementation of ILevelObjectManager */
 	virtual ILevelObjectManager::LevelObjectList GetObjectRectangle(const Game::Rectangle& rect);
 private:
-	typedef std::vector<POgreEntityView> OgreViewList;
+	typedef std::vector<POgreView> OgreViewList;
 
 	ILevelObjectManager::LevelObjectList GetObjectRectangle(const Game::Rectangle& rect, ILevelObjectManager::LevelObjectList& list);
 

File src/GameObjects/LevelDataReader.cpp

 	return Ogre::Quaternion(w,x,y,z);
 }
 
-void CXMLReader::ReadLevelObject(const ticpp::Element& node, PLevelObject object, POgreEntityView view) {
-	assert(object != NULL); assert(view != NULL);
+void CXMLReader::ReadLevelObject(const ticpp::Element& node, PLevelObject object) {
+	Ogre::Vector2 position;
+	node.GetAttributeOrDefault<float>("x", &position.x, 0.0f);
+	node.GetAttributeOrDefault<float>("y", &position.y, 0.0f);
+	object->SetPosition(position);
 
+	ticpp::Element* AABBsize = node.FirstChildElement("AABBsize", false);
+	if (AABBsize) {
+		object->SetAABBSize(ReadVector2(*AABBsize));
+	}
+
+	ticpp::Element* AABBextends = node.FirstChildElement("AABBextends", false);
+	if (AABBextends) {
+		object->SetBoundingBox(ReadRectangle(*AABBextends));
+	}
+
+	ticpp::Element* collision = node.FirstChildElement("Collision", false);
+	if (collision) {
+		object->SetRotation(collision->GetAttribute<float>("rotation"));
+		object->SetCollisionPolygon(ReadPolygon(*collision));
+	}
+
+	ticpp::Element* handler = node.FirstChildElement("CollisionHandler", false);
+	if (handler) {
+		std::string handlerType = ReadString(*handler, "type");
+		if (handlerType == "Coin")
+			object->SetCollisionHandler(TestHandler());
+	}
+}
+
+void CXMLReader::ReadOgreEntityView(const ticpp::Element& node, POgreEntityView view) {
 	ticpp::Element* position = node.FirstChildElement("position", false);
 	if (position) {
-		//Ogre::Vector3 pos = ReadVector(*position);
-		//object->SetPosition(object->GetPosition() + Ogre::Vector2(pos.z, pos.y));
-		//view->SetPositionOffset(view->GetPositionOffset() + Ogre::Vector3(pos.x, 0.0f, 0.0f));
 		view->SetPositionOffset(ReadVector(*position));
 	}
 
 		view->SetMaterial(ReadString(*material));
 	}
 
-	ticpp::Element* AABBsize = node.FirstChildElement("AABBsize", false);
-	if (AABBsize) {
-		object->SetAABBSize(ReadVector2(*AABBsize));
-	}
-
-	ticpp::Element* AABBextends = node.FirstChildElement("AABBextends", false);
-	if (AABBextends) {
-		object->SetBoundingBox(ReadRectangle(*AABBextends));
-	}
-
 	ticpp::Element* orientation = node.FirstChildElement("orientation", false);
 	if (orientation) {
 		view->SetOrientation(ReadQuaternion(*orientation));
 	}
+}
 
-	ticpp::Element* collision = node.FirstChildElement("Collision", false);
-	if (collision) {
-		object->SetRotation(collision->GetAttribute<float>("rotation"));
-		object->SetCollisionPolygon(ReadPolygon(*collision));
+void CXMLReader::ReadOgreBillboardView(const ticpp::Element& node, POgreBillboardView view) {
+	ticpp::Element* material = node.FirstChildElement("material", false);
+	if (material) {
+		view->SetMaterial(ReadString(*material));
+	}
+
+	ticpp::Element* direction = node.FirstChildElement("direction", false);
+	if (direction) {
+		view->SetDirection(ReadVector(*direction));
 	}
 }
 
-void CXMLReader::ReadTemplate(const ticpp::Element& node, Listener* listener) {
+void CXMLReader::ReadObject(const ticpp::Element& node, Listener* listener, bool asTemplate) {
 	try {
-		std::string sName = ReadString(node);
 		std::string sType = ReadString(node, "type");
 
 		PLevelObject object;
-		POgreEntityView view;
+		POgreView view;
 
-		if (sType != "CustomAABB") {
-			ModelViewPair Template = templates[sType];
-			if (Template.first != NULL && Template.second != NULL) {
-				// copy template
-				object = Template.first->Copy();
-				view = Template.second->Copy(object);
-			} else {
-				// template not found
-				// TODO throw exception
-				return;
-			}
-		} else {
+		if (sType == "CustomAABB") {
 			object = CLevelObject::Create();
 			view = COgreEntityView::Create(object);
-		}
-
-		ReadLevelObject(node, object, view);
-
-		templates[sName] = ModelViewPair(object, view);
-		listener->ReadTemplate(sName, object, view);
-
-	} catch (ticpp::Exception& ex) {
-	}
-}
-
-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");
-		std::string sLayer = ReadString(node, "layer");
-		Ogre::Vector2 position = ReadVector2(node);
-
-		PLevelObject object;
-		POgreEntityView view;
-
-		if (sType == "CustomAABB") {
-			// create new
+		} else if (sType == "Billboard") {
 			object = CLevelObject::Create();
-			view = COgreEntityView::Create(object);
+			view = COgreBillboardView::Create(object);
 		} else {
 			// copy template
 			ModelViewPair Template = templates[sType];
 			}
 		}
 
-		ObjectType otype;
-		if (sLayer == "static")
-			otype = otStatic;
-		else if (sLayer == "background")
-			otype = otBackground;
-		else if (sLayer == "dynamic")
-			otype = otDynamic;
-		else
-			return;
+		// TODO find better solution than dynamic_cast
 
-		// add position from object tag
-		object->SetPosition(position);
-		//object->SetPosition(object->GetPosition() + Ogre::Vector2(position.z, position.y));
-		//view->SetPositionOffset(view->GetPositionOffset() + Ogre::Vector3(position.x, 0.0f, 0.0f));
+		// read model data
+		ReadLevelObject(node, object);
 
-		// read additional data if exists
-		ReadLevelObject(node, object, view);
+		// read view data
+		if (boost::dynamic_pointer_cast<COgreEntityView>(view) != NULL)
+			ReadOgreEntityView(node, boost::static_pointer_cast<COgreEntityView>(view));
+		if (boost::dynamic_pointer_cast<COgreBillboardView>(view) != NULL)
+			ReadOgreBillboardView(node, boost::static_pointer_cast<COgreBillboardView>(view));
 
-		listener->ReadLevelObject(object, otype);
-		listener->ReadOgreEntityView(view, otype);
+		if (!asTemplate) {
+			std::string sLayer = ReadString(node, "layer");
+			ObjectType otype;
+			if (sLayer == "static")
+				otype = otStatic;
+			else if (sLayer == "background")
+				otype = otBackground;
+			else
+				otype = otDynamic;
+
+			listener->ReadLevelObject(object, otype);
+			listener->ReadOgreView(view, otype);
+		} else {
+			std::string sName = ReadString(node);
+			templates[sName] = ModelViewPair(object, view);
+			listener->ReadTemplate(sName, object, view);
+		}
 	} catch(ticpp::Exception& ex) {
 	}
 }
 			// iterate <template>..</template>
 			ticpp::Iterator< ticpp::Element > child("template");
 			for (child = child.begin( templatesNode ); child != child.end(); child++) {
-				ReadTemplate(*child, listener);
+				ReadObject(*child, listener, true);
 			}
 		}
 
 				// iterate <object... />
 				ticpp::Iterator< ticpp::Element > child("object");
 				for (child = child.begin( objectsNode ); child != child.end(); child++) {
-					ReadObject(*child, listener);
+					ReadObject(*child, listener, false);
 				}
 			}
 		}

File src/GameObjects/LevelDataReader.h

 		Listener() { }
 		virtual ~Listener() { }
 
-		virtual void ReadTemplate(std::string name, PLevelObject object, POgreEntityView view) { }
+		virtual void ReadTemplate(std::string name, PLevelObject object, POgreView view) { }
 		virtual void ReadLevelObject(PLevelObject object, ObjectType layer) { }
-		virtual void ReadOgreEntityView(POgreEntityView view, ObjectType layer) { }
+		virtual void ReadOgreView(POgreView 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, Listener* listener, bool TemplatesOnly = false);
 private:
+	typedef std::pair<PLevelObject, POgreView> ModelViewPair;
+	typedef std::map<std::string, ModelViewPair> TemplateMap;
 	TemplateMap templates;
 
-	void ReadLevelObject(const ticpp::Element& node, PLevelObject object, POgreEntityView view);
-	void ReadTemplate(const ticpp::Element& node, Listener* listener);
-	void ReadObject(const ticpp::Element& node, Listener* listener);
+	void ReadOgreEntityView(const ticpp::Element& node, POgreEntityView view);
+	void ReadOgreBillboardView(const ticpp::Element& node, POgreBillboardView view);
+	void ReadLevelObject(const ticpp::Element& node, PLevelObject object);
+	void ReadObject(const ticpp::Element& node, Listener* listener, bool asTemplate);
 
 	Ogre::Quaternion ReadQuaternion(const ticpp::Element& node);
 	std::string ReadString(const ticpp::Element& node, const std::string& name = "name");

File src/GameObjects/LevelObjectManager.h

 #ifndef LEVELOBJECTMANAGER_H_
 #define LEVELOBJECTMANAGER_H_
 
+#include "Messaging.h"
 #include "Geometry.h"
 #include "boost/shared_ptr.hpp"
 
 
 class CLevelObject;
 
-class ILevelObjectManager {
+class ILevelObjectManager : public CMessageManager {
 public:
 	typedef std::vector<boost::shared_ptr<CLevelObject> > LevelObjectList;
 
 	virtual ~ILevelObjectManager() { }
 
 	/** Return all Objects inside given Rectangle */
-	virtual LevelObjectList GetObjectRectangle(const Game::Rectangle& rect) { }
+	virtual LevelObjectList GetObjectRectangle(const Game::Rectangle& rect) = 0;
 
 	/** Called by a level object when it changes its position or boundaries */
 	virtual void ObjectPositionChanged(boost::shared_ptr<CLevelObject> object) { }

File src/GameObjects/LevelObjects.cpp

 namespace GameObjects {
 
 CLevelObject::CLevelObject()
-	: vPosition(0.0f, 0.0f), collisionType(ctBlocking)
+	: IMessagingObject(), vPosition(0.0f, 0.0f), collisionType(ctBlocking)
 {
 	EnableBoundingBoxCollision();
 }
 
 CLevelObject::CLevelObject(const Ogre::Vector2& position)
-	: vPosition(position), collisionType(ctBlocking)
+	: IMessagingObject(), vPosition(position), collisionType(ctBlocking)
 {
 	EnableBoundingBoxCollision();
 }
 
 CLevelObject::CLevelObject(const Game::Rectangle& bounds)
-	: vPosition(0.0f, 0.0f), aabBounds(bounds), collisionType(ctBlocking)
+	: IMessagingObject(), vPosition(0.0f, 0.0f), aabBounds(bounds), collisionType(ctBlocking)
 {
 	EnableBoundingBoxCollision();
 }
 
-CLevelObject::CLevelObject(const CLevelObject& obj) {
+CLevelObject::CLevelObject(const CLevelObject& obj) : IMessagingObject(obj) {
 	// don't copy listeners
 	aabBounds = obj.aabBounds;
 	collisionData = obj.collisionData;
 	vPosition = obj.vPosition;
 	collisionType = obj.collisionType;
 	BoundingBoxCollision = obj.BoundingBoxCollision;
+	collisionHandler = obj.collisionHandler;
 }
 
 PLevelObject CLevelObject::Create() {
 	Ogre::Vector2 currentTranslation = translation; // translation we are performing currently
 	Ogre::Vector2 result(0,0); 	// all performed translations until any given point
 
+	std::map<PLevelObject, Ogre::Vector2> Collisions; // store collisions
+
 	float maxLength = translation.length(); // maximum allowed transformation length
 
 	// stores translation vector for each face we already moved along
 			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) {
+					if (obj->GetCollisionType(object, currentTranslation) == CLevelObject::ctBlocking) {
 						intersectingObjects.push_back(obj);
 					} else {
 						ignoreObjects.push_back(obj);
 					}
+					Collisions[obj] = currentTranslation;
 				}
 			}
 
 				rect.Translate(maxTranslation);
 				currentTranslation -= maxTranslation;
 
+				Collisions[nearestObj] = maxTranslation;
+
 				// check if collision is blocking
-				if (nearestObj->HandleCollision(object, maxTranslation) != CLevelObject::ctBlocking) {
+				if (nearestObj->GetCollisionType(object, maxTranslation) != CLevelObject::ctBlocking) {
 					ignoreObjects.push_back(nearestObj);
 					nearestObj.reset();
 					LastCollisionNonBlocking = true;
 			currentTranslation = nextTranslation;
 		}
 
+		Collisions[nearestObj] = currentTranslation;
+
 		// 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
 	}
 
 	TEST_LOG("result=" << result);
+	TEST_LOG("collisions=" << Collisions.size());
 	TEST_LOG("========================"<<std::endl);
 
+	// fire collision handlers of objects we collided with
+	for (std::map<PLevelObject, Ogre::Vector2>::iterator it = Collisions.begin(); it != Collisions.end(); ++it) {
+		it->first->HandleCollision(object, it->second);
+	}
+
+
 	return result;
 }
 

File src/GameObjects/LevelObjects.h

 #ifndef LEVELOBJECTS_H_
 #define LEVELOBJECTS_H_
 
+#include "Messaging.h"
 #include "Geometry.h"
 #include "LevelObjectManager.h"
 
 #include "Ogre.h"
 #include "boost/smart_ptr.hpp"
 #include "boost/utility.hpp"
+#include "boost/function.hpp"
 #include <set>
 
 namespace Game {
 
 class CLevelObject;
 typedef boost::shared_ptr<CLevelObject> PLevelObject;
+typedef boost::shared_ptr<const CLevelObject> cPLevelObject;
 typedef boost::weak_ptr<CLevelObject> WLevelObject;
 
 /** Level Object Model with Convex Polygon Collision */
-class CLevelObject {
+class CLevelObject : public IMessagingObject {
 public:
+	/** void CollisionHandler(PLevelObject this, cPLevelObject sender, const Ogre::Vector2& direction) */
+	typedef boost::function<void (PLevelObject, cPLevelObject, const Ogre::Vector2&)> CollisionHandler;
+
 	enum CollisionType {ctBlocking, ctNonBlocking};
 	typedef std::vector<Game::Face> FaceList;
 
 	/** Get Rotation */
 	virtual float GetRotation() const { return angle; }
 
-	virtual CollisionType GetCollisionType() const { return collisionType; };
+	/** Return Collision Type for a given level object and direction
+	 *  Overwrite to get dynamic behaviour
+	 @remarks As opposed to HandleCollision(...) this function might get called a lot during one Movement.
+			  Returns this->collisionType as default. */
+	virtual CollisionType GetCollisionType(cPLevelObject sender, const Ogre::Vector2& direction) const { return collisionType; };
+
+	/** Set Collision Type */
 	virtual void SetCollisionType(CollisionType collisionType);
 
-	/** Called when another object collides
+	/** Called once if another Object collides with this
 	@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; }
+	@remarks Overwrite to respond to Collision. */
+	virtual void HandleCollision(cPLevelObject sender, const Ogre::Vector2& direction) {
+		if (collisionHandler) collisionHandler(self.lock(), sender, direction);
+	}
+
+	virtual void SetCollisionHandler(CollisionHandler handler) { collisionHandler = handler; }
 
 	/** Set Position (also updates bounding box) */
 	virtual void SetPosition(const Ogre::Vector2& position);
 	/** Smart Pointer to this */
 	WLevelObject self;
 
+	/** Collision Handler */
+	CollisionHandler collisionHandler;
+
+
 	typedef std::set<PListener> ListenerList;
 	ListenerList Listeners;
 

File src/GameObjects/Messaging.h

 
 #include "boost/smart_ptr.hpp"
 #include "boost/any.hpp"
+#include <string>
+#include <map>
+#include <queue>
 
 namespace Game {
-namespace Gameobjects {
+namespace GameObjects {
+
+/** Wrapper for easier handling of a map<string,any> */
+class ParameterList {
+private:
+	typedef std::map<std::string, boost::any> StringAnyMap;
+	StringAnyMap parameters;
+
+public:
+	ParameterList() { }
+	virtual ~ParameterList() { }
+
+	/** Checks if given key contains specific type */
+	template <typename T>
+	bool IsType(const std::string& key) const {
+		try {
+			boost::any_cast<T>(parameters[key]);
+		} catch (...) {
+			return false;
+		}
+		return true;
+	}
+
+	template<typename T>
+	T operator[](const std::string& key) {
+		boost::any value = parameters[key];
+		T result;
+		try {
+			result = boost::any_cast<T>(value);
+		} catch (...) { }
+
+		return result;
+	}
+
+	template<typename T>
+	T operator[](const std::string& key, const T& defaultValue) {
+		boost::any value = parameters[key];
+		T result;
+		try {
+			result = boost::any_cast<T>(value);
+		} catch (...) {
+			return defaultValue;
+		}
+
+		return result;
+	}
+
+	template<typename T>
+	void Set(const std::string& key, const T& value) {
+		parameters[key] = value;
+	}
+};
 
 class IMessagingObject;
 typedef boost::shared_ptr<IMessagingObject> PMessagingObject;
+typedef boost::weak_ptr<IMessagingObject> WMessagingObject;
 
 /** Object containing Message Information */
 struct SMessage {
 	enum MessageType {mtKill, mtGetItem};
 	PMessagingObject Sender;
 	PMessagingObject Target;
-	std::vector<boost::any> Parameters;
+	ParameterList Parameters;
 };
 
 /** Interface for objects which can receive messages*/
 class IMessagingObject {
 public:
-	IMessagingObject() {}
-	virtual ~IMessagingObject() {}
+	IMessagingObject() { }
+	virtual ~IMessagingObject() { }
 
-	virtual void ReceiveMessage(SMessage message) = 0;
+	/** Handle given message */
+	virtual void ReceiveMessage(const SMessage& message) { }
+
+	virtual void SetName(const std::string& Name) { name = Name; }
+	virtual const std::string& GetName() const { return name; }
+
+	/** Meta Data */
+	ParameterList Tags;
+protected:
+	std::string name;
+};
+typedef boost::shared_ptr<IMessagingObject> PMessagingObject;
+
+/** Messaging Manager
+ *  Inserts Messages into a Queue when sent. Delivers all messages in queue when calling DispatchMessages */
+class CMessageManager {
+private:
+	typedef std::pair<WMessagingObject, SMessage> ObjectMessagePair;
+	typedef std::queue<ObjectMessagePair> MessageQueue;
+
+	MessageQueue Queue;
+
+public:
+	CMessageManager() { }
+	virtual ~CMessageManager() { }
+
+	/** Sends a message to a messaging object */
+	virtual void SendMessage(PMessagingObject target, const SMessage& message) {
+		Queue.push(ObjectMessagePair(target, message));
+	}
+
+	/** Delivers all messages currently in queue */
+	virtual void DispatchMessages() {
+		if (Queue.empty())
+			return;
+
+		// copy Queue
+		MessageQueue currentMessages = Queue;
+		// replace queue with empty one
+		Queue = MessageQueue();
+
+		// dispatch all messages
+		while (!currentMessages.empty()) {
+			ObjectMessagePair message = currentMessages.front();
+			PMessagingObject receiver = message.first.lock();
+			if (receiver)
+				receiver->ReceiveMessage(message.second);
+			currentMessages.pop();
+		}
+	}
 };
 
 }

File src/GameObjects/OgreViews.cpp

 namespace GameObjects {
 
 COgreEntityView::COgreEntityView(PLevelObject model, Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode)
-	: sEntityName(""),
+	: IOgreView(model),
+	  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)
+	  qOrientation(Ogre::Quaternion::IDENTITY)
 {
-	assert(model != NULL);
+	oScene = ogreScene;
+	oParentNode = ogreNode;
 	UpdateGeometry();
 }
 
 	return objPtr;
 }
 
-COgreEntityView::~COgreEntityView() {
-	PLevelObject model = Model.lock();
-	if (model)
-		model->RemoveListener(self.lock());
-}
-
-POgreEntityView COgreEntityView::Copy(PLevelObject model) {
+POgreView COgreEntityView::Copy(PLevelObject model) {
 	COgreEntityView* copy = new COgreEntityView(model);
 	POgreEntityView copyPtr( copy );
 	copy->self = copyPtr;
 	return objPtr;
 }
 
-POgreEntityView COgreBoundsView::Copy(PLevelObject model) {
+POgreView COgreBoundsView::Copy(PLevelObject model) {
 	COgreBoundsView* copy = new COgreBoundsView(model);
 	POgreBoundsView copyPtr( copy );
 	copy->self = copyPtr;
 	}
 }
 
+COgreBillboardView::COgreBillboardView(PLevelObject model)
+	: IOgreView(model), sMaterial(""), billboardDirection(0, 0, 0)
+{ }
+
+POgreBillboardView COgreBillboardView::Create(PLevelObject model) {
+	COgreBillboardView* obj = new COgreBillboardView(model);
+	POgreBillboardView objPtr(obj);
+	model->AddListener(objPtr);
+	obj->self = objPtr;
+	return objPtr;
 }
+
+POgreView COgreBillboardView::Copy(PLevelObject model) {
+	COgreBillboardView* copy = new COgreBillboardView(model);
+	POgreBillboardView copyPtr( copy );
+	copy->self = copyPtr;
+	model->AddListener(copyPtr);
+	copy->sMaterial = this->sMaterial;
+	copy->billboardDirection = this->billboardDirection;
+	return copyPtr;
 }
+
+void COgreBillboardView::SetMaterial(const std::string& material) {
+	if (sMaterial != material) {
+		sMaterial = material;
+		UpdateGeometry();
+	}
+}
+
+void COgreBillboardView::BoundsChanged(CLevelObject*) {
+	UpdateGeometry();
+}
+
+void COgreBillboardView::PositionChanged(CLevelObject*) {
+	UpdateGeometry();
+}
+
+void COgreBillboardView::SetOgreScene(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode) {
+	this->oScene = ogreScene;
+	this->oParentNode = ogreNode;
+	this->oBillboard = NULL;
+	this->oBillboardSet = NULL;
+	UpdateGeometry();
+}
+
+void COgreBillboardView::SetDirection(const Ogre::Vector3& direction) {
+	if (direction != billboardDirection) {
+		billboardDirection = direction;
+		UpdateGeometry();
+	}
+}
+
+void COgreBillboardView::UpdateGeometry() {
+	PLevelObject model = Model.lock();
+	Ogre::Vector2 position(0,0);
+	Game::Rectangle rect;
+	if (model) {
+		position = model->GetPosition();
+		rect = model->GetBoundingBox();
+	}
+	if (oScene) {
+		if (!oParentNode) {
+			oParentNode = oScene->getRootSceneNode();
+		}
+		if (!oNode) {
+			oNode = oParentNode->createChildSceneNode(Ogre::Vector3::ZERO);
+		}
+		if (!oBillboardSet) {
+			oBillboardSet = oScene->createBillboardSet(1);
+			oBillboard = NULL;
+			oNode->attachObject(oBillboardSet);
+		}
+
+		if (billboardDirection == Ogre::Vector3::ZERO) {
+			oBillboardSet->setBillboardType(Ogre::BBT_POINT);
+		} else {
+			oBillboardSet->setBillboardType(Ogre::BBT_ORIENTED_COMMON);
+			oBillboardSet->setCommonDirection(billboardDirection);
+		}
+
+		float width = fabs(rect.Right() - rect.Left());
+		float height = fabs(rect.Top() - rect.Bottom());
+		oBillboardSet->setDefaultDimensions(width, height);
+
+		if (!oBillboard) {
+			oBillboard = oBillboardSet->createBillboard(Ogre::Vector3::ZERO, Ogre::ColourValue::White);
+		}
+
+		oBillboard->setDimensions(width, height);
+		oBillboard->setPosition(0.0f, position.y, position.x);
+		oBillboardSet->_updateBounds();
+		if (sMaterial != "")
+			oBillboardSet->setMaterialName(sMaterial);
+	}
+}
+
+}
+}

File src/GameObjects/OgreViews.h

 namespace Game {
 namespace GameObjects {
 
+
+class IOgreView;
+typedef boost::shared_ptr<IOgreView> POgreView;
+typedef boost::weak_ptr<IOgreView> WOgreView;
+
+/** Interface for Ogre Views
+@remarks Provide static factory methods in derived classes and use these to make sure
+		 Model->AddListener is called with a Smart Pointer to this. You may also take the chance
+		 and store a weak pointer to this in your class.
+		 Implement the methods from CLevelObject::Listener to synchronize the view with its model.
+		 Usually an Ogre View should create an Ogre SceneNode and attach everything to it (see oNode, GetOgreNode).
+		 Make sure whenever SetOgreScene is called, your view is actually displayed using the provieded scenemanager and node.
+		 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 (if no other references exist).
+		 See COgreEntityView as an example */
+class IOgreView : boost::noncopyable, public CLevelObject::Listener  {
+public:
+	/** Copy this object and set model (use this method instead of copy constructor) */
+	virtual POgreView Copy(PLevelObject model) = 0;
+
+	/** Set Ogre SceneManager and Node. Use rootSceneNode if no Scene Node given
+	@remarks Existing Ogre Entity and Ogre Node will not be deleted */
+	virtual void SetOgreScene(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode = NULL) = 0;
+
+	/** Returns created Ogre Scene Node */
+	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) { }
+
+	/** Returns corresponding model (can return NULL) */
+	virtual PLevelObject GetModel() { return Model.lock(); }
+
+	virtual ~IOgreView() {
+		PLevelObject model = Model.lock();
+		if (model) model->RemoveListener(self.lock());
+	}
+protected:
+	IOgreView(PLevelObject model) : oScene(NULL), oNode(NULL), oParentNode(NULL), Model(model) {
+		assert(model != NULL);
+	}
+
+	/** Ogre Scene Manager */
+	Ogre::SceneManager* oScene;
+	/** Pointer to own ogre node */
+	Ogre::SceneNode* oNode;
+	/** Pointer to parent ogre node */
+	Ogre::SceneNode* oParentNode;
+	/** Model */
+	WLevelObject Model;
+	/** Smart Pointer to this */
+	WOgreView self;
+};
+
 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.
-         The view object will be deleted when the Model is deleted. */
-class COgreEntityView : boost::noncopyable, public CLevelObject::Listener {
+/** Level Object View bound to Ogre Entity  */
+class COgreEntityView : public IOgreView {
 public:
 	/** Factory Method */
 	static POgreEntityView Create(PLevelObject model, Ogre::SceneManager* ogreScene = NULL, Ogre::SceneNode* ogreNode = NULL);
 
-	virtual ~COgreEntityView();
+	virtual ~COgreEntityView() { }
 
 	/** Copy object, set new model
 	@remarks Sets Ogre Scene Manager and Node to NULL */
-	virtual POgreEntityView Copy(PLevelObject model);
+	virtual POgreView Copy(PLevelObject model);
 
 	/** Implementation of CLevelObject::Listener::PositionChanged */
 	virtual void PositionChanged(CLevelObject*);
 
 	/** 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 SetOgreScene(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode = NULL);
 
 	virtual void SetPositionOffset(const Ogre::Vector3& offset);
 	virtual const Ogre::Vector3& GetPositionOffset() const { return vPositionOffset; }
 	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();
 	/** 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;
 };
 
 
 public:
 	static POgreBoundsView Create(PLevelObject model, Ogre::SceneManager* ogreScene = NULL, Ogre::SceneNode* ogreNode = NULL);
 
-	virtual POgreEntityView Copy(PLevelObject model);
+	virtual POgreView Copy(PLevelObject model);
 
 	virtual void BoundsChanged(CLevelObject*);
 protected:
 };
 
 
+class COgreBillboardView;
+typedef boost::shared_ptr<COgreBillboardView> POgreBillboardView;
+typedef boost::weak_ptr<COgreBillboardView> WOgreBillboardView;
+
+/** Level Object View which displays object as billboard with the size of its bounding box */
+class COgreBillboardView : public IOgreView {
+public:
+	virtual ~COgreBillboardView() { }
+
+	static POgreBillboardView Create(PLevelObject model);
+	virtual POgreView Copy(PLevelObject model);
+
+	/** Set Material */
+	virtual void SetMaterial(const std::string& material);
+	virtual const std::string& GetMaterial() const { return sMaterial; }
+
+	/** Set Billboard Direction; call with Ogre::Vector3::ZERO to enable all axes for rotation */
+	virtual void SetDirection(const Ogre::Vector3& direction);
+
+	/** Implementation of IOgreView */
+	virtual void SetOgreScene(Ogre::SceneManager* ogreScene, Ogre::SceneNode* ogreNode = NULL);
+	/** Implementation of CLevelObject::Listener */
+	virtual void BoundsChanged(CLevelObject*);
+	/** Implementation of CLevelObject::Listener */
+	virtual void PositionChanged(CLevelObject*);
+protected:
+	COgreBillboardView(PLevelObject model);
+	virtual void UpdateGeometry();
+
+	std::string sMaterial;
+	Ogre::Billboard* oBillboard;
+	Ogre::BillboardSet* oBillboardSet;
+	Ogre::Vector3 billboardDirection;
+};
+
 }
 }
 

File src/States/CEditorState.cpp

 		CLog::Get().Write("error loading templates", CLog::logRelease);
 }
 
-void CEditorState::ReadTemplate(std::string name, GameObjects::PLevelObject object, GameObjects::POgreEntityView view) {
+void CEditorState::ReadTemplate(std::string name, GameObjects::PLevelObject object, GameObjects::POgreView view) {
+	// workaround: currently only ogreentityview is supported
+	GameObjects::POgreEntityView entview = boost::dynamic_pointer_cast<GameObjects::COgreEntityView>(view);
+	if (!entview) return;
+
 	this->templates[name].first = object;
-	this->templates[name].second = view;
+	this->templates[name].second = entview;
 }
 
 void CEditorState::ReadLevelObject(GameObjects::PLevelObject object, GameObjects::ObjectType layer) {
 	this->GetContainer(layer).push_back(object);
 }
 
-void CEditorState::ReadOgreEntityView(GameObjects::POgreEntityView view, GameObjects::ObjectType layer) {
-	this->GetViewContainer(layer).push_back(view);
+void CEditorState::ReadOgreView(GameObjects::POgreView view, GameObjects::ObjectType layer) {
+	// workaround: currently only ogreentityview is supported
+	GameObjects::POgreEntityView entview = boost::dynamic_pointer_cast<GameObjects::COgreEntityView>(view);
+	if (!entview) return;
+	this->GetViewContainer(layer).push_back(entview);
 }
 
 void CEditorState::LoadFile() {
 	// backup objects
 	for (OgreViewList::iterator it = staticViews.begin(); it != staticViews.end(); ++it) {
 		GameObjects::PLevelObject modelCopy = (*it)->GetModel()->Copy();
-		GameObjects::POgreEntityView viewCopy = (*it)->Copy(modelCopy);
+		GameObjects::POgreEntityView viewCopy = boost::dynamic_pointer_cast<GameObjects::COgreEntityView>( (*it)->Copy(modelCopy) );
 		backupObjects[*it].first = modelCopy;
 		backupObjects[*it].second = viewCopy;
 	}
 	for (OgreViewList::iterator it = backgroundViews.begin(); it != backgroundViews.end(); ++it) {
 		GameObjects::PLevelObject modelCopy = (*it)->GetModel()->Copy();
-		GameObjects::POgreEntityView viewCopy = (*it)->Copy(modelCopy);
+		GameObjects::POgreEntityView viewCopy = boost::dynamic_pointer_cast<GameObjects::COgreEntityView>( (*it)->Copy(modelCopy) );
 		backupObjects[*it].first = modelCopy;
 		backupObjects[*it].second = viewCopy;
 	}
 	for (OgreViewList::iterator it = dynamicViews.begin(); it != dynamicViews.end(); ++it) {
 		GameObjects::PLevelObject modelCopy = (*it)->GetModel()->Copy();
-		GameObjects::POgreEntityView viewCopy = (*it)->Copy(modelCopy);
+		GameObjects::POgreEntityView viewCopy = boost::dynamic_pointer_cast<GameObjects::COgreEntityView>( (*it)->Copy(modelCopy) );
 		backupObjects[*it].first = modelCopy;
 		backupObjects[*it].second = viewCopy;
 	}
 
 	// copy template
 	GameObjects::PLevelObject objModel = templateModel->Copy();
-	GameObjects::POgreEntityView objView = templateView->Copy(objModel);
+	GameObjects::POgreEntityView objView = boost::dynamic_pointer_cast<GameObjects::COgreEntityView>( templateView->Copy(objModel) );
 
 	objView->SetOgreScene(ogreScene, ogreLevelNode);
 
 
 	// make backup copy
 	GameObjects::PLevelObject modelCopy = objModel->Copy();
-	GameObjects::POgreEntityView viewCopy = objView->Copy(modelCopy);
+	GameObjects::POgreEntityView viewCopy = boost::dynamic_pointer_cast<GameObjects::COgreEntityView>( objView->Copy(modelCopy) );
 	backupObjects[objView].first = modelCopy;
 	backupObjects[objView].second = viewCopy;
 

File src/States/CEditorState.h

 		return PEditorState(new CEditorState());
 	}
 
-	virtual void ReadTemplate(std::string name, GameObjects::PLevelObject object, GameObjects::POgreEntityView view);
+	virtual void ReadTemplate(std::string name, GameObjects::PLevelObject object, GameObjects::POgreView view);
 	virtual void ReadLevelObject(GameObjects::PLevelObject object, GameObjects::ObjectType layer);
-	virtual void ReadOgreEntityView(GameObjects::POgreEntityView view, GameObjects::ObjectType layer);
+	virtual void ReadOgreView(GameObjects::POgreView view, GameObjects::ObjectType layer);
 
 private:
 	typedef std::vector<GameObjects::PLevelObject> LevelObjectList;

File src/States/CGameState.cpp

 
 	ogreScene->setSkyBox(true, "Textures/SkyBox", 5000, true, Ogre::Quaternion(Ogre::Degree(fSkyRotation), Ogre::Vector3::UNIT_Y));
 
-	ogreCamera->setNearClipDistance(0.1);
+	ogreCamera->setNearClipDistance(0.1f);
 	ogreCamera->setFarClipDistance(0); // infinity
 
 	// create ogre light