Explorar o código

Scene
- Add property classes
- Modify a few scene components

Util
- Add observer classes

Panagiotis Christopoulos Charitos %!s(int64=14) %!d(string=hai) anos
pai
achega
fd7b7247f4
Modificáronse 6 ficheiros con 399 adicións e 8 borrados
  1. 10 0
      anki/scene/Movable.cpp
  2. 5 3
      anki/scene/Movable.h
  3. 254 0
      anki/scene/Property.h
  4. 3 0
      anki/scene/SceneNode.cpp
  5. 24 5
      anki/scene/SceneNode.h
  6. 103 0
      anki/util/Observer.h

+ 10 - 0
anki/scene/Movable.cpp

@@ -1,9 +1,19 @@
 #include "anki/scene/Movable.h"
+#include "anki/scene/Property.h"
 
 
 namespace anki {
 
 
+//==============================================================================
+Movable::Movable(uint flags_, Movable* parent, PropertyMap& pmap)
+	: Base(this, parent), flags(flags_)
+{
+	pmap.addProperty("localTransform", &lTrf, PropertyBase::PF_READ_WRITE);
+	pmap.addProperty("worldTransform", &wTrf, PropertyBase::PF_READ);
+}
+
+
 //==============================================================================
 void Movable::updateWorldTransform()
 {

+ 5 - 3
anki/scene/Movable.h

@@ -8,6 +8,9 @@
 namespace anki {
 
 
+class PropertyMap;
+
+
 /// @addtogroup Scene
 /// @{
 
@@ -30,9 +33,8 @@ public:
 	/// The one and only constructor
 	/// @param flags_ The flags
 	/// @param parent The parent. It can be nullptr
-	Movable(uint flags_, Movable* parent)
-		: Base(this, parent), flags(flags_)
-	{}
+	/// @param pmap Property map to add a few variables
+	Movable(uint flags_, Movable* parent, PropertyMap& pmap);
 	/// @}
 
 	/// @name Accessors

+ 254 - 0
anki/scene/Property.h

@@ -0,0 +1,254 @@
+#ifndef ANKI_SCENE_PROPERTY_H
+#define ANKI_SCENE_PROPERTY_H
+
+#include "anki/util/Observer.h"
+#include "anki/util/Exception.h"
+#include "anki/util/Assert.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/unordered_map.hpp>
+
+
+namespace anki {
+
+
+// Forward
+template<typename T>
+class Property;
+
+
+/// Base class for property
+class PropertyBase
+{
+public:
+	/// Flags for property
+	enum PropertyFlag
+	{
+		PF_NONE = 0,
+		PF_READ = 1,
+		PF_WRITE = 2,
+		PF_READ_WRITE = PF_READ | PF_WRITE
+	};
+
+	/// @name Constructors/Destructor
+	/// @{
+	PropertyBase(const char* name_, uint tid_, uint flags_ = PF_NONE)
+		: name(name_), tid(tid_), flags(flags_)
+	{
+		ANKI_ASSERT(tid != 0);
+	}
+
+	virtual ~PropertyBase()
+	{}
+	/// @}
+
+	/// @name Accessors
+	/// @{
+	const std::string& getName() const
+	{
+		return name;
+	}
+
+	uint getTypeId() const
+	{
+		return tid;
+	}
+
+	template<typename T>
+	const T& getValue() const
+	{
+		checkType<T>();
+		return static_cast<const Property<T>*>(this)->getValue();
+	}
+
+	template<typename T>
+	void setValue(const T& x)
+	{
+		checkType<T>();
+		return static_cast<Property<T>*>(this)->setValue(x);
+	}
+
+	bool isFlagEnabled(PropertyFlag flag) const
+	{
+		return flags & flag;
+	}
+	/// @}
+
+protected:
+	void enableFlag(PropertyFlag flag, bool enable = true)
+	{
+		flags = enable ? flags | flag : flags & ~flag;
+	}
+	void disableFlag(PropertyFlag flag)
+	{
+		enableFlag(flag, false);
+	}
+
+private:
+	std::string name;
+	uint tid;
+	uint flags;
+
+	/// Runtime checking of type
+	template<typename T>
+	void checkType() const
+	{
+		if(Property<T>::TYPE_ID != getTypeId())
+		{
+			throw ANKI_EXCEPTION("Types do not match: " + name);
+		}
+	}
+};
+
+
+/// Property. It holds a pointer to a value
+template<typename T>
+class Property: public PropertyBase
+{
+public:
+	typedef T Value;
+	typedef Property<Value> Self;
+
+	static const uint TYPE_ID; ///< Unique id for every type of property
+
+	/// @name Constructors/Destructor
+	/// @{
+
+	/// Read only property
+	Property(const char* name, const Value* x, uint flags = PF_NONE)
+		: PropertyBase(name, TYPE_ID, flags), ptr(x)
+	{
+		disableFlag(PF_WRITE);
+	}
+
+	/// Read/write property
+	Property(const char* name, Value* x, uint flags = PF_NONE)
+		: PropertyBase(name, TYPE_ID, flags), ptr(x)
+	{}
+	/// @}
+
+	/// @name Accessors
+	/// @{
+	const Value& getValue() const
+	{
+		if(!isFlagEnabled(PF_READ))
+		{
+			throw ANKI_EXCEPTION("Property is not readable: " + name);
+		}
+
+		return *ptr;
+	}
+
+	/// Set the value and emit the signal valueChanged
+	void setValue(const Value& x)
+	{
+		if(!isFlagEnabled(PF_WRITE))
+		{
+			throw ANKI_EXCEPTION("Property is not writable: " + name);
+		}
+
+		*(const_cast<Value*>(ptr)) = x;
+		ANKI_EMIT valueChanged(x);
+	}
+	/// @}
+
+	ANKI_SIGNAL(const Value&, valueChanged)
+
+private:
+	const Value* ptr; ///< Have only one const pointer for size saving
+};
+
+
+template<typename T>
+const uint Property<T>::TYPE_ID = 0;
+
+
+/// A set of properties
+class PropertyMap
+{
+public:
+	typedef boost::ptr_vector<PropertyBase> Container;
+	typedef boost::unordered_map<std::string, PropertyBase*> NameToPropertyMap;
+
+
+	/// Create a new property
+	template<typename T>
+	Property<T>& addProperty(const char* name, T* x,
+		uint flags = PropertyBase::PF_NONE)
+	{
+		if(propertyExists(name))
+		{
+			throw ANKI_EXCEPTION("Property already exists: " + name);
+		}
+
+		Property<T>* newp = new Property<T>(name, x, flags);
+
+		props.push_back(newp);
+		map[name] = newp;
+
+		return *newp;
+	}
+
+
+	/// XXX
+	const PropertyBase& findPropertyBaseByName(const char* name) const
+	{
+		NameToPropertyMap::const_iterator it = map.find(name);
+		if(it == map.end())
+		{
+			throw ANKI_EXCEPTION("Property not found: " + name);
+		}
+		return *(it->second);
+	}
+
+
+	/// XXX
+	PropertyBase& findPropertyBaseByName(const char* name)
+	{
+		NameToPropertyMap::iterator it = map.find(name);
+		if(it == map.end())
+		{
+			throw ANKI_EXCEPTION("Property not found: " + name);
+		}
+		return *(it->second);
+	}
+
+
+	/// Alias for findPropertyBaseByName
+	const PropertyBase& operator[](const char* name) const
+	{
+		return findPropertyBaseByName(name);
+	}
+
+
+	/// Alias for findPropertyBaseByName
+	PropertyBase& operator[](const char* name)
+	{
+		return findPropertyBaseByName(name);
+	}
+
+
+	/// Return true if the property named @a name exists
+	bool propertyExists(const char* name) const
+	{
+		return map.find(name) != map.end();
+	}
+
+
+	/// XXX
+	template<typename T>
+	void setValue(const char* name, const T& v)
+	{
+		findPropertyBaseByName(name).setValue<T>(v);
+	}
+
+private:
+	Container props;
+	NameToPropertyMap map;
+};
+
+
+} // namespace anki
+
+
+#endif

+ 3 - 0
anki/scene/SceneNode.cpp

@@ -9,6 +9,9 @@ namespace anki {
 SceneNode::SceneNode(const char* name, Scene* scene)
 {
 	scene->registerNode(this);
+
+	/// Add the first property
+	pmap.addProperty("name", &name, PropertyBase::PF_READ);
 }
 
 

+ 24 - 5
anki/scene/SceneNode.h

@@ -1,6 +1,7 @@
 #ifndef ANKI_SCENE_SCENE_NODE_H
 #define ANKI_SCENE_SCENE_NODE_H
 
+#include "anki/scene/Property.h"
 #include <string>
 
 
@@ -22,7 +23,7 @@ class Spatial;
 class SceneNode
 {
 public:
-	/// @name Constructors
+	/// @name Constructors/Destructor
 	/// @{
 
 	/// The one and only constructor
@@ -31,12 +32,29 @@ public:
 	explicit SceneNode(
 		const char* name,
 		Scene* scene);
-	/// @}
 
 	/// Unregister node
 	virtual ~SceneNode();
+	/// @}
+
+	/// @name Accessors
+	/// @{
+	const std::string& getName() const
+	{
+		return name;
+	}
+
+	const PropertyMap& getPropertyMap() const
+	{
+		return pmap;
+	}
+	PropertyMap& getPropertyMap()
+	{
+		return pmap;
+	}
+	/// @}
 
-	/// @name Accessors of properties
+	/// @name Accessors of components
 	/// @{
 	virtual Movable* getMovable()
 	{
@@ -59,7 +77,7 @@ public:
 	}
 	/// @}
 
-	/// This is called by the scene every frame
+	/// This is called by the scene every frame. But default it does nothing
 	virtual void frameUpdate(float prevUpdateTime, float crntTime)
 	{
 		(void)prevUpdateTime;
@@ -68,7 +86,8 @@ public:
 
 private:
 	std::string name; ///< A unique name
-	Scene* scene; ///< For registering and unregistering
+	Scene* scene; ///< Keep it here for unregistering
+	PropertyMap pmap;
 };
 /// @}
 

+ 103 - 0
anki/util/Observer.h

@@ -0,0 +1,103 @@
+#ifndef ANKI_UTIL_OBSERVER_H
+#define ANKI_UTIL_OBSERVER_H
+
+#include <boost/ptr_container/ptr_vector.hpp>
+
+
+namespace anki {
+
+
+/// The observer interface template
+template<typename T>
+struct Observer
+{
+	typedef T Value; ///< The type of the notification value
+
+	virtual void notify(Value notificationVal) = 0;
+};
+
+
+/// Basically a container of observers
+template<typename T>
+class Observable
+{
+public:
+	typedef T Value;
+	typedef Observer<Value> ObserverType;
+	typedef boost::ptr_vector<ObserverType> Container;
+
+	/// Add a new observer. The Observable takes ownership of the
+	/// pointer and its responsible of cleaning
+	void addNewObserver(ObserverType* x)
+	{
+		observers.push_back(x);
+	}
+
+	/// Notify all observers
+	void notifyAll(Value x)
+	{
+		for(typename Container::iterator it = observers.begin();
+			it != observers.end(); ++it)
+		{
+			(*it).notify(x);
+		}
+	}
+
+	/// Alias to notifyAll
+	void operator()(Value x)
+	{
+		notifyAll(x);
+	}
+
+private:
+	Container observers;
+};
+
+
+/// An over-qualified observer
+template<typename ObservingType, typename Value,
+	void (ObservingType::*method)(Value)>
+struct Observing: Observer<Value>
+{
+	ObservingType* reveiver;
+
+	Observing(ObservingType* reveiver_)
+		: reveiver(reveiver_)
+	{}
+
+	void notify(Value x)
+	{
+		(reveiver->*method)(x);
+	}
+};
+
+
+/// If a class has slots it should include this
+#define ANKI_OBSERVING(_class) \
+	typedef _class ObservingType;
+
+
+/// Define a signal
+#define ANKI_SIGNAL(_type, _name) \
+	Observable<_type> _name;
+
+
+/// It doesn't do anything. Its purpose is to make the code more understandable
+#define ANKI_EMIT this->
+
+
+/// Define a slot. This should follow the method declaration
+#define ANKI_SLOT(_name, _type) \
+	typedef Observing<ObservingType, _type, &ObservingType::_name> \
+		Observing_##_name;
+
+
+/// Connect a signal to a slot
+#define ANKI_CONNECT(_sender, _signal, _reveiver, _slot) \
+	 (_sender)->_signal.addNewObserver(new Observing_##_slot(_reveiver))
+
+
+} // namespace anki
+
+
+#endif