Explorar o código

Scene view frame object functionality

BearishSun %!s(int64=10) %!d(string=hai) anos
pai
achega
dab2b32982

+ 672 - 672
BansheeCore/Include/BsSceneObject.h

@@ -1,673 +1,673 @@
-#pragma once
-
-#include "BsCorePrerequisites.h"
-#include "BsMatrix4.h"
-#include "BsVector3.h"
-#include "BsQuaternion.h"
-#include "BsRTTIType.h"
-#include "BsGameObjectManager.h"
-#include "BsGameObject.h"
-#include "BsComponent.h"
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Possible modifiers that can be applied to a SceneObject
-	 */
-	enum SceneObjectFlags
-	{
-		SOF_DontInstantiate = 0x01, /**< Object wont be in the main scene and its components won't receive updates. */
-		SOF_DontSave = 0x02,		/**< Object will be skipped when saving the scene hierarchy or a prefab. */
-		SOF_Persistent = 0x04,		/**< Object will remain in the scene even after scene clear, unless destroyed directly. 
-										 This only works with top-level objects. */
-		SOF_Internal = 0x08			/**< Provides a hint to external systems that his object is used by engine internals.
-									     For example, those systems might not want to display those objects together with the
-										 user created ones. */
-	};
-
-	/**
-	 * @brief	An object in the scene graph. It has a world position, place in the hierarchy and 
-	 * 			optionally a number of attached components.
-	 */
-	class BS_CORE_EXPORT SceneObject : public GameObject
-	{
-		/**
-		 * @brief	Flags that signify which part of the SceneObject needs updating.
-		 */
-		enum DirtyFlags
-		{
-			LocalTfrmDirty = 0x01,
-			WorldTfrmDirty = 0x02
-		};
-
-		friend class CoreSceneManager;
-		friend class Prefab;
-		friend class PrefabDiff;
-		friend class PrefabUtility;
-	public:
-		~SceneObject();
-
-		/**
-		 * @brief	Creates a new SceneObject with the specified name. Object will be placed in the top
-		 *			of the scene hierarchy.
-		 *
-		 * @param	name	Name of the scene object.
-		 * @param	flags	Optional flags that control object behavior. See SceneObjectFlags.
-		 */
-		static HSceneObject create(const String& name, UINT32 flags = 0);
-
-		/**
-		 * @brief	Destroys this object and any of its held components.
-		 *
-		 * @param [in]	immediate	If true, the object will be deallocated and become unusable
-		 *							right away. Otherwise the deallocation will be delayed to the end of
-		 *							frame (preferred method).
-		 */
-		void destroy(bool immediate = false);
-
-		/**
-		 * @copydoc	GameObject::_setInstanceData
-		 */
-		void _setInstanceData(GameObjectInstanceDataPtr& other) override;
-
-		/**
-		 * @brief	Returns a handle to this object.
-		 */
-		HSceneObject getHandle() const { return mThisHandle; }
-
-		/**
-		 * @brief	Returns the UUID of the prefab this object is linked to, if any. 
-		 *
-		 * @note	Requires a search of all parents potentially.
-		 */
-		String getPrefabLink() const;
-
-		/**
-		 * @brief	Breaks the link between this prefab instance and its prefab. Object will retain 
-		 *			all current values but will no longer be influenced by modifications to its parent prefab.
-		 */
-		void breakPrefabLink();
-
-		/**
-		 * @brief	Checks if the scene object has a specific bit flag set.
-		 */
-		bool hasFlag(UINT32 flag) const;
-
-		/**
-		 * @brief	Register the scene object with the scene and activate all of its components.
-		 * 		
-		 * @note	Internal method.
-		 */
-		void _instantiate();
-
-		/**
-		 * @brief	Clears the internally stored prefab diff. If this object is updated from prefab its instance specific
-		 * 			changes will be lost.
-		 * 			
-		 * @note	Internal method.
-		 */
-		void _clearPrefabDiff() { mPrefabDiff = nullptr; }
-	private:
-		SceneObject(const String& name, UINT32 flags);
-
-		/**
-		 * @brief	Creates a new SceneObject instance, registers it with the game object manager,
-		 *			creates and returns a handle to the new object.
-		 *
-		 * @note	When creating objects with DontInstantiate flag it is the callers responsibility
-		 *			to manually destroy the object, otherwise it will leak.
-		 */
-		static HSceneObject createInternal(const String& name, UINT32 flags = 0);
-
-		/**
-		 * @brief	Creates a new SceneObject instance from an existing pointer, registers it with the game object manager,
-		 *			creates and returns a handle to the object.
-		 *			
-		 * @param	soPtr		Pointer to the scene object register and return a handle to.
-		 * @param	originalId	If the provided pointer was deserialized, this is the original object's ID at the time
-		 * 						of serialization. Used for resolving handles pointing to the object.
-		 */
-		static HSceneObject createInternal(const SPtr<SceneObject>& soPtr, UINT64 originalId = 0);
-
-		/**
-		 * @brief	Destroys this object and any of its held components.
-		 *
-		 * @param [in]	handle		Game object handle to this object.
-		 * @param [in]	immediate	If true, the object will be deallocated and become unusable
-		 *							right away. Otherwise the deallocation will be delayed to the end of
-		 *							frame (preferred method).
-		 *
-		 * @note	Unlike "destroy", does not remove the object from its parent.
-		 */
-		void destroyInternal(GameObjectHandleBase& handle, bool immediate = false) override;
-
-		/**
-		 * @brief	Recursively enables the provided set of flags on
-		 *			this object and all children.
-		 */
-		void setFlags(UINT32 flags);
-
-		/**
-		 * @brief	Recursively disables the provided set of flags on
-		 *			this object and all children.
-		 */
-		void unsetFlags(UINT32 flags);
-
-		/**
-		 * @brief	Checks is the scene object instantiated and visible in the scene.
-		 */
-		bool isInstantiated() const { return (mFlags & SOF_DontInstantiate) == 0; }
-
-	private:
-		HSceneObject mThisHandle;
-		String mPrefabLinkUUID;
-		PrefabDiffPtr mPrefabDiff;
-		UINT32 mPrefabHash;
-		UINT32 mFlags;
-
-		/************************************************************************/
-		/* 								Transform	                     		*/
-		/************************************************************************/
-	public:
-		/**
-		 * @brief	Sets the local position of the object.
-		 */
-		void setPosition(const Vector3& position);
-
-		/**
-		 * @brief	Gets the local position of the object.
-		 */
-		const Vector3& getPosition() const { return mPosition; }
-
-		/**
-		 * @brief	Sets the world position of the object.
-		 */
-		void setWorldPosition(const Vector3& position);
-
-		/**
-		 * @brief	Gets the world position of the object.
-		 *
-		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
-		 */
-		const Vector3& getWorldPosition() const;
-
-		/**
-		 * @brief	Sets the local rotation of the object.
-		 */
-		void setRotation(const Quaternion& rotation);
-
-		/**
-		 * @brief	Gets the local rotation of the object.
-		 */
-		const Quaternion& getRotation() const { return mRotation; }
-
-		/**
-		 * @brief	Sets the world rotation of the object.
-		 */
-		void setWorldRotation(const Quaternion& rotation);
-
-		/**
-		 * @brief	Gets world rotation of the object.
-		 *
-		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
-		 */
-		const Quaternion& getWorldRotation() const;
-
-		/**
-		 * @brief	Sets the local scale of the object.
-		 */
-		void setScale(const Vector3& scale);
-
-		/**
-		 * @brief	Gets the local scale of the object.
-		 */
-		const Vector3& getScale() const { return mScale; }
-
-		/**
-		 * @brief	Sets the world scale of the object.
-		 *
-		 * @note	This will not work properly if this object or any of its parents
-		 *			have non-affine transform matrices.
-		 */
-		void setWorldScale(const Vector3& scale);
-
-		/**
-		 * @brief	Gets world scale of the object.
-		 *
-		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
-		 */
-		const Vector3& getWorldScale() const;
-
-		/**
-		 * @brief	Orients the object so it is looking at the provided \p location (local space)
-		 *			where \p up is used for determining the location of the object's Y axis.
-		 *
-		 */
-		void lookAt(const Vector3& location, const Vector3& up = Vector3::UNIT_Y);
-
-		/**
-		 * @brief	Gets the objects world transform matrix.
-		 *
-		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
-		 */
-		const Matrix4& getWorldTfrm() const;
-
-		/**
-		 * @brief	Gets the objects local transform matrix.
-		 */
-		const Matrix4& getLocalTfrm() const;
-
-		/**
-		 * @brief	Moves the object's position by the vector offset provided along world axes.
-		 */
-        void move(const Vector3& vec);
-
-		/**
-		 * @brief	Moves the object's position by the vector offset provided along it's own axes (relative to orientation).
-		 */
-        void moveRelative(const Vector3& vec);
-
-		/**
-		 * @brief	Gets the Z (forward) axis of the object, in world space.
-		 *
-		 * @return	Forward axis of the object.
-		 */
-		Vector3 getForward() const { return getWorldRotation().rotate(-Vector3::UNIT_Z); }
-
-		/**
-		 * @brief	Gets the Y (up) axis of the object, in world space.
-		 *
-		 * @return	Up axis of the object.
-		 */
-		Vector3 getUp() const { return getWorldRotation().rotate(Vector3::UNIT_Y); }
-
-		/**
-		 * @brief	Gets the X (right) axis of the object, in world space.
-		 *
-		 * @return	Right axis of the object.
-		 */
-		Vector3 getRight() const { return getWorldRotation().rotate(Vector3::UNIT_X); }
-
-		/**
-		 * @brief	Rotates the game object so it's forward axis faces the provided
-		 * 			direction.
-		 * 			
-		 * @note	Local forward axis is considered to be negative Z.
-		 *
-		 * @param	forwardDir	The forward direction to face, in world space.
-		 */
-		void setForward(const Vector3& forwardDir);
-
-		/**
-		 * @brief	Rotate the object around an arbitrary axis.
-		 */
-        void rotate(const Vector3& axis, const Radian& angle);
-
-		/**
-		 * @brief	Rotate the object around an arbitrary axis using a Quaternion.
-		 */
-        void rotate(const Quaternion& q);
-
-		/**
-		 * @brief	Rotates around local Z axis.
-		 *
-		 * @param	angle	Angle to rotate by.
-		 */
-		void roll(const Radian& angle);
-
-		/**
-		 * @brief	Rotates around Y axis.
-		 *
-		 * @param	angle	Angle to rotate by.
-		 */
-		void yaw(const Radian& angle);
-
-		/**
-		 * @brief	Rotates around X axis
-		 *
-		 * @param	angle	Angle to rotate by.
-		 */
-		void pitch(const Radian& angle);
-
-		/**
-		 * @brief	Forces any dirty transform matrices on this object to be updated.
-		 *
-		 * @note	Normally this is done internally when retrieving a transform, but sometimes
-		 *			it is useful to update transforms manually.
-		 */
-		void updateTransformsIfDirty();
-
-		/**
-		 * @brief	Returns a hash value that changes whenever a scene objects
-		 *			transform gets updated. It allows you to detect changes with
-		 *			the local or world transforms without directly comparing their
-		 *			values with some older state.
-		 */
-		UINT32 getTransformHash() const { return mDirtyHash; }
-
-	private:
-		Vector3 mPosition;
-		Quaternion mRotation;
-		Vector3 mScale;
-
-		mutable Vector3 mWorldPosition;
-		mutable Quaternion mWorldRotation;
-		mutable Vector3 mWorldScale;
-
-		mutable Matrix4 mCachedLocalTfrm;
-		mutable Matrix4 mCachedWorldTfrm;
-
-		mutable UINT32 mDirtyFlags;
-		mutable UINT32 mDirtyHash;
-
-		/**
-		 * @brief	Marks the transform as dirty so that we know to update
-		 *			it when the transform is requested.
-		 */
-		void markTfrmDirty() const;
-
-		/**
-		 * @brief	Updates the local transform. Normally just reconstructs the 
-		 *			transform matrix from the position/rotation/scale.
-		 */
-		void updateLocalTfrm() const;
-
-		/**
-		 * @brief	Updates the world transform. Reconstructs the local transform
-		 *			matrix and multiplies it with any parent transforms.
-		 *
-		 * @note	If parent transforms are dirty they will be updated.
-		 */
-		void updateWorldTfrm() const;
-
-		/**
-		 * @brief	Checks if cached local transform needs updating.
-		 */
-		bool isCachedLocalTfrmUpToDate() const { return (mDirtyFlags & DirtyFlags::LocalTfrmDirty) == 0; }
-
-		/**
-		 * @brief	Checks if cached world transform needs updating.
-		 */
-		bool isCachedWorldTfrmUpToDate() const { return (mDirtyFlags & DirtyFlags::WorldTfrmDirty) == 0; }
-
-		/************************************************************************/
-		/* 								Hierarchy	                     		*/
-		/************************************************************************/
-	public:
-		/**
-		 * @brief	Changes the parent of this object. Also removes the object from the current parent,
-		 * 			and assigns it to the new parent.
-		 *
-		 * @param [in]	parent			New parent.
-		 * @param [in]	keepWorldPos	Determines should the current transform be maintained even after the parent is changed
-		 * 								(this means the local transform will be modified accordingly).
-		 */
-		void setParent(const HSceneObject& parent, bool keepWorldTransform = true);
-
-		/**
-		 * @brief	Gets the parent of this object.
-		 *
-		 * @return	Parent object, or nullptr if this SceneObject is at root level.
-		 */
-		HSceneObject getParent() const { return mParent; }
-
-		/**
-		 * @brief	Gets a child of this item.
-		 *
-		 * @param	idx	The zero based index of the child.
-		 *
-		 * @return	SceneObject of the child.
-		 */
-		HSceneObject getChild(UINT32 idx) const;
-
-		/**
-		 * @brief	Find the index of the specified child. Don't persist this value as
-		 * 			it may change whenever you add/remove children.
-		 *
-		 * @param	child	The child to look for.
-		 *
-		 * @return	The zero-based index of the found child, or -1 if no match was found.
-		 */
-		int indexOfChild(const HSceneObject& child) const;
-
-		/**
-		 * @brief	Gets the number of all child GameObjects.
-		 */
-		UINT32 getNumChildren() const { return (UINT32)mChildren.size(); }
-
-		/**
-		 * @brief	Searches the child objects for an object matching the specified name.
-		 *
-		 * @param	name		Name of the object to locate.
-		 * @param	recursive	If true all descendants of the scene object will be searched, 
-		 *						otherwise only immediate children.
-		 *
-		 * @returns	First found scene object, or empty handle if none found.
-		 */
-		HSceneObject findChild(const String& name, bool recursive = true);
-
-		/**
-		 * @brief	Searches the child objects for objects matching the specified name.
-		 *
-		 * @param	name		Name of the objects to locate.
-		 * @param	recursive	If true all descendants of the scene object will be searched, 
-		 *						otherwise only immediate children.
-		 *
-		 * @returns	All scene objects matching the specified name.
-		 */
-		Vector<HSceneObject> findChildren(const String& name, bool recursive = true);
-
-		/**
-		 * @brief	Enables or disables this object. Disabled objects also implicitly disable
-		 *			all their child objects. No components on the disabled object are updated.
-		 */
-		void setActive(bool active);
-
-		/**
-		 * @brief	Returns whether or not an object is active.
-		 *
-		 * @param	self	If true, the method will only check if this particular object was activated
-		 *					or deactivated directly via setActive. If false we we also check if any of
-		 *					the objects parents are inactive.
-		 */
-		bool getActive(bool self = false);
-
-		/**
-		 * @brief	Makes a deep copy of this object.
-		 * 			
-		 * @param	instantiate	If false, the cloned hierarchy will just be a memory copy, but will not be present in the
-		 * 						scene or otherwise active until ::instantiate() is called.
-		 */
-		HSceneObject clone(bool instantiate = true);
-
-	private:
-		HSceneObject mParent;
-		Vector<HSceneObject> mChildren;
-		bool mActiveSelf;
-		bool mActiveHierarchy;
-
-		/**
-		 * @brief	Internal version of ::setParent that allows you to set a null parent.
-		 *
-		 * @param [in]	parent			New parent.
-		 * @param [in]	keepWorldPos	Determines should the current transform be maintained even after the parent is changed
-		 * 								(this means the local transform will be modified accordingly).
-		 */
-		void _setParent(const HSceneObject& parent, bool keepWorldTransform = true);
-
-		/**
-		 * @brief	Adds a child to the child array. This method doesn't check for null or duplicate values.
-		 *
-		 * @param [in]	object	New child.
-		 */
-		void addChild(const HSceneObject& object);
-		
-		/**
-		 * @brief	Removes the child from the object. 
-		 *
-		 * @param [in]	object	Child to remove.
-		 * 					
-		 * @throws INTERNAL_ERROR If the provided child isn't a child of the current object.
-		 */
-		void removeChild(const HSceneObject& object);
-
-		/**
-		 * @brief	Changes the object active in hierarchy state.
-		 */
-		void setActiveHierarchy(bool active);
-
-		/************************************************************************/
-		/* 								Component	                     		*/
-		/************************************************************************/
-	public:
-		/**
-		 * @brief	Constructs a new component of the specified type and adds it to 
-		 *			the internal component list.
-		 */
-		template<class T, class... Args>
-		GameObjectHandle<T> addComponent(Args &&... args)
-		{
-			static_assert((std::is_base_of<BansheeEngine::Component, T>::value),
-				"Specified type is not a valid Component.");
-
-			std::shared_ptr<T> gameObject(new (bs_alloc<T>()) T(mThisHandle,
-				std::forward<Args>(args)...),
-				&bs_delete<T>, StdAlloc<T>());
-
-			for (auto& component : mComponents)
-			{
-				if (component->typeEquals(*gameObject))
-					return component; // Component of this type already exists
-			}
-
-			GameObjectHandle<T> newComponent =
-				GameObjectManager::instance().registerObject(gameObject);
-
-			mComponents.push_back(newComponent);
-
-			if (isInstantiated())
-			{
-				newComponent->instantiate();
-				newComponent->onInitialized();
-
-				if (getActive())
-					newComponent->onEnabled();
-			}
-
-			return newComponent;
-		}
-
-		/**
-		 * @brief	Searches for a component with the specific type and returns the first one
-		 * 			it finds. 
-		 * 			
-		 * @note	Don't call this too often as it is relatively slow. It is more efficient 
-		 * 			to call it once and store the result for further use.
-		 *
-		 * @tparam	typename T	Type of the component.
-		 *
-		 * @return	Component if found, nullptr otherwise.
-		 */
-		template <typename T>
-		GameObjectHandle<T> getComponent()
-		{
-			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), 
-				"Specified type is not a valid Component.");
-
-			return static_object_cast<T>(getComponent(T::getRTTIStatic()->getRTTIId()));
-		}
-
-		/**
-		 * @brief	Checks if the current object contains the specified component
-		 * 			 			
-		 * @note	Don't call this too often as it is relatively slow. 
-		 *
-		 * @tparam	typename T	Type of the component.
-		 *
-		 * @return	True if component exists on the object.
-		 */
-		template <typename T>
-		bool hasComponent()
-		{
-			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), 
-				"Specified type is not a valid Component.");
-
-			for (auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
-			{
-				if ((*iter)->getRTTI()->getRTTIId() == T::getRTTIStatic()->getRTTIId())
-					return true;
-			}
-
-			return false;
-		}
-
-		/**
-		 * @brief	Searches for a component with the specified type id and returns the first one it
-		 * 			finds.
-		 * 			
-		 * 			@note	Don't call this too often as it is relatively slow. It is more efficient to
-		 * 			call it once and store the result for further use.
-		 *
-		 * @param	typeId	Identifier for the type.
-		 *
-		 * @return	Component if found, nullptr otherwise.
-		 */
-		HComponent getComponent(UINT32 typeId) const;
-
-		/**
-		 * @brief	Removes the component from this object, and deallocates it.
-		 *
-		 * @param [in]	component	The component to destroy.
-		 * @param [in]	immediate	If true, the component will be deallocated and become unusable
-		 *							right away. Otherwise the deallocation will be delayed to the end of
-		 *							frame (preferred method).
-		 */
-		void destroyComponent(const HComponent component, bool immediate = false);
-
-		/**
-		 * @brief	Removes the component from this object, and deallocates it.
-		 *
-		 * @param [in]	component	The component to destroy.
-		 * @param [in]	immediate	If true, the component will be deallocated and become unusable
-		 *							right away. Otherwise the deallocation will be delayed to the end of
-		 *							frame (preferred method).
-		 */
-		void destroyComponent(Component* component, bool immediate = false);
-
-		/**
-		 * @brief	Returns all components on this object.
-		 */
-		const Vector<HComponent>& getComponents() const { return mComponents; }
-
-	private:
-		/**
-		 * @brief	Creates an empty component with the default constructor.
-		 */
-		template <typename T>
-		static std::shared_ptr<T> createEmptyComponent()
-		{
-			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), "Specified type is not a valid Component.");
-
-			T* rawPtr = new (bs_alloc<T>()) T();
-			std::shared_ptr<T> gameObject(rawPtr, &bs_delete<T>, StdAlloc<T>());
-
-			return gameObject;
-		}
-
-		/**
-		 * @brief	Adds the component to the internal component array.
-		 */
-		void addComponentInternal(const std::shared_ptr<Component> component);
-
-		Vector<HComponent> mComponents;
-
-		/************************************************************************/
-		/* 								RTTI		                     		*/
-		/************************************************************************/
-	public:
-		friend class GameObjectRTTI;
-		friend class SceneObjectRTTI;
-		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
-	};
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsMatrix4.h"
+#include "BsVector3.h"
+#include "BsQuaternion.h"
+#include "BsRTTIType.h"
+#include "BsGameObjectManager.h"
+#include "BsGameObject.h"
+#include "BsComponent.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Possible modifiers that can be applied to a SceneObject
+	 */
+	enum SceneObjectFlags
+	{
+		SOF_DontInstantiate = 0x01, /**< Object wont be in the main scene and its components won't receive updates. */
+		SOF_DontSave = 0x02,		/**< Object will be skipped when saving the scene hierarchy or a prefab. */
+		SOF_Persistent = 0x04,		/**< Object will remain in the scene even after scene clear, unless destroyed directly. 
+										 This only works with top-level objects. */
+		SOF_Internal = 0x08			/**< Provides a hint to external systems that his object is used by engine internals.
+									     For example, those systems might not want to display those objects together with the
+										 user created ones. */
+	};
+
+	/**
+	 * @brief	An object in the scene graph. It has a world position, place in the hierarchy and 
+	 * 			optionally a number of attached components.
+	 */
+	class BS_CORE_EXPORT SceneObject : public GameObject
+	{
+		/**
+		 * @brief	Flags that signify which part of the SceneObject needs updating.
+		 */
+		enum DirtyFlags
+		{
+			LocalTfrmDirty = 0x01,
+			WorldTfrmDirty = 0x02
+		};
+
+		friend class CoreSceneManager;
+		friend class Prefab;
+		friend class PrefabDiff;
+		friend class PrefabUtility;
+	public:
+		~SceneObject();
+
+		/**
+		 * @brief	Creates a new SceneObject with the specified name. Object will be placed in the top
+		 *			of the scene hierarchy.
+		 *
+		 * @param	name	Name of the scene object.
+		 * @param	flags	Optional flags that control object behavior. See SceneObjectFlags.
+		 */
+		static HSceneObject create(const String& name, UINT32 flags = 0);
+
+		/**
+		 * @brief	Destroys this object and any of its held components.
+		 *
+		 * @param [in]	immediate	If true, the object will be deallocated and become unusable
+		 *							right away. Otherwise the deallocation will be delayed to the end of
+		 *							frame (preferred method).
+		 */
+		void destroy(bool immediate = false);
+
+		/**
+		 * @copydoc	GameObject::_setInstanceData
+		 */
+		void _setInstanceData(GameObjectInstanceDataPtr& other) override;
+
+		/**
+		 * @brief	Returns a handle to this object.
+		 */
+		HSceneObject getHandle() const { return mThisHandle; }
+
+		/**
+		 * @brief	Returns the UUID of the prefab this object is linked to, if any. 
+		 *
+		 * @note	Requires a search of all parents potentially.
+		 */
+		String getPrefabLink() const;
+
+		/**
+		 * @brief	Breaks the link between this prefab instance and its prefab. Object will retain 
+		 *			all current values but will no longer be influenced by modifications to its parent prefab.
+		 */
+		void breakPrefabLink();
+
+		/**
+		 * @brief	Checks if the scene object has a specific bit flag set.
+		 */
+		bool hasFlag(UINT32 flag) const;
+
+		/**
+		 * @brief	Register the scene object with the scene and activate all of its components.
+		 * 		
+		 * @note	Internal method.
+		 */
+		void _instantiate();
+
+		/**
+		 * @brief	Clears the internally stored prefab diff. If this object is updated from prefab its instance specific
+		 * 			changes will be lost.
+		 * 			
+		 * @note	Internal method.
+		 */
+		void _clearPrefabDiff() { mPrefabDiff = nullptr; }
+	private:
+		SceneObject(const String& name, UINT32 flags);
+
+		/**
+		 * @brief	Creates a new SceneObject instance, registers it with the game object manager,
+		 *			creates and returns a handle to the new object.
+		 *
+		 * @note	When creating objects with DontInstantiate flag it is the callers responsibility
+		 *			to manually destroy the object, otherwise it will leak.
+		 */
+		static HSceneObject createInternal(const String& name, UINT32 flags = 0);
+
+		/**
+		 * @brief	Creates a new SceneObject instance from an existing pointer, registers it with the game object manager,
+		 *			creates and returns a handle to the object.
+		 *			
+		 * @param	soPtr		Pointer to the scene object register and return a handle to.
+		 * @param	originalId	If the provided pointer was deserialized, this is the original object's ID at the time
+		 * 						of serialization. Used for resolving handles pointing to the object.
+		 */
+		static HSceneObject createInternal(const SPtr<SceneObject>& soPtr, UINT64 originalId = 0);
+
+		/**
+		 * @brief	Destroys this object and any of its held components.
+		 *
+		 * @param [in]	handle		Game object handle to this object.
+		 * @param [in]	immediate	If true, the object will be deallocated and become unusable
+		 *							right away. Otherwise the deallocation will be delayed to the end of
+		 *							frame (preferred method).
+		 *
+		 * @note	Unlike "destroy", does not remove the object from its parent.
+		 */
+		void destroyInternal(GameObjectHandleBase& handle, bool immediate = false) override;
+
+		/**
+		 * @brief	Recursively enables the provided set of flags on
+		 *			this object and all children.
+		 */
+		void setFlags(UINT32 flags);
+
+		/**
+		 * @brief	Recursively disables the provided set of flags on
+		 *			this object and all children.
+		 */
+		void unsetFlags(UINT32 flags);
+
+		/**
+		 * @brief	Checks is the scene object instantiated and visible in the scene.
+		 */
+		bool isInstantiated() const { return (mFlags & SOF_DontInstantiate) == 0; }
+
+	private:
+		HSceneObject mThisHandle;
+		String mPrefabLinkUUID;
+		PrefabDiffPtr mPrefabDiff;
+		UINT32 mPrefabHash;
+		UINT32 mFlags;
+
+		/************************************************************************/
+		/* 								Transform	                     		*/
+		/************************************************************************/
+	public:
+		/**
+		 * @brief	Sets the local position of the object.
+		 */
+		void setPosition(const Vector3& position);
+
+		/**
+		 * @brief	Gets the local position of the object.
+		 */
+		const Vector3& getPosition() const { return mPosition; }
+
+		/**
+		 * @brief	Sets the world position of the object.
+		 */
+		void setWorldPosition(const Vector3& position);
+
+		/**
+		 * @brief	Gets the world position of the object.
+		 *
+		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
+		 */
+		const Vector3& getWorldPosition() const;
+
+		/**
+		 * @brief	Sets the local rotation of the object.
+		 */
+		void setRotation(const Quaternion& rotation);
+
+		/**
+		 * @brief	Gets the local rotation of the object.
+		 */
+		const Quaternion& getRotation() const { return mRotation; }
+
+		/**
+		 * @brief	Sets the world rotation of the object.
+		 */
+		void setWorldRotation(const Quaternion& rotation);
+
+		/**
+		 * @brief	Gets world rotation of the object.
+		 *
+		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
+		 */
+		const Quaternion& getWorldRotation() const;
+
+		/**
+		 * @brief	Sets the local scale of the object.
+		 */
+		void setScale(const Vector3& scale);
+
+		/**
+		 * @brief	Gets the local scale of the object.
+		 */
+		const Vector3& getScale() const { return mScale; }
+
+		/**
+		 * @brief	Sets the world scale of the object.
+		 *
+		 * @note	This will not work properly if this object or any of its parents
+		 *			have non-affine transform matrices.
+		 */
+		void setWorldScale(const Vector3& scale);
+
+		/**
+		 * @brief	Gets world scale of the object.
+		 *
+		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
+		 */
+		const Vector3& getWorldScale() const;
+
+		/**
+		 * @brief	Orients the object so it is looking at the provided \p location (world space)
+		 *			where \p up is used for determining the location of the object's Y axis.
+		 *
+		 */
+		void lookAt(const Vector3& location, const Vector3& up = Vector3::UNIT_Y);
+
+		/**
+		 * @brief	Gets the objects world transform matrix.
+		 *
+		 * @note	Performance warning: This might involve updating the transforms if the transform is dirty.
+		 */
+		const Matrix4& getWorldTfrm() const;
+
+		/**
+		 * @brief	Gets the objects local transform matrix.
+		 */
+		const Matrix4& getLocalTfrm() const;
+
+		/**
+		 * @brief	Moves the object's position by the vector offset provided along world axes.
+		 */
+        void move(const Vector3& vec);
+
+		/**
+		 * @brief	Moves the object's position by the vector offset provided along it's own axes (relative to orientation).
+		 */
+        void moveRelative(const Vector3& vec);
+
+		/**
+		 * @brief	Gets the Z (forward) axis of the object, in world space.
+		 *
+		 * @return	Forward axis of the object.
+		 */
+		Vector3 getForward() const { return getWorldRotation().rotate(-Vector3::UNIT_Z); }
+
+		/**
+		 * @brief	Gets the Y (up) axis of the object, in world space.
+		 *
+		 * @return	Up axis of the object.
+		 */
+		Vector3 getUp() const { return getWorldRotation().rotate(Vector3::UNIT_Y); }
+
+		/**
+		 * @brief	Gets the X (right) axis of the object, in world space.
+		 *
+		 * @return	Right axis of the object.
+		 */
+		Vector3 getRight() const { return getWorldRotation().rotate(Vector3::UNIT_X); }
+
+		/**
+		 * @brief	Rotates the game object so it's forward axis faces the provided
+		 * 			direction.
+		 * 			
+		 * @note	Local forward axis is considered to be negative Z.
+		 *
+		 * @param	forwardDir	The forward direction to face, in world space.
+		 */
+		void setForward(const Vector3& forwardDir);
+
+		/**
+		 * @brief	Rotate the object around an arbitrary axis.
+		 */
+        void rotate(const Vector3& axis, const Radian& angle);
+
+		/**
+		 * @brief	Rotate the object around an arbitrary axis using a Quaternion.
+		 */
+        void rotate(const Quaternion& q);
+
+		/**
+		 * @brief	Rotates around local Z axis.
+		 *
+		 * @param	angle	Angle to rotate by.
+		 */
+		void roll(const Radian& angle);
+
+		/**
+		 * @brief	Rotates around Y axis.
+		 *
+		 * @param	angle	Angle to rotate by.
+		 */
+		void yaw(const Radian& angle);
+
+		/**
+		 * @brief	Rotates around X axis
+		 *
+		 * @param	angle	Angle to rotate by.
+		 */
+		void pitch(const Radian& angle);
+
+		/**
+		 * @brief	Forces any dirty transform matrices on this object to be updated.
+		 *
+		 * @note	Normally this is done internally when retrieving a transform, but sometimes
+		 *			it is useful to update transforms manually.
+		 */
+		void updateTransformsIfDirty();
+
+		/**
+		 * @brief	Returns a hash value that changes whenever a scene objects
+		 *			transform gets updated. It allows you to detect changes with
+		 *			the local or world transforms without directly comparing their
+		 *			values with some older state.
+		 */
+		UINT32 getTransformHash() const { return mDirtyHash; }
+
+	private:
+		Vector3 mPosition;
+		Quaternion mRotation;
+		Vector3 mScale;
+
+		mutable Vector3 mWorldPosition;
+		mutable Quaternion mWorldRotation;
+		mutable Vector3 mWorldScale;
+
+		mutable Matrix4 mCachedLocalTfrm;
+		mutable Matrix4 mCachedWorldTfrm;
+
+		mutable UINT32 mDirtyFlags;
+		mutable UINT32 mDirtyHash;
+
+		/**
+		 * @brief	Marks the transform as dirty so that we know to update
+		 *			it when the transform is requested.
+		 */
+		void markTfrmDirty() const;
+
+		/**
+		 * @brief	Updates the local transform. Normally just reconstructs the 
+		 *			transform matrix from the position/rotation/scale.
+		 */
+		void updateLocalTfrm() const;
+
+		/**
+		 * @brief	Updates the world transform. Reconstructs the local transform
+		 *			matrix and multiplies it with any parent transforms.
+		 *
+		 * @note	If parent transforms are dirty they will be updated.
+		 */
+		void updateWorldTfrm() const;
+
+		/**
+		 * @brief	Checks if cached local transform needs updating.
+		 */
+		bool isCachedLocalTfrmUpToDate() const { return (mDirtyFlags & DirtyFlags::LocalTfrmDirty) == 0; }
+
+		/**
+		 * @brief	Checks if cached world transform needs updating.
+		 */
+		bool isCachedWorldTfrmUpToDate() const { return (mDirtyFlags & DirtyFlags::WorldTfrmDirty) == 0; }
+
+		/************************************************************************/
+		/* 								Hierarchy	                     		*/
+		/************************************************************************/
+	public:
+		/**
+		 * @brief	Changes the parent of this object. Also removes the object from the current parent,
+		 * 			and assigns it to the new parent.
+		 *
+		 * @param [in]	parent			New parent.
+		 * @param [in]	keepWorldPos	Determines should the current transform be maintained even after the parent is changed
+		 * 								(this means the local transform will be modified accordingly).
+		 */
+		void setParent(const HSceneObject& parent, bool keepWorldTransform = true);
+
+		/**
+		 * @brief	Gets the parent of this object.
+		 *
+		 * @return	Parent object, or nullptr if this SceneObject is at root level.
+		 */
+		HSceneObject getParent() const { return mParent; }
+
+		/**
+		 * @brief	Gets a child of this item.
+		 *
+		 * @param	idx	The zero based index of the child.
+		 *
+		 * @return	SceneObject of the child.
+		 */
+		HSceneObject getChild(UINT32 idx) const;
+
+		/**
+		 * @brief	Find the index of the specified child. Don't persist this value as
+		 * 			it may change whenever you add/remove children.
+		 *
+		 * @param	child	The child to look for.
+		 *
+		 * @return	The zero-based index of the found child, or -1 if no match was found.
+		 */
+		int indexOfChild(const HSceneObject& child) const;
+
+		/**
+		 * @brief	Gets the number of all child GameObjects.
+		 */
+		UINT32 getNumChildren() const { return (UINT32)mChildren.size(); }
+
+		/**
+		 * @brief	Searches the child objects for an object matching the specified name.
+		 *
+		 * @param	name		Name of the object to locate.
+		 * @param	recursive	If true all descendants of the scene object will be searched, 
+		 *						otherwise only immediate children.
+		 *
+		 * @returns	First found scene object, or empty handle if none found.
+		 */
+		HSceneObject findChild(const String& name, bool recursive = true);
+
+		/**
+		 * @brief	Searches the child objects for objects matching the specified name.
+		 *
+		 * @param	name		Name of the objects to locate.
+		 * @param	recursive	If true all descendants of the scene object will be searched, 
+		 *						otherwise only immediate children.
+		 *
+		 * @returns	All scene objects matching the specified name.
+		 */
+		Vector<HSceneObject> findChildren(const String& name, bool recursive = true);
+
+		/**
+		 * @brief	Enables or disables this object. Disabled objects also implicitly disable
+		 *			all their child objects. No components on the disabled object are updated.
+		 */
+		void setActive(bool active);
+
+		/**
+		 * @brief	Returns whether or not an object is active.
+		 *
+		 * @param	self	If true, the method will only check if this particular object was activated
+		 *					or deactivated directly via setActive. If false we we also check if any of
+		 *					the objects parents are inactive.
+		 */
+		bool getActive(bool self = false);
+
+		/**
+		 * @brief	Makes a deep copy of this object.
+		 * 			
+		 * @param	instantiate	If false, the cloned hierarchy will just be a memory copy, but will not be present in the
+		 * 						scene or otherwise active until ::instantiate() is called.
+		 */
+		HSceneObject clone(bool instantiate = true);
+
+	private:
+		HSceneObject mParent;
+		Vector<HSceneObject> mChildren;
+		bool mActiveSelf;
+		bool mActiveHierarchy;
+
+		/**
+		 * @brief	Internal version of ::setParent that allows you to set a null parent.
+		 *
+		 * @param [in]	parent			New parent.
+		 * @param [in]	keepWorldPos	Determines should the current transform be maintained even after the parent is changed
+		 * 								(this means the local transform will be modified accordingly).
+		 */
+		void _setParent(const HSceneObject& parent, bool keepWorldTransform = true);
+
+		/**
+		 * @brief	Adds a child to the child array. This method doesn't check for null or duplicate values.
+		 *
+		 * @param [in]	object	New child.
+		 */
+		void addChild(const HSceneObject& object);
+		
+		/**
+		 * @brief	Removes the child from the object. 
+		 *
+		 * @param [in]	object	Child to remove.
+		 * 					
+		 * @throws INTERNAL_ERROR If the provided child isn't a child of the current object.
+		 */
+		void removeChild(const HSceneObject& object);
+
+		/**
+		 * @brief	Changes the object active in hierarchy state.
+		 */
+		void setActiveHierarchy(bool active);
+
+		/************************************************************************/
+		/* 								Component	                     		*/
+		/************************************************************************/
+	public:
+		/**
+		 * @brief	Constructs a new component of the specified type and adds it to 
+		 *			the internal component list.
+		 */
+		template<class T, class... Args>
+		GameObjectHandle<T> addComponent(Args &&... args)
+		{
+			static_assert((std::is_base_of<BansheeEngine::Component, T>::value),
+				"Specified type is not a valid Component.");
+
+			std::shared_ptr<T> gameObject(new (bs_alloc<T>()) T(mThisHandle,
+				std::forward<Args>(args)...),
+				&bs_delete<T>, StdAlloc<T>());
+
+			for (auto& component : mComponents)
+			{
+				if (component->typeEquals(*gameObject))
+					return component; // Component of this type already exists
+			}
+
+			GameObjectHandle<T> newComponent =
+				GameObjectManager::instance().registerObject(gameObject);
+
+			mComponents.push_back(newComponent);
+
+			if (isInstantiated())
+			{
+				newComponent->instantiate();
+				newComponent->onInitialized();
+
+				if (getActive())
+					newComponent->onEnabled();
+			}
+
+			return newComponent;
+		}
+
+		/**
+		 * @brief	Searches for a component with the specific type and returns the first one
+		 * 			it finds. 
+		 * 			
+		 * @note	Don't call this too often as it is relatively slow. It is more efficient 
+		 * 			to call it once and store the result for further use.
+		 *
+		 * @tparam	typename T	Type of the component.
+		 *
+		 * @return	Component if found, nullptr otherwise.
+		 */
+		template <typename T>
+		GameObjectHandle<T> getComponent()
+		{
+			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), 
+				"Specified type is not a valid Component.");
+
+			return static_object_cast<T>(getComponent(T::getRTTIStatic()->getRTTIId()));
+		}
+
+		/**
+		 * @brief	Checks if the current object contains the specified component
+		 * 			 			
+		 * @note	Don't call this too often as it is relatively slow. 
+		 *
+		 * @tparam	typename T	Type of the component.
+		 *
+		 * @return	True if component exists on the object.
+		 */
+		template <typename T>
+		bool hasComponent()
+		{
+			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), 
+				"Specified type is not a valid Component.");
+
+			for (auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
+			{
+				if ((*iter)->getRTTI()->getRTTIId() == T::getRTTIStatic()->getRTTIId())
+					return true;
+			}
+
+			return false;
+		}
+
+		/**
+		 * @brief	Searches for a component with the specified type id and returns the first one it
+		 * 			finds.
+		 * 			
+		 * 			@note	Don't call this too often as it is relatively slow. It is more efficient to
+		 * 			call it once and store the result for further use.
+		 *
+		 * @param	typeId	Identifier for the type.
+		 *
+		 * @return	Component if found, nullptr otherwise.
+		 */
+		HComponent getComponent(UINT32 typeId) const;
+
+		/**
+		 * @brief	Removes the component from this object, and deallocates it.
+		 *
+		 * @param [in]	component	The component to destroy.
+		 * @param [in]	immediate	If true, the component will be deallocated and become unusable
+		 *							right away. Otherwise the deallocation will be delayed to the end of
+		 *							frame (preferred method).
+		 */
+		void destroyComponent(const HComponent component, bool immediate = false);
+
+		/**
+		 * @brief	Removes the component from this object, and deallocates it.
+		 *
+		 * @param [in]	component	The component to destroy.
+		 * @param [in]	immediate	If true, the component will be deallocated and become unusable
+		 *							right away. Otherwise the deallocation will be delayed to the end of
+		 *							frame (preferred method).
+		 */
+		void destroyComponent(Component* component, bool immediate = false);
+
+		/**
+		 * @brief	Returns all components on this object.
+		 */
+		const Vector<HComponent>& getComponents() const { return mComponents; }
+
+	private:
+		/**
+		 * @brief	Creates an empty component with the default constructor.
+		 */
+		template <typename T>
+		static std::shared_ptr<T> createEmptyComponent()
+		{
+			static_assert((std::is_base_of<BansheeEngine::Component, T>::value), "Specified type is not a valid Component.");
+
+			T* rawPtr = new (bs_alloc<T>()) T();
+			std::shared_ptr<T> gameObject(rawPtr, &bs_delete<T>, StdAlloc<T>());
+
+			return gameObject;
+		}
+
+		/**
+		 * @brief	Adds the component to the internal component array.
+		 */
+		void addComponentInternal(const std::shared_ptr<Component> component);
+
+		Vector<HComponent> mComponents;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class GameObjectRTTI;
+		friend class SceneObjectRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;
+	};
 }

+ 736 - 757
BansheeCore/Source/BsSceneObject.cpp

@@ -1,758 +1,737 @@
-#include "BsSceneObject.h"
-#include "BsComponent.h"
-#include "BsCoreSceneManager.h"
-#include "BsException.h"
-#include "BsDebug.h"
-#include "BsSceneObjectRTTI.h"
-#include "BsMemorySerializer.h"
-#include "BsGameObjectManager.h"
-#include "BsPrefabUtility.h"
-#include "BsMatrix3.h"
-
-namespace BansheeEngine
-{
-	SceneObject::SceneObject(const String& name, UINT32 flags)
-		:GameObject(), mPosition(Vector3::ZERO), mRotation(Quaternion::IDENTITY), mScale(Vector3::ONE),
-		mWorldPosition(Vector3::ZERO), mWorldRotation(Quaternion::IDENTITY), mWorldScale(Vector3::ONE),
-		mCachedLocalTfrm(Matrix4::IDENTITY), mDirtyFlags(0xFFFFFFFF), mCachedWorldTfrm(Matrix4::IDENTITY), 
-		mActiveSelf(true), mActiveHierarchy(true), mDirtyHash(0), mFlags(flags), mPrefabHash(0)
-	{
-		setName(name);
-	}
-
-	SceneObject::~SceneObject()
-	{
-		if(!mThisHandle.isDestroyed())
-		{
-			LOGWRN("Object is being deleted without being destroyed first?");
-			destroyInternal(mThisHandle, true);
-		}
-	}
-
-	HSceneObject SceneObject::create(const String& name, UINT32 flags)
-	{
-		HSceneObject newObject = createInternal(name, flags);
-
-		if (newObject->isInstantiated())
-			gCoreSceneManager().registerNewSO(newObject);
-
-		return newObject;
-	}
-
-	HSceneObject SceneObject::createInternal(const String& name, UINT32 flags)
-	{
-		SPtr<SceneObject> sceneObjectPtr = SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject(name, flags),
-			&bs_delete<SceneObject>, StdAlloc<SceneObject>());
-		
-		HSceneObject sceneObject = GameObjectManager::instance().registerObject(sceneObjectPtr);
-		sceneObject->mThisHandle = sceneObject;
-
-		return sceneObject;
-	}
-
-	HSceneObject SceneObject::createInternal(const SPtr<SceneObject>& soPtr, UINT64 originalId)
-	{
-		HSceneObject sceneObject = GameObjectManager::instance().registerObject(soPtr, originalId);
-		sceneObject->mThisHandle = sceneObject;
-
-		return sceneObject;
-	}
-
-	void SceneObject::destroy(bool immediate)
-	{
-		// Parent is our owner, so when his reference to us is removed, delete might be called.
-		// So make sure this is the last thing we do.
-		if(mParent != nullptr)
-		{
-			if(!mParent.isDestroyed())
-				mParent->removeChild(mThisHandle);
-
-			mParent = nullptr;
-		}
-
-		destroyInternal(mThisHandle, immediate);
-	}
-
-	void SceneObject::destroyInternal(GameObjectHandleBase& handle, bool immediate)
-	{
-		if (immediate)
-		{
-			for (auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
-				(*iter)->destroyInternal(*iter, true);
-
-			mChildren.clear();
-
-			for (auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
-			{
-				HComponent component = *iter;
-				component->_setIsDestroyed();
-
-				if (isInstantiated())
-				{
-					if (getActive())
-						component->onDisabled();
-
-					component->onDestroyed();
-				}
-
-				component->destroyInternal(*iter, true);
-			}
-
-			mComponents.clear();
-
-			GameObjectManager::instance().unregisterObject(handle);
-		}
-		else
-			GameObjectManager::instance().queueForDestroy(handle);
-	}
-
-	void SceneObject::_setInstanceData(GameObjectInstanceDataPtr& other)
-	{
-		GameObject::_setInstanceData(other);
-
-		// Instance data changed, so make sure to refresh the handles to reflect that
-		mThisHandle._setHandleData(mThisHandle.getInternalPtr());
-	}
-
-	String SceneObject::getPrefabLink() const
-	{
-		const SceneObject* curObj = this;
-
-		while (curObj != nullptr)
-		{
-			if (!curObj->mPrefabLinkUUID.empty())
-				return curObj->mPrefabLinkUUID;
-
-			if (curObj->mParent != nullptr)
-				curObj = curObj->mParent.get();
-			else
-				curObj = nullptr;
-		}
-
-		return "";
-	}
-
-	void SceneObject::breakPrefabLink()
-	{
-		SceneObject* rootObj = this;
-
-		while (rootObj == nullptr)
-		{
-			if (!rootObj->mPrefabLinkUUID.empty())
-				return;
-
-			if (rootObj->mParent != nullptr)
-				rootObj = rootObj->mParent.get();
-			else
-				rootObj = nullptr;
-		}
-
-		if (rootObj != nullptr)
-		{
-			rootObj->mPrefabLinkUUID = "";
-			rootObj->mPrefabDiff = nullptr;
-			PrefabUtility::clearPrefabIds(rootObj->getHandle());
-		}
-	}
-
-	bool SceneObject::hasFlag(UINT32 flag) const
-	{
-		return (mFlags & flag) != 0;
-	}
-
-	void SceneObject::setFlags(UINT32 flags)
-	{
-		mFlags |= flags;
-
-		for (auto& child : mChildren)
-			child->setFlags(flags);
-	}
-
-	void SceneObject::unsetFlags(UINT32 flags)
-	{
-		mFlags &= ~flags;
-
-		for (auto& child : mChildren)
-			child->unsetFlags(flags);
-	}
-
-	void SceneObject::_instantiate()
-	{
-		std::function<void(SceneObject*)> instantiateRecursive = [&](SceneObject* obj)
-		{
-			obj->mFlags &= ~SOF_DontInstantiate;
-
-			if (obj->mParent == nullptr)
-				gCoreSceneManager().registerNewSO(obj->mThisHandle);
-
-			for (auto& component : obj->mComponents)
-				component->instantiate();
-
-			for (auto& child : obj->mChildren)
-				instantiateRecursive(child.get());
-		};
-
-		std::function<void(SceneObject*)> triggerEventsRecursive = [&](SceneObject* obj)
-		{
-			for (auto& component : obj->mComponents)
-			{
-				component->onInitialized();
-
-				if (obj->getActive())
-					component->onEnabled();
-			}
-
-			for (auto& child : obj->mChildren)
-				triggerEventsRecursive(child.get());
-		};
-
-		instantiateRecursive(this);
-		triggerEventsRecursive(this);
-	}
-
-	/************************************************************************/
-	/* 								Transform	                     		*/
-	/************************************************************************/
-
-	void SceneObject::setPosition(const Vector3& position)
-	{
-		mPosition = position;
-		markTfrmDirty();
-	}
-
-	void SceneObject::setRotation(const Quaternion& rotation)
-	{
-		mRotation = rotation;
-		markTfrmDirty();
-	}
-
-	void SceneObject::setScale(const Vector3& scale)
-	{
-		mScale = scale;
-		markTfrmDirty();
-	}
-
-	void SceneObject::setWorldPosition(const Vector3& position)
-	{
-		if (mParent != nullptr)
-		{
-			Vector3 invScale = mParent->getWorldScale();
-			if (invScale.x != 0) invScale.x = 1.0f / invScale.x;
-			if (invScale.y != 0) invScale.y = 1.0f / invScale.y;
-			if (invScale.z != 0) invScale.z = 1.0f / invScale.z;
-
-			Quaternion invRotation = mParent->getWorldRotation().inverse();
-
-			mPosition = invRotation.rotate(position - mParent->getWorldPosition()) *  invScale;
-		}
-		else
-			mPosition = position;
-
-		markTfrmDirty();
-	}
-
-	void SceneObject::setWorldRotation(const Quaternion& rotation)
-	{
-		if (mParent != nullptr)
-		{
-			Quaternion invRotation = mParent->getWorldRotation().inverse();
-
-			mRotation = invRotation * rotation;
-		}
-		else
-			mRotation = rotation;
-
-		markTfrmDirty();
-	}
-
-	void SceneObject::setWorldScale(const Vector3& scale)
-	{
-		if (mParent != nullptr)
-		{
-			Matrix3 rotScale;
-			mParent->getWorldTfrm().extract3x3Matrix(rotScale);
-			rotScale.inverse();
-
-			Matrix3 scaleMat = Matrix3(Quaternion::IDENTITY, scale);
-			scaleMat = rotScale * scaleMat;
-
-			Quaternion rotation;
-			Vector3 localScale;
-			scaleMat.decomposition(rotation, localScale);
-
-			mScale = localScale;
-		}
-		else
-			mScale = scale;
-
-		markTfrmDirty();
-	}
-
-	const Vector3& SceneObject::getWorldPosition() const
-	{ 
-		if (!isCachedWorldTfrmUpToDate())
-			updateWorldTfrm();
-
-		return mWorldPosition; 
-	}
-
-	const Quaternion& SceneObject::getWorldRotation() const 
-	{ 
-		if (!isCachedWorldTfrmUpToDate())
-			updateWorldTfrm();
-
-		return mWorldRotation; 
-	}
-
-	const Vector3& SceneObject::getWorldScale() const 
-	{ 
-		if (!isCachedWorldTfrmUpToDate())
-			updateWorldTfrm();
-
-		return mWorldScale; 
-	}
-
-	void SceneObject::lookAt(const Vector3& location, const Vector3& up)
-	{
-		Vector3 forward = location - mPosition;
-		forward.normalize();
-
-		setForward(forward);
-
-		Quaternion upRot = Quaternion::getRotationFromTo(getUp(), up);
-		setRotation(getRotation() * upRot);
-	}
-
-	const Matrix4& SceneObject::getWorldTfrm() const
-	{
-		if (!isCachedWorldTfrmUpToDate())
-			updateWorldTfrm();
-
-		return mCachedWorldTfrm;
-	}
-
-	const Matrix4& SceneObject::getLocalTfrm() const
-	{
-		if (!isCachedLocalTfrmUpToDate())
-			updateLocalTfrm();
-
-		return mCachedLocalTfrm;
-	}
-
-	void SceneObject::move(const Vector3& vec)
-	{
-		setPosition(mPosition + vec);
-	}
-
-	void SceneObject::moveRelative(const Vector3& vec)
-	{
-		// Transform the axes of the relative vector by camera's local axes
-		Vector3 trans = mRotation.rotate(vec);
-
-		setPosition(mPosition + trans);
-	}
-
-	void SceneObject::rotate(const Vector3& axis, const Radian& angle)
-	{
-		Quaternion q;
-		q.fromAxisAngle(axis, angle);
-		rotate(q);
-	}
-
-	void SceneObject::rotate(const Quaternion& q)
-	{
-		// Note the order of the mult, i.e. q comes after
-
-		// Normalize the quat to avoid cumulative problems with precision
-		Quaternion qnorm = q;
-		qnorm.normalize();
-		setRotation(qnorm * mRotation);
-	}
-
-	void SceneObject::roll(const Radian& angle)
-	{
-		// Rotate around local Z axis
-		Vector3 zAxis = mRotation.rotate(Vector3::UNIT_Z);
-		rotate(zAxis, angle);
-	}
-
-	void SceneObject::yaw(const Radian& angle)
-	{
-		Vector3 yAxis = mRotation.rotate(Vector3::UNIT_Y);
-		rotate(yAxis, angle);
-	}
-
-	void SceneObject::pitch(const Radian& angle)
-	{
-		// Rotate around local X axis
-		Vector3 xAxis = mRotation.rotate(Vector3::UNIT_X);
-		rotate(xAxis, angle);
-	}
-
-	void SceneObject::setForward(const Vector3& forwardDir)
-	{
-		if (forwardDir == Vector3::ZERO) 
-			return;
-
-		Vector3 nrmForwardDir = Vector3::normalize(forwardDir);
-		Vector3 currentForwardDir = getForward();
-		
-		const Quaternion& currentRotation = getWorldRotation();
-		Quaternion targetRotation;
-		if ((nrmForwardDir+currentForwardDir).squaredLength() < 0.00005f)
-		{
-			// Oops, a 180 degree turn (infinite possible rotation axes)
-			// Default to yaw i.e. use current UP
-			targetRotation = Quaternion(-currentRotation.y, -currentRotation.z, currentRotation.w, currentRotation.x);
-		}
-		else
-		{
-			// Derive shortest arc to new direction
-			Quaternion rotQuat = Quaternion::getRotationFromTo(currentForwardDir, nrmForwardDir);
-			targetRotation = rotQuat * currentRotation;
-		}
-
-		setRotation(targetRotation);
-	}
-
-	void SceneObject::updateTransformsIfDirty()
-	{
-		if (!isCachedLocalTfrmUpToDate())
-			updateLocalTfrm();
-
-		if (!isCachedWorldTfrmUpToDate())
-			updateWorldTfrm();
-	}
-
-	void SceneObject::markTfrmDirty() const
-	{
-		mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty;
-		mDirtyHash++;
-
-		for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
-		{
-			(*iter)->markTfrmDirty();
-		}
-	}
-
-	void SceneObject::updateWorldTfrm() const
-	{
-		if(mParent != nullptr)
-		{
-			// Update orientation
-			const Quaternion& parentOrientation = mParent->getWorldRotation();
-			mWorldRotation = parentOrientation * mRotation;
-
-			// Update scale
-			const Vector3& parentScale = mParent->getWorldScale();
-			// Scale own position by parent scale, just combine
-			// as equivalent axes, no shearing
-			mWorldScale = parentScale * mScale;
-
-			// Change position vector based on parent's orientation & scale
-			mWorldPosition = parentOrientation.rotate(parentScale * mPosition);
-
-			// Add altered position vector to parents
-			mWorldPosition += mParent->getWorldPosition();
-
-			mCachedWorldTfrm.setTRS(mWorldPosition, mWorldRotation, mWorldScale);
-		}
-		else
-		{
-			mWorldRotation = mRotation;
-			mWorldPosition = mPosition;
-			mWorldScale = mScale;
-
-			mCachedWorldTfrm = getLocalTfrm();
-		}
-
-		mDirtyFlags &= ~DirtyFlags::WorldTfrmDirty;
-	}
-
-	void SceneObject::updateLocalTfrm() const
-	{
-		mCachedLocalTfrm.setTRS(mPosition, mRotation, mScale);
-
-		mDirtyFlags &= ~DirtyFlags::LocalTfrmDirty;
-	}
-
-	/************************************************************************/
-	/* 								Hierarchy	                     		*/
-	/************************************************************************/
-
-	void SceneObject::setParent(const HSceneObject& parent, bool keepWorldTransform)
-	{
-		if (parent.isDestroyed())
-			return;
-
-#if BS_EDITOR_BUILD
-		String originalPrefab = getPrefabLink();
-#endif
-
-		_setParent(parent, keepWorldTransform);
-
-#if BS_EDITOR_BUILD
-		String newPrefab = getPrefabLink();
-		if (originalPrefab != newPrefab)
-			PrefabUtility::clearPrefabIds(mThisHandle);
-#endif
-	}
-
-	void SceneObject::_setParent(const HSceneObject& parent, bool keepWorldTransform)
-	{
-		if (mThisHandle == parent)
-			return;
-
-		if (mParent == nullptr || mParent != parent)
-		{
-			Vector3 worldPos;
-			Quaternion worldRot;
-			Vector3 worldScale;
-
-			if (keepWorldTransform)
-			{
-				// Make sure the object keeps its world coordinates
-				worldPos = getWorldPosition();
-				worldRot = getWorldRotation();
-				worldScale = getWorldScale();
-			}
-
-			if (mParent != nullptr)
-				mParent->removeChild(mThisHandle);
-
-			if (parent != nullptr)
-				parent->addChild(mThisHandle);
-
-			mParent = parent;
-
-			if (keepWorldTransform)
-			{
-				setWorldPosition(worldPos);
-				setWorldRotation(worldRot);
-				setWorldScale(worldScale);
-			}
-
-			markTfrmDirty();
-		}
-	}
-
-	HSceneObject SceneObject::getChild(UINT32 idx) const
-	{
-		if(idx < 0 || idx >= mChildren.size())
-		{
-			BS_EXCEPT(InternalErrorException, "Child index out of range.");
-		}
-
-		return mChildren[idx];
-	}
-
-	int SceneObject::indexOfChild(const HSceneObject& child) const
-	{
-		for(int i = 0; i < (int)mChildren.size(); i++)
-		{
-			if(mChildren[i] == child)
-				return i;
-		}
-
-		return -1;
-	}
-
-	void SceneObject::addChild(const HSceneObject& object)
-	{
-		mChildren.push_back(object); 
-
-		object->setFlags(mFlags);
-	}
-
-	void SceneObject::removeChild(const HSceneObject& object)
-	{
-		auto result = find(mChildren.begin(), mChildren.end(), object);
-
-		if(result != mChildren.end())
-			mChildren.erase(result);
-		else
-		{
-			BS_EXCEPT(InternalErrorException, 
-				"Trying to remove a child but it's not a child of the transform.");
-		}
-	}
-
-	HSceneObject SceneObject::findChild(const String& name, bool recursive)
-	{
-		for (auto& child : mChildren)
-		{
-			if (child->getName() == name)
-				return child;
-		}
-
-		if (recursive)
-		{
-			for (auto& child : mChildren)
-			{
-				HSceneObject foundObject = child->findChild(name, true);
-				if (foundObject != nullptr)
-					return foundObject;
-			}
-		}
-
-		return HSceneObject();
-	}
-
-	Vector<HSceneObject> SceneObject::findChildren(const String& name, bool recursive)
-	{
-		std::function<void(const HSceneObject&, Vector<HSceneObject>&)> findChildrenInternal = 
-			[&](const HSceneObject& so, Vector<HSceneObject>& output)
-		{
-			for (auto& child : so->mChildren)
-			{
-				if (child->getName() == name)
-					output.push_back(child);
-			}
-
-			if (recursive)
-			{
-				for (auto& child : so->mChildren)
-					findChildrenInternal(child, output);
-			}
-		};
-
-		Vector<HSceneObject> output;
-		findChildrenInternal(mThisHandle, output);
-
-		return output;
-	}
-
-	void SceneObject::setActive(bool active)
-	{
-		mActiveSelf = active;
-		setActiveHierarchy(active);
-	}
-
-	void SceneObject::setActiveHierarchy(bool active) 
-	{ 
-		bool activeHierarchy = active && mActiveSelf;
-
-		if (mActiveHierarchy != activeHierarchy)
-		{
-			mActiveHierarchy = activeHierarchy;
-
-			if (activeHierarchy)
-			{
-				for (auto& component : mComponents)
-					component->onEnabled();
-			}
-			else
-			{
-				for (auto& component : mComponents)
-					component->onDisabled();
-			}
-		}
-		
-		for (auto child : mChildren)
-		{
-			child->setActiveHierarchy(mActiveHierarchy);
-		}
-	}
-
-	bool SceneObject::getActive(bool self)
-	{
-		if (self)
-			return mActiveSelf;
-		else
-			return mActiveHierarchy;
-	}
-
-	HSceneObject SceneObject::clone(bool instantiate)
-	{
-		if (!instantiate)
-			setFlags(SOF_DontInstantiate);
-
-		UINT32 bufferSize = 0;
-
-		MemorySerializer serializer;
-		UINT8* buffer = serializer.encode(this, bufferSize, (void*(*)(UINT32))&bs_alloc);
-
-		GameObjectManager::instance().setDeserializationMode(GODM_UseNewIds | GODM_RestoreExternal);
-		std::shared_ptr<SceneObject> cloneObj = std::static_pointer_cast<SceneObject>(serializer.decode(buffer, bufferSize));
-		bs_free(buffer);
-
-		if (!instantiate)
-			unsetFlags(SOF_DontInstantiate);
-
-		return cloneObj->mThisHandle;
-	}
-
-	HComponent SceneObject::getComponent(UINT32 typeId) const
-	{
-		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
-		{
-			if((*iter)->getRTTI()->getRTTIId() == typeId)
-				return *iter;
-		}
-
-		return HComponent();
-	}
-
-	void SceneObject::destroyComponent(const HComponent component, bool immediate)
-	{
-		if(component == nullptr)
-		{
-			LOGDBG("Trying to remove a null component");
-			return;
-		}
-
-		auto iter = std::find(mComponents.begin(), mComponents.end(), component);
-
-		if(iter != mComponents.end())
-		{
-			(*iter)->_setIsDestroyed();
-
-			if (isInstantiated())
-			{
-				if (getActive())
-					component->onDisabled();
-
-				(*iter)->onDestroyed();
-			}
-			
-			(*iter)->destroyInternal(*iter, immediate);
-			mComponents.erase(iter);
-		}
-		else
-			LOGDBG("Trying to remove a component that doesn't exist on this SceneObject.");
-	}
-
-	void SceneObject::destroyComponent(Component* component, bool immediate)
-	{
-		auto iterFind = std::find_if(mComponents.begin(), mComponents.end(), 
-			[component](const HComponent& x) 
-		{ 
-			if(x.isDestroyed())
-				return false;
-
-			return x._getHandleData()->mPtr->object.get() == component; }
-		);
-
-		if(iterFind != mComponents.end())
-		{
-			destroyComponent(*iterFind, immediate);
-		}
-	}
-
-	void SceneObject::addComponentInternal(const std::shared_ptr<Component> component)
-	{
-		GameObjectHandle<Component> newComponent = GameObjectManager::instance().getObject(component->getInstanceId());
-		newComponent->mParent = mThisHandle;
-		mComponents.push_back(newComponent);
-	}
-
-	RTTITypeBase* SceneObject::getRTTIStatic()
-	{
-		return SceneObjectRTTI::instance();
-	}
-
-	RTTITypeBase* SceneObject::getRTTI() const
-	{
-		return SceneObject::getRTTIStatic();
-	}
+#include "BsSceneObject.h"
+#include "BsComponent.h"
+#include "BsCoreSceneManager.h"
+#include "BsException.h"
+#include "BsDebug.h"
+#include "BsSceneObjectRTTI.h"
+#include "BsMemorySerializer.h"
+#include "BsGameObjectManager.h"
+#include "BsPrefabUtility.h"
+#include "BsMatrix3.h"
+
+namespace BansheeEngine
+{
+	SceneObject::SceneObject(const String& name, UINT32 flags)
+		:GameObject(), mPosition(Vector3::ZERO), mRotation(Quaternion::IDENTITY), mScale(Vector3::ONE),
+		mWorldPosition(Vector3::ZERO), mWorldRotation(Quaternion::IDENTITY), mWorldScale(Vector3::ONE),
+		mCachedLocalTfrm(Matrix4::IDENTITY), mDirtyFlags(0xFFFFFFFF), mCachedWorldTfrm(Matrix4::IDENTITY), 
+		mActiveSelf(true), mActiveHierarchy(true), mDirtyHash(0), mFlags(flags), mPrefabHash(0)
+	{
+		setName(name);
+	}
+
+	SceneObject::~SceneObject()
+	{
+		if(!mThisHandle.isDestroyed())
+		{
+			LOGWRN("Object is being deleted without being destroyed first?");
+			destroyInternal(mThisHandle, true);
+		}
+	}
+
+	HSceneObject SceneObject::create(const String& name, UINT32 flags)
+	{
+		HSceneObject newObject = createInternal(name, flags);
+
+		if (newObject->isInstantiated())
+			gCoreSceneManager().registerNewSO(newObject);
+
+		return newObject;
+	}
+
+	HSceneObject SceneObject::createInternal(const String& name, UINT32 flags)
+	{
+		SPtr<SceneObject> sceneObjectPtr = SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject(name, flags),
+			&bs_delete<SceneObject>, StdAlloc<SceneObject>());
+		
+		HSceneObject sceneObject = GameObjectManager::instance().registerObject(sceneObjectPtr);
+		sceneObject->mThisHandle = sceneObject;
+
+		return sceneObject;
+	}
+
+	HSceneObject SceneObject::createInternal(const SPtr<SceneObject>& soPtr, UINT64 originalId)
+	{
+		HSceneObject sceneObject = GameObjectManager::instance().registerObject(soPtr, originalId);
+		sceneObject->mThisHandle = sceneObject;
+
+		return sceneObject;
+	}
+
+	void SceneObject::destroy(bool immediate)
+	{
+		// Parent is our owner, so when his reference to us is removed, delete might be called.
+		// So make sure this is the last thing we do.
+		if(mParent != nullptr)
+		{
+			if(!mParent.isDestroyed())
+				mParent->removeChild(mThisHandle);
+
+			mParent = nullptr;
+		}
+
+		destroyInternal(mThisHandle, immediate);
+	}
+
+	void SceneObject::destroyInternal(GameObjectHandleBase& handle, bool immediate)
+	{
+		if (immediate)
+		{
+			for (auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
+				(*iter)->destroyInternal(*iter, true);
+
+			mChildren.clear();
+
+			for (auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
+			{
+				HComponent component = *iter;
+				component->_setIsDestroyed();
+
+				if (isInstantiated())
+				{
+					if (getActive())
+						component->onDisabled();
+
+					component->onDestroyed();
+				}
+
+				component->destroyInternal(*iter, true);
+			}
+
+			mComponents.clear();
+
+			GameObjectManager::instance().unregisterObject(handle);
+		}
+		else
+			GameObjectManager::instance().queueForDestroy(handle);
+	}
+
+	void SceneObject::_setInstanceData(GameObjectInstanceDataPtr& other)
+	{
+		GameObject::_setInstanceData(other);
+
+		// Instance data changed, so make sure to refresh the handles to reflect that
+		mThisHandle._setHandleData(mThisHandle.getInternalPtr());
+	}
+
+	String SceneObject::getPrefabLink() const
+	{
+		const SceneObject* curObj = this;
+
+		while (curObj != nullptr)
+		{
+			if (!curObj->mPrefabLinkUUID.empty())
+				return curObj->mPrefabLinkUUID;
+
+			if (curObj->mParent != nullptr)
+				curObj = curObj->mParent.get();
+			else
+				curObj = nullptr;
+		}
+
+		return "";
+	}
+
+	void SceneObject::breakPrefabLink()
+	{
+		SceneObject* rootObj = this;
+
+		while (rootObj == nullptr)
+		{
+			if (!rootObj->mPrefabLinkUUID.empty())
+				return;
+
+			if (rootObj->mParent != nullptr)
+				rootObj = rootObj->mParent.get();
+			else
+				rootObj = nullptr;
+		}
+
+		if (rootObj != nullptr)
+		{
+			rootObj->mPrefabLinkUUID = "";
+			rootObj->mPrefabDiff = nullptr;
+			PrefabUtility::clearPrefabIds(rootObj->getHandle());
+		}
+	}
+
+	bool SceneObject::hasFlag(UINT32 flag) const
+	{
+		return (mFlags & flag) != 0;
+	}
+
+	void SceneObject::setFlags(UINT32 flags)
+	{
+		mFlags |= flags;
+
+		for (auto& child : mChildren)
+			child->setFlags(flags);
+	}
+
+	void SceneObject::unsetFlags(UINT32 flags)
+	{
+		mFlags &= ~flags;
+
+		for (auto& child : mChildren)
+			child->unsetFlags(flags);
+	}
+
+	void SceneObject::_instantiate()
+	{
+		std::function<void(SceneObject*)> instantiateRecursive = [&](SceneObject* obj)
+		{
+			obj->mFlags &= ~SOF_DontInstantiate;
+
+			if (obj->mParent == nullptr)
+				gCoreSceneManager().registerNewSO(obj->mThisHandle);
+
+			for (auto& component : obj->mComponents)
+				component->instantiate();
+
+			for (auto& child : obj->mChildren)
+				instantiateRecursive(child.get());
+		};
+
+		std::function<void(SceneObject*)> triggerEventsRecursive = [&](SceneObject* obj)
+		{
+			for (auto& component : obj->mComponents)
+			{
+				component->onInitialized();
+
+				if (obj->getActive())
+					component->onEnabled();
+			}
+
+			for (auto& child : obj->mChildren)
+				triggerEventsRecursive(child.get());
+		};
+
+		instantiateRecursive(this);
+		triggerEventsRecursive(this);
+	}
+
+	/************************************************************************/
+	/* 								Transform	                     		*/
+	/************************************************************************/
+
+	void SceneObject::setPosition(const Vector3& position)
+	{
+		mPosition = position;
+		markTfrmDirty();
+	}
+
+	void SceneObject::setRotation(const Quaternion& rotation)
+	{
+		mRotation = rotation;
+		markTfrmDirty();
+	}
+
+	void SceneObject::setScale(const Vector3& scale)
+	{
+		mScale = scale;
+		markTfrmDirty();
+	}
+
+	void SceneObject::setWorldPosition(const Vector3& position)
+	{
+		if (mParent != nullptr)
+		{
+			Vector3 invScale = mParent->getWorldScale();
+			if (invScale.x != 0) invScale.x = 1.0f / invScale.x;
+			if (invScale.y != 0) invScale.y = 1.0f / invScale.y;
+			if (invScale.z != 0) invScale.z = 1.0f / invScale.z;
+
+			Quaternion invRotation = mParent->getWorldRotation().inverse();
+
+			mPosition = invRotation.rotate(position - mParent->getWorldPosition()) *  invScale;
+		}
+		else
+			mPosition = position;
+
+		markTfrmDirty();
+	}
+
+	void SceneObject::setWorldRotation(const Quaternion& rotation)
+	{
+		if (mParent != nullptr)
+		{
+			Quaternion invRotation = mParent->getWorldRotation().inverse();
+
+			mRotation = invRotation * rotation;
+		}
+		else
+			mRotation = rotation;
+
+		markTfrmDirty();
+	}
+
+	void SceneObject::setWorldScale(const Vector3& scale)
+	{
+		if (mParent != nullptr)
+		{
+			Matrix3 rotScale;
+			mParent->getWorldTfrm().extract3x3Matrix(rotScale);
+			rotScale.inverse();
+
+			Matrix3 scaleMat = Matrix3(Quaternion::IDENTITY, scale);
+			scaleMat = rotScale * scaleMat;
+
+			Quaternion rotation;
+			Vector3 localScale;
+			scaleMat.decomposition(rotation, localScale);
+
+			mScale = localScale;
+		}
+		else
+			mScale = scale;
+
+		markTfrmDirty();
+	}
+
+	const Vector3& SceneObject::getWorldPosition() const
+	{ 
+		if (!isCachedWorldTfrmUpToDate())
+			updateWorldTfrm();
+
+		return mWorldPosition; 
+	}
+
+	const Quaternion& SceneObject::getWorldRotation() const 
+	{ 
+		if (!isCachedWorldTfrmUpToDate())
+			updateWorldTfrm();
+
+		return mWorldRotation; 
+	}
+
+	const Vector3& SceneObject::getWorldScale() const 
+	{ 
+		if (!isCachedWorldTfrmUpToDate())
+			updateWorldTfrm();
+
+		return mWorldScale; 
+	}
+
+	void SceneObject::lookAt(const Vector3& location, const Vector3& up)
+	{
+		Vector3 forward = location - getWorldPosition();
+		
+		Quaternion rotation = getWorldRotation();
+		rotation.lookRotation(forward, up);
+		setWorldRotation(rotation);
+	}
+
+	const Matrix4& SceneObject::getWorldTfrm() const
+	{
+		if (!isCachedWorldTfrmUpToDate())
+			updateWorldTfrm();
+
+		return mCachedWorldTfrm;
+	}
+
+	const Matrix4& SceneObject::getLocalTfrm() const
+	{
+		if (!isCachedLocalTfrmUpToDate())
+			updateLocalTfrm();
+
+		return mCachedLocalTfrm;
+	}
+
+	void SceneObject::move(const Vector3& vec)
+	{
+		setPosition(mPosition + vec);
+	}
+
+	void SceneObject::moveRelative(const Vector3& vec)
+	{
+		// Transform the axes of the relative vector by camera's local axes
+		Vector3 trans = mRotation.rotate(vec);
+
+		setPosition(mPosition + trans);
+	}
+
+	void SceneObject::rotate(const Vector3& axis, const Radian& angle)
+	{
+		Quaternion q;
+		q.fromAxisAngle(axis, angle);
+		rotate(q);
+	}
+
+	void SceneObject::rotate(const Quaternion& q)
+	{
+		// Note the order of the mult, i.e. q comes after
+
+		// Normalize the quat to avoid cumulative problems with precision
+		Quaternion qnorm = q;
+		qnorm.normalize();
+		setRotation(qnorm * mRotation);
+	}
+
+	void SceneObject::roll(const Radian& angle)
+	{
+		// Rotate around local Z axis
+		Vector3 zAxis = mRotation.rotate(Vector3::UNIT_Z);
+		rotate(zAxis, angle);
+	}
+
+	void SceneObject::yaw(const Radian& angle)
+	{
+		Vector3 yAxis = mRotation.rotate(Vector3::UNIT_Y);
+		rotate(yAxis, angle);
+	}
+
+	void SceneObject::pitch(const Radian& angle)
+	{
+		// Rotate around local X axis
+		Vector3 xAxis = mRotation.rotate(Vector3::UNIT_X);
+		rotate(xAxis, angle);
+	}
+
+	void SceneObject::setForward(const Vector3& forwardDir)
+	{
+		Quaternion currentRotation = getWorldRotation();
+		currentRotation.lookRotation(forwardDir);
+		setWorldRotation(currentRotation);
+	}
+
+	void SceneObject::updateTransformsIfDirty()
+	{
+		if (!isCachedLocalTfrmUpToDate())
+			updateLocalTfrm();
+
+		if (!isCachedWorldTfrmUpToDate())
+			updateWorldTfrm();
+	}
+
+	void SceneObject::markTfrmDirty() const
+	{
+		mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty;
+		mDirtyHash++;
+
+		for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
+		{
+			(*iter)->markTfrmDirty();
+		}
+	}
+
+	void SceneObject::updateWorldTfrm() const
+	{
+		if(mParent != nullptr)
+		{
+			// Update orientation
+			const Quaternion& parentOrientation = mParent->getWorldRotation();
+			mWorldRotation = parentOrientation * mRotation;
+
+			// Update scale
+			const Vector3& parentScale = mParent->getWorldScale();
+			// Scale own position by parent scale, just combine
+			// as equivalent axes, no shearing
+			mWorldScale = parentScale * mScale;
+
+			// Change position vector based on parent's orientation & scale
+			mWorldPosition = parentOrientation.rotate(parentScale * mPosition);
+
+			// Add altered position vector to parents
+			mWorldPosition += mParent->getWorldPosition();
+
+			mCachedWorldTfrm.setTRS(mWorldPosition, mWorldRotation, mWorldScale);
+		}
+		else
+		{
+			mWorldRotation = mRotation;
+			mWorldPosition = mPosition;
+			mWorldScale = mScale;
+
+			mCachedWorldTfrm = getLocalTfrm();
+		}
+
+		mDirtyFlags &= ~DirtyFlags::WorldTfrmDirty;
+	}
+
+	void SceneObject::updateLocalTfrm() const
+	{
+		mCachedLocalTfrm.setTRS(mPosition, mRotation, mScale);
+
+		mDirtyFlags &= ~DirtyFlags::LocalTfrmDirty;
+	}
+
+	/************************************************************************/
+	/* 								Hierarchy	                     		*/
+	/************************************************************************/
+
+	void SceneObject::setParent(const HSceneObject& parent, bool keepWorldTransform)
+	{
+		if (parent.isDestroyed())
+			return;
+
+#if BS_EDITOR_BUILD
+		String originalPrefab = getPrefabLink();
+#endif
+
+		_setParent(parent, keepWorldTransform);
+
+#if BS_EDITOR_BUILD
+		String newPrefab = getPrefabLink();
+		if (originalPrefab != newPrefab)
+			PrefabUtility::clearPrefabIds(mThisHandle);
+#endif
+	}
+
+	void SceneObject::_setParent(const HSceneObject& parent, bool keepWorldTransform)
+	{
+		if (mThisHandle == parent)
+			return;
+
+		if (mParent == nullptr || mParent != parent)
+		{
+			Vector3 worldPos;
+			Quaternion worldRot;
+			Vector3 worldScale;
+
+			if (keepWorldTransform)
+			{
+				// Make sure the object keeps its world coordinates
+				worldPos = getWorldPosition();
+				worldRot = getWorldRotation();
+				worldScale = getWorldScale();
+			}
+
+			if (mParent != nullptr)
+				mParent->removeChild(mThisHandle);
+
+			if (parent != nullptr)
+				parent->addChild(mThisHandle);
+
+			mParent = parent;
+
+			if (keepWorldTransform)
+			{
+				setWorldPosition(worldPos);
+				setWorldRotation(worldRot);
+				setWorldScale(worldScale);
+			}
+
+			markTfrmDirty();
+		}
+	}
+
+	HSceneObject SceneObject::getChild(UINT32 idx) const
+	{
+		if(idx < 0 || idx >= mChildren.size())
+		{
+			BS_EXCEPT(InternalErrorException, "Child index out of range.");
+		}
+
+		return mChildren[idx];
+	}
+
+	int SceneObject::indexOfChild(const HSceneObject& child) const
+	{
+		for(int i = 0; i < (int)mChildren.size(); i++)
+		{
+			if(mChildren[i] == child)
+				return i;
+		}
+
+		return -1;
+	}
+
+	void SceneObject::addChild(const HSceneObject& object)
+	{
+		mChildren.push_back(object); 
+
+		object->setFlags(mFlags);
+	}
+
+	void SceneObject::removeChild(const HSceneObject& object)
+	{
+		auto result = find(mChildren.begin(), mChildren.end(), object);
+
+		if(result != mChildren.end())
+			mChildren.erase(result);
+		else
+		{
+			BS_EXCEPT(InternalErrorException, 
+				"Trying to remove a child but it's not a child of the transform.");
+		}
+	}
+
+	HSceneObject SceneObject::findChild(const String& name, bool recursive)
+	{
+		for (auto& child : mChildren)
+		{
+			if (child->getName() == name)
+				return child;
+		}
+
+		if (recursive)
+		{
+			for (auto& child : mChildren)
+			{
+				HSceneObject foundObject = child->findChild(name, true);
+				if (foundObject != nullptr)
+					return foundObject;
+			}
+		}
+
+		return HSceneObject();
+	}
+
+	Vector<HSceneObject> SceneObject::findChildren(const String& name, bool recursive)
+	{
+		std::function<void(const HSceneObject&, Vector<HSceneObject>&)> findChildrenInternal = 
+			[&](const HSceneObject& so, Vector<HSceneObject>& output)
+		{
+			for (auto& child : so->mChildren)
+			{
+				if (child->getName() == name)
+					output.push_back(child);
+			}
+
+			if (recursive)
+			{
+				for (auto& child : so->mChildren)
+					findChildrenInternal(child, output);
+			}
+		};
+
+		Vector<HSceneObject> output;
+		findChildrenInternal(mThisHandle, output);
+
+		return output;
+	}
+
+	void SceneObject::setActive(bool active)
+	{
+		mActiveSelf = active;
+		setActiveHierarchy(active);
+	}
+
+	void SceneObject::setActiveHierarchy(bool active) 
+	{ 
+		bool activeHierarchy = active && mActiveSelf;
+
+		if (mActiveHierarchy != activeHierarchy)
+		{
+			mActiveHierarchy = activeHierarchy;
+
+			if (activeHierarchy)
+			{
+				for (auto& component : mComponents)
+					component->onEnabled();
+			}
+			else
+			{
+				for (auto& component : mComponents)
+					component->onDisabled();
+			}
+		}
+		
+		for (auto child : mChildren)
+		{
+			child->setActiveHierarchy(mActiveHierarchy);
+		}
+	}
+
+	bool SceneObject::getActive(bool self)
+	{
+		if (self)
+			return mActiveSelf;
+		else
+			return mActiveHierarchy;
+	}
+
+	HSceneObject SceneObject::clone(bool instantiate)
+	{
+		if (!instantiate)
+			setFlags(SOF_DontInstantiate);
+
+		UINT32 bufferSize = 0;
+
+		MemorySerializer serializer;
+		UINT8* buffer = serializer.encode(this, bufferSize, (void*(*)(UINT32))&bs_alloc);
+
+		GameObjectManager::instance().setDeserializationMode(GODM_UseNewIds | GODM_RestoreExternal);
+		std::shared_ptr<SceneObject> cloneObj = std::static_pointer_cast<SceneObject>(serializer.decode(buffer, bufferSize));
+		bs_free(buffer);
+
+		if (!instantiate)
+			unsetFlags(SOF_DontInstantiate);
+
+		return cloneObj->mThisHandle;
+	}
+
+	HComponent SceneObject::getComponent(UINT32 typeId) const
+	{
+		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
+		{
+			if((*iter)->getRTTI()->getRTTIId() == typeId)
+				return *iter;
+		}
+
+		return HComponent();
+	}
+
+	void SceneObject::destroyComponent(const HComponent component, bool immediate)
+	{
+		if(component == nullptr)
+		{
+			LOGDBG("Trying to remove a null component");
+			return;
+		}
+
+		auto iter = std::find(mComponents.begin(), mComponents.end(), component);
+
+		if(iter != mComponents.end())
+		{
+			(*iter)->_setIsDestroyed();
+
+			if (isInstantiated())
+			{
+				if (getActive())
+					component->onDisabled();
+
+				(*iter)->onDestroyed();
+			}
+			
+			(*iter)->destroyInternal(*iter, immediate);
+			mComponents.erase(iter);
+		}
+		else
+			LOGDBG("Trying to remove a component that doesn't exist on this SceneObject.");
+	}
+
+	void SceneObject::destroyComponent(Component* component, bool immediate)
+	{
+		auto iterFind = std::find_if(mComponents.begin(), mComponents.end(), 
+			[component](const HComponent& x) 
+		{ 
+			if(x.isDestroyed())
+				return false;
+
+			return x._getHandleData()->mPtr->object.get() == component; }
+		);
+
+		if(iterFind != mComponents.end())
+		{
+			destroyComponent(*iterFind, immediate);
+		}
+	}
+
+	void SceneObject::addComponentInternal(const std::shared_ptr<Component> component)
+	{
+		GameObjectHandle<Component> newComponent = GameObjectManager::instance().getObject(component->getInstanceId());
+		newComponent->mParent = mThisHandle;
+		mComponents.push_back(newComponent);
+	}
+
+	RTTITypeBase* SceneObject::getRTTIStatic()
+	{
+		return SceneObjectRTTI::instance();
+	}
+
+	RTTITypeBase* SceneObject::getRTTI() const
+	{
+		return SceneObject::getRTTIStatic();
+	}
 }

+ 147 - 147
BansheeEditor/Source/BsCodeEditor.cpp

@@ -1,148 +1,148 @@
-#include "BsCodeEditor.h"
-#include "BsEditorApplication.h"
-#include "BsProjectLibrary.h"
-#include "BsProjectResourceMeta.h"
-#include "BsScriptCodeImportOptions.h"
-#include "BsBuildManager.h"
-
-#if BS_PLATFORM == BS_PLATFORM_WIN32
-#include "Win32/BsVSCodeEditor.h"
-#else
-static_assert("Make sure to add implementations for other platforms.");
-#endif
-
-namespace BansheeEngine
-{
-	CodeEditorManager::CodeEditorManager()
-		:mActiveEditor(nullptr), mActiveEditorType(CodeEditorType::None)
-	{
-#if BS_PLATFORM == BS_PLATFORM_WIN32
-		VSCodeEditorFactory* vsCodeEditorFactory = bs_new<VSCodeEditorFactory>();
-		Vector<CodeEditorType> vsEditors = vsCodeEditorFactory->getAvailableEditors();
-		for(auto& editor : vsEditors)
-		{
-			mFactoryPerEditor[editor] = vsCodeEditorFactory;
-			mEditors.push_back(editor);
-		}
-
-		mFactories.push_back(vsCodeEditorFactory);
-#else
-		static_assert("Make sure to add implementations for other platforms.");
-#endif
-	}
-
-	CodeEditorManager::~CodeEditorManager()
-	{
-		for (auto& factory : mFactories)
-			bs_delete(factory);
-
-		if (mActiveEditor != nullptr)
-			bs_delete(mActiveEditor);
-	}
-
-	void CodeEditorManager::setActive(CodeEditorType editor)
-	{
-		if (mActiveEditor != nullptr)
-		{
-			bs_delete(mActiveEditor);
-			mActiveEditor = nullptr;
-		}
-
-		auto findIter = mFactoryPerEditor.find(editor);
-		if (findIter == mFactoryPerEditor.end())
-			return;
-
-		mActiveEditor = findIter->second->create(editor);
-		mActiveEditorType = editor;
-	}
-
-	void CodeEditorManager::openFile(const Path& path, UINT32 lineNumber) const
-	{
-		if (mActiveEditor == nullptr)
-			return;
-
-		Path filePath = path;
-		if (!path.isAbsolute())
-			filePath.makeAbsolute(gProjectLibrary().getResourcesFolder());
-
-		mActiveEditor->openFile(getSolutionPath(), filePath, lineNumber);
-	}
-
-	void CodeEditorManager::syncSolution() const
-	{
-		if (mActiveEditor == nullptr)
-			return;
-
-		CodeSolutionData slnData;
-		slnData.name = gEditorApplication().getProjectName();
-
-		Vector<UINT32> scriptTypeIds =
-		{
-			TID_ScriptCode, TID_PlainText, TID_Shader, TID_ShaderInclude
-		};
-
-		Vector<ProjectLibrary::LibraryEntry*> libraryEntries = gProjectLibrary().search(L"*", scriptTypeIds);
-		
-		PlatformType activePlatform = BuildManager::instance().getActivePlatform();
-		Vector<WString> frameworkAssemblies = BuildManager::instance().getFrameworkAssemblies(activePlatform);
-
-		slnData.projects.push_back(CodeProjectData());
-		slnData.projects.push_back(CodeProjectData());
-
-		// Game project
-		CodeProjectData& gameProject = slnData.projects[0];
-		gameProject.name = toWString(SCRIPT_GAME_ASSEMBLY);
-		gameProject.defines = BuildManager::instance().getDefines(activePlatform);
-		
-		//// Add references
-		gameProject.assemblyReferences.push_back(CodeProjectReference{ toWString(ENGINE_ASSEMBLY), gApplication().getEngineAssemblyPath() });
-		for (auto& assemblyName : frameworkAssemblies)
-			gameProject.assemblyReferences.push_back(CodeProjectReference{ assemblyName, Path::BLANK });
-
-		// Editor project
-		CodeProjectData& editorProject = slnData.projects[1];
-		editorProject.name = toWString(SCRIPT_EDITOR_ASSEMBLY);
-
-		//// Add references
-		editorProject.assemblyReferences.push_back(CodeProjectReference{ toWString(ENGINE_ASSEMBLY), gApplication().getEngineAssemblyPath() });
-		editorProject.assemblyReferences.push_back(CodeProjectReference{ toWString(EDITOR_ASSEMBLY), gEditorApplication().getEditorAssemblyPath() });
-		for (auto& assemblyName : frameworkAssemblies)
-			gameProject.assemblyReferences.push_back(CodeProjectReference{ assemblyName, Path::BLANK });
-
-		editorProject.projectReferences.push_back(CodeProjectReference{ gameProject.name, Path::BLANK });
-
-		//// Add files for both projects
-		for (auto& entry : libraryEntries)
-		{
-			if (entry->type != ProjectLibrary::LibraryEntryType::File)
-				continue;
-
-			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
-			if (resEntry->meta->getTypeID() == TID_ScriptCode)
-			{
-				SPtr<ScriptCodeImportOptions> scriptIO = std::static_pointer_cast<ScriptCodeImportOptions>(resEntry->meta->getImportOptions());
-
-				bool isEditorScript = false;
-				if (scriptIO != nullptr)
-					isEditorScript = scriptIO->isEditorScript();
-
-				if (isEditorScript)
-					editorProject.codeFiles.push_back(resEntry->path);
-				else
-					gameProject.codeFiles.push_back(resEntry->path);
-			}
-			else
-				gameProject.nonCodeFiles.push_back(resEntry->path);
-		}
-
-		mActiveEditor->syncSolution(slnData, gEditorApplication().getProjectPath());
-	}
-
-	Path CodeEditorManager::getSolutionPath() const
-	{
-		Path path = gEditorApplication().getProjectPath();
-		path.append(gEditorApplication().getProjectName() + L".sln");
-
-		return path;
-	}
+#include "BsCodeEditor.h"
+#include "BsEditorApplication.h"
+#include "BsProjectLibrary.h"
+#include "BsProjectResourceMeta.h"
+#include "BsScriptCodeImportOptions.h"
+#include "BsBuildManager.h"
+
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+#include "Win32/BsVSCodeEditor.h"
+#else
+// Add implementations for code editors on other platforms.
+#endif
+
+namespace BansheeEngine
+{
+	CodeEditorManager::CodeEditorManager()
+		:mActiveEditor(nullptr), mActiveEditorType(CodeEditorType::None)
+	{
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+		VSCodeEditorFactory* vsCodeEditorFactory = bs_new<VSCodeEditorFactory>();
+		Vector<CodeEditorType> vsEditors = vsCodeEditorFactory->getAvailableEditors();
+		for(auto& editor : vsEditors)
+		{
+			mFactoryPerEditor[editor] = vsCodeEditorFactory;
+			mEditors.push_back(editor);
+		}
+
+		mFactories.push_back(vsCodeEditorFactory);
+#else
+		// Add implementations for code editors on other platforms.
+#endif
+	}
+
+	CodeEditorManager::~CodeEditorManager()
+	{
+		for (auto& factory : mFactories)
+			bs_delete(factory);
+
+		if (mActiveEditor != nullptr)
+			bs_delete(mActiveEditor);
+	}
+
+	void CodeEditorManager::setActive(CodeEditorType editor)
+	{
+		if (mActiveEditor != nullptr)
+		{
+			bs_delete(mActiveEditor);
+			mActiveEditor = nullptr;
+		}
+
+		auto findIter = mFactoryPerEditor.find(editor);
+		if (findIter == mFactoryPerEditor.end())
+			return;
+
+		mActiveEditor = findIter->second->create(editor);
+		mActiveEditorType = editor;
+	}
+
+	void CodeEditorManager::openFile(const Path& path, UINT32 lineNumber) const
+	{
+		if (mActiveEditor == nullptr)
+			return;
+
+		Path filePath = path;
+		if (!path.isAbsolute())
+			filePath.makeAbsolute(gProjectLibrary().getResourcesFolder());
+
+		mActiveEditor->openFile(getSolutionPath(), filePath, lineNumber);
+	}
+
+	void CodeEditorManager::syncSolution() const
+	{
+		if (mActiveEditor == nullptr)
+			return;
+
+		CodeSolutionData slnData;
+		slnData.name = gEditorApplication().getProjectName();
+
+		Vector<UINT32> scriptTypeIds =
+		{
+			TID_ScriptCode, TID_PlainText, TID_Shader, TID_ShaderInclude
+		};
+
+		Vector<ProjectLibrary::LibraryEntry*> libraryEntries = gProjectLibrary().search(L"*", scriptTypeIds);
+		
+		PlatformType activePlatform = BuildManager::instance().getActivePlatform();
+		Vector<WString> frameworkAssemblies = BuildManager::instance().getFrameworkAssemblies(activePlatform);
+
+		slnData.projects.push_back(CodeProjectData());
+		slnData.projects.push_back(CodeProjectData());
+
+		// Game project
+		CodeProjectData& gameProject = slnData.projects[0];
+		gameProject.name = toWString(SCRIPT_GAME_ASSEMBLY);
+		gameProject.defines = BuildManager::instance().getDefines(activePlatform);
+		
+		//// Add references
+		gameProject.assemblyReferences.push_back(CodeProjectReference{ toWString(ENGINE_ASSEMBLY), gApplication().getEngineAssemblyPath() });
+		for (auto& assemblyName : frameworkAssemblies)
+			gameProject.assemblyReferences.push_back(CodeProjectReference{ assemblyName, Path::BLANK });
+
+		// Editor project
+		CodeProjectData& editorProject = slnData.projects[1];
+		editorProject.name = toWString(SCRIPT_EDITOR_ASSEMBLY);
+
+		//// Add references
+		editorProject.assemblyReferences.push_back(CodeProjectReference{ toWString(ENGINE_ASSEMBLY), gApplication().getEngineAssemblyPath() });
+		editorProject.assemblyReferences.push_back(CodeProjectReference{ toWString(EDITOR_ASSEMBLY), gEditorApplication().getEditorAssemblyPath() });
+		for (auto& assemblyName : frameworkAssemblies)
+			gameProject.assemblyReferences.push_back(CodeProjectReference{ assemblyName, Path::BLANK });
+
+		editorProject.projectReferences.push_back(CodeProjectReference{ gameProject.name, Path::BLANK });
+
+		//// Add files for both projects
+		for (auto& entry : libraryEntries)
+		{
+			if (entry->type != ProjectLibrary::LibraryEntryType::File)
+				continue;
+
+			ProjectLibrary::ResourceEntry* resEntry = static_cast<ProjectLibrary::ResourceEntry*>(entry);
+			if (resEntry->meta->getTypeID() == TID_ScriptCode)
+			{
+				SPtr<ScriptCodeImportOptions> scriptIO = std::static_pointer_cast<ScriptCodeImportOptions>(resEntry->meta->getImportOptions());
+
+				bool isEditorScript = false;
+				if (scriptIO != nullptr)
+					isEditorScript = scriptIO->isEditorScript();
+
+				if (isEditorScript)
+					editorProject.codeFiles.push_back(resEntry->path);
+				else
+					gameProject.codeFiles.push_back(resEntry->path);
+			}
+			else
+				gameProject.nonCodeFiles.push_back(resEntry->path);
+		}
+
+		mActiveEditor->syncSolution(slnData, gEditorApplication().getProjectPath());
+	}
+
+	Path CodeEditorManager::getSolutionPath() const
+	{
+		Path path = gEditorApplication().getProjectPath();
+		path.append(gEditorApplication().getProjectName() + L".sln");
+
+		return path;
+	}
 }

+ 285 - 269
BansheeUtility/Include/BsQuaternion.h

@@ -1,270 +1,286 @@
-#pragma once
-
-#include "BsPrerequisitesUtil.h"
-#include "BsMath.h"
-#include "BsVector3.h"
-
-namespace BansheeEngine 
-{
-    /**
-     * @brief	Represents a quaternion used for 3D rotations.
-     */
-    class BS_UTILITY_EXPORT Quaternion
-    {
-	private:
-		struct EulerAngleOrderData
-		{
-			int a, b, c;
-		};
-
-    public:
-        Quaternion(float w = 1.0f, float x = 0.0f, float y = 0.0f, float z = 0.0f)
-			:w(w), z(z), y(y), x(x)
-		{ }
-
-        /**
-         * @brief	Construct a quaternion from a rotation matrix.
-         */
-        explicit Quaternion(const Matrix3& rot)
-        {
-            fromRotationMatrix(rot);
-        }
-
-        /**
-         * @brief	Construct a quaternion from an angle/axis.
-         */
-        explicit Quaternion(const Vector3& axis, const Radian& angle)
-        {
-            fromAxisAngle(axis, angle);
-        }
-
-        /**
-         * @brief	Construct a quaternion from 3 orthonormal local axes.
-         */
-        explicit Quaternion(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
-        {
-            fromAxes(xaxis, yaxis, zaxis);
-        }
-
-        /**
-         * @brief	Construct a quaternion from euler angles, YXZ ordering.
-         * 			
-		 * @see		Quaternion::fromEulerAngles
-         */
-		explicit Quaternion(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle)
-		{
-			fromEulerAngles(xAngle, yAngle, zAngle);
-		}
-
-        /**
-         * @brief	Construct a quaternion from euler angles, custom ordering.
-         * 			
-		 * @see		Quaternion::fromEulerAngles
-         */
-		explicit Quaternion(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order)
-		{
-			fromEulerAngles(xAngle, yAngle, zAngle, order);
-		}
-
-		/**
-		 * @brief	Exchange the contents of this quaternion with another.
-		 */
-		void swap(Quaternion& other)
-		{
-			std::swap(w, other.w);
-			std::swap(x, other.x);
-			std::swap(y, other.y);
-			std::swap(z, other.z);
-		}
-
-		float operator[] (const size_t i) const
-		{
-			assert(i < 4);
-
-			return *(&w+i);
-		}
-
-		float& operator[] (const size_t i)
-		{
-			assert(i < 4);
-
-			return *(&w+i);
-		}
-
-		/**
-		 * @brief	Initializes the quaternion from a 3x3 rotation matrix.
-		 * 			
-		 * @note	It's up to the caller to ensure the matrix is orthonormal.
-		 */
-		void fromRotationMatrix(const Matrix3& mat);
-
-		/**
-		 * @brief	Initializes the quaternion from an angle axis pair. Quaternion
-		 * 			will represent a rotation of "angle" radians around "axis".
-		 */
-        void fromAxisAngle(const Vector3& axis, const Radian& angle);
-
-        /**
-         * @brief	Initializes the quaternion from orthonormal set of axes. Quaternion
-         * 			will represent a rotation from base axes to the specified set of axes.
-         * 			
-		 * @note	It's up to the caller to ensure the axes are orthonormal.
-         */
-        void fromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis);
-        
-        /**
-         * @brief	Creates a quaternion from the provided Pitch/Yaw/Roll angles.
-         *
-		 * @param	xAngle	Rotation about x axis. (AKA Pitch)
-		 * @param	yAngle	Rotation about y axis. (AKA Yaw)
-		 * @param	zAngle	Rotation about z axis. (AKA Roll)
-         *
-         * @note	Since different values will be produced depending in which order are the rotations applied, this method assumes
-		 * 			they are applied in YXZ order. If you need a specific order, use the overloaded "fromEulerAngles" method instead.
-         */
-        void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle);
-
-        /**
-         * @brief	Creates a quaternion from the provided Pitch/Yaw/Roll angles.
-         *
-		 * @param	xAngle	Rotation about x axis. (AKA Pitch)
-		 * @param	yAngle	Rotation about y axis. (AKA Yaw)
-		 * @param	zAngle	Rotation about z axis. (AKA Roll)
-		 * @param	order 	The order in which rotations will be extracted.
-		 * 					Different values can be retrieved depending on the order.
-         */
-        void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order);
-
-		/**
-		 * @brief	Converts a quaternion to a rotation matrix.
-		 */
-		void toRotationMatrix(Matrix3& mat) const;
-
-		/**
-		 * @brief	Converts a quaternion to an angle axis pair.
-		 *
-		 * @param [out]	axis 	The axis around the which rotation takes place.
-		 * @param [out]	angle	The angle in radians determining amount of rotation around the axis.
-		 */
-		void toAxisAngle(Vector3& axis, Radian& angle) const;
-
-		/**
-		 * @brief	Converts a quaternion to an orthonormal set of axes.
-		 *
-		 * @param [out]	xAxis	The X axis.
-		 * @param [out]	yAxis	The Y axis.
-		 * @param [out]	zAxis	The Z axis.
-		 */
-		void toAxes(Vector3& xAxis, Vector3& yAxis, Vector3& zAxis) const;
-
-		/**
-         * @brief	Extracts Pitch/Yaw/Roll rotations from this quaternion.
-         *
-         * @param [out]	xAngle	Rotation about x axis. (AKA Pitch)
-         * @param [out]	yAngle  Rotation about y axis. (AKA Yaw)
-         * @param [out]	zAngle 	Rotation about z axis. (AKA Roll)
-         *
-         * @return	True if unique solution was found, false otherwise.
-         */
-        bool toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const;
-
-        /**
-         * @brief	Gets the positive x-axis of the coordinate system transformed by this quaternion.
-         */
-        Vector3 xAxis() const;
-
-        /**
-         * @brief	Gets the positive y-axis of the coordinate system transformed by this quaternion.
-         */
-        Vector3 yAxis() const;
-
-		/**
-         * @brief	Gets the positive z-axis of the coordinate system transformed by this quaternion.
-         */
-        Vector3 zAxis() const;
-
-        Quaternion& operator= (const Quaternion& rhs)
-		{
-			w = rhs.w;
-			x = rhs.x;
-			y = rhs.y;
-			z = rhs.z;
-
-			return *this;
-		}
-
-        Quaternion operator+ (const Quaternion& rhs) const;
-        Quaternion operator- (const Quaternion& rhs) const;
-        Quaternion operator* (const Quaternion& rhs) const;
-        Quaternion operator* (float rhs) const;
-        Quaternion operator- () const;
-
-        bool operator== (const Quaternion& rhs) const
-		{
-			return (rhs.x == x) && (rhs.y == y) && (rhs.z == z) && (rhs.w == w);
-		}
-
-        bool operator!= (const Quaternion& rhs) const
-		{
-			return !operator==(rhs);
-		}
-
-		friend Quaternion operator* (float lhs, const Quaternion& rhs);
-
-        /**
-         * @brief	Calculates the dot product of this quaternion and another.
-         */
-        float dot(const Quaternion& other) const;  
-
-        /**
-         * @brief	Normalizes this quaternion, and returns the previous length.
-         */
-        float normalize(); 
-
-        /**
-         * @brief	Gets the inverse.
-         *
-         * @note	Quaternion must be non-zero.
-         */
-        Quaternion inverse() const; 
-
-        /**
-         * @brief	Rotates the provided vector.
-         */
-        Vector3 rotate(const Vector3& vec) const;
-
-		/**
-		* @brief	Query if any of the components of the quaternion are NaN.
-		 */
-		bool isNaN() const
-		{
-			return Math::isNaN(x) || Math::isNaN(y) || Math::isNaN(z) || Math::isNaN(w);
-		}
-
-        /**
-         * @brief	Performs spherical interpolation between two quaternions. Spherical interpolation 
-         *          neatly interpolates between two rotations without modifying the size of the vector 
-         *          it is applied to (unlike linear interpolation).
-         */
-        static Quaternion slerp(float t, const Quaternion& p,
-            const Quaternion& q, bool shortestPath = false);
-
-        /**
-		 * @brief	Gets the shortest arc quaternion to rotate this vector to the destination
-		 *		vector.
-         */
-        static Quaternion getRotationFromTo(const Vector3& from, const Vector3& dest, const Vector3& fallbackAxis = Vector3::ZERO);
-
-        static const float EPSILON;
-
-        static const Quaternion ZERO;
-        static const Quaternion IDENTITY;
-
-		float x, y, z, w;
-
-		private:
-			static const EulerAngleOrderData EA_LOOKUP[6];
-    };
-
-	BS_ALLOW_MEMCPY_SERIALIZATION(Quaternion);
+#pragma once
+
+#include "BsPrerequisitesUtil.h"
+#include "BsMath.h"
+#include "BsVector3.h"
+
+namespace BansheeEngine 
+{
+    /**
+     * @brief	Represents a quaternion used for 3D rotations.
+     */
+    class BS_UTILITY_EXPORT Quaternion
+    {
+	private:
+		struct EulerAngleOrderData
+		{
+			int a, b, c;
+		};
+
+    public:
+        Quaternion(float w = 1.0f, float x = 0.0f, float y = 0.0f, float z = 0.0f)
+			:w(w), z(z), y(y), x(x)
+		{ }
+
+        /**
+         * @brief	Construct a quaternion from a rotation matrix.
+         */
+        explicit Quaternion(const Matrix3& rot)
+        {
+            fromRotationMatrix(rot);
+        }
+
+        /**
+         * @brief	Construct a quaternion from an angle/axis.
+         */
+        explicit Quaternion(const Vector3& axis, const Radian& angle)
+        {
+            fromAxisAngle(axis, angle);
+        }
+
+        /**
+         * @brief	Construct a quaternion from 3 orthonormal local axes.
+         */
+        explicit Quaternion(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
+        {
+            fromAxes(xaxis, yaxis, zaxis);
+        }
+
+        /**
+         * @brief	Construct a quaternion from euler angles, YXZ ordering.
+         * 			
+		 * @see		Quaternion::fromEulerAngles
+         */
+		explicit Quaternion(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle)
+		{
+			fromEulerAngles(xAngle, yAngle, zAngle);
+		}
+
+        /**
+         * @brief	Construct a quaternion from euler angles, custom ordering.
+         * 			
+		 * @see		Quaternion::fromEulerAngles
+         */
+		explicit Quaternion(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order)
+		{
+			fromEulerAngles(xAngle, yAngle, zAngle, order);
+		}
+
+		/**
+		 * @brief	Exchange the contents of this quaternion with another.
+		 */
+		void swap(Quaternion& other)
+		{
+			std::swap(w, other.w);
+			std::swap(x, other.x);
+			std::swap(y, other.y);
+			std::swap(z, other.z);
+		}
+
+		float operator[] (const size_t i) const
+		{
+			assert(i < 4);
+
+			return *(&w+i);
+		}
+
+		float& operator[] (const size_t i)
+		{
+			assert(i < 4);
+
+			return *(&w+i);
+		}
+
+		/**
+		 * @brief	Initializes the quaternion from a 3x3 rotation matrix.
+		 * 			
+		 * @note	It's up to the caller to ensure the matrix is orthonormal.
+		 */
+		void fromRotationMatrix(const Matrix3& mat);
+
+		/**
+		 * @brief	Initializes the quaternion from an angle axis pair. Quaternion
+		 * 			will represent a rotation of "angle" radians around "axis".
+		 */
+        void fromAxisAngle(const Vector3& axis, const Radian& angle);
+
+        /**
+         * @brief	Initializes the quaternion from orthonormal set of axes. Quaternion
+         * 			will represent a rotation from base axes to the specified set of axes.
+         * 			
+		 * @note	It's up to the caller to ensure the axes are orthonormal.
+         */
+        void fromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis);
+        
+        /**
+         * @brief	Creates a quaternion from the provided Pitch/Yaw/Roll angles.
+         *
+		 * @param	xAngle	Rotation about x axis. (AKA Pitch)
+		 * @param	yAngle	Rotation about y axis. (AKA Yaw)
+		 * @param	zAngle	Rotation about z axis. (AKA Roll)
+         *
+         * @note	Since different values will be produced depending in which order are the rotations applied, this method assumes
+		 * 			they are applied in YXZ order. If you need a specific order, use the overloaded "fromEulerAngles" method instead.
+         */
+        void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle);
+
+        /**
+         * @brief	Creates a quaternion from the provided Pitch/Yaw/Roll angles.
+         *
+		 * @param	xAngle	Rotation about x axis. (AKA Pitch)
+		 * @param	yAngle	Rotation about y axis. (AKA Yaw)
+		 * @param	zAngle	Rotation about z axis. (AKA Roll)
+		 * @param	order 	The order in which rotations will be extracted.
+		 * 					Different values can be retrieved depending on the order.
+         */
+        void fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order);
+
+		/**
+		 * @brief	Converts a quaternion to a rotation matrix.
+		 */
+		void toRotationMatrix(Matrix3& mat) const;
+
+		/**
+		 * @brief	Converts a quaternion to an angle axis pair.
+		 *
+		 * @param [out]	axis 	The axis around the which rotation takes place.
+		 * @param [out]	angle	The angle in radians determining amount of rotation around the axis.
+		 */
+		void toAxisAngle(Vector3& axis, Radian& angle) const;
+
+		/**
+		 * @brief	Converts a quaternion to an orthonormal set of axes.
+		 *
+		 * @param [out]	xAxis	The X axis.
+		 * @param [out]	yAxis	The Y axis.
+		 * @param [out]	zAxis	The Z axis.
+		 */
+		void toAxes(Vector3& xAxis, Vector3& yAxis, Vector3& zAxis) const;
+
+		/**
+         * @brief	Extracts Pitch/Yaw/Roll rotations from this quaternion.
+         *
+         * @param [out]	xAngle	Rotation about x axis. (AKA Pitch)
+         * @param [out]	yAngle  Rotation about y axis. (AKA Yaw)
+         * @param [out]	zAngle 	Rotation about z axis. (AKA Roll)
+         *
+         * @return	True if unique solution was found, false otherwise.
+         */
+        bool toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const;
+
+        /**
+         * @brief	Gets the positive x-axis of the coordinate system transformed by this quaternion.
+         */
+        Vector3 xAxis() const;
+
+        /**
+         * @brief	Gets the positive y-axis of the coordinate system transformed by this quaternion.
+         */
+        Vector3 yAxis() const;
+
+		/**
+         * @brief	Gets the positive z-axis of the coordinate system transformed by this quaternion.
+         */
+        Vector3 zAxis() const;
+
+        Quaternion& operator= (const Quaternion& rhs)
+		{
+			w = rhs.w;
+			x = rhs.x;
+			y = rhs.y;
+			z = rhs.z;
+
+			return *this;
+		}
+
+        Quaternion operator+ (const Quaternion& rhs) const;
+        Quaternion operator- (const Quaternion& rhs) const;
+        Quaternion operator* (const Quaternion& rhs) const;
+        Quaternion operator* (float rhs) const;
+        Quaternion operator- () const;
+
+        bool operator== (const Quaternion& rhs) const
+		{
+			return (rhs.x == x) && (rhs.y == y) && (rhs.z == z) && (rhs.w == w);
+		}
+
+        bool operator!= (const Quaternion& rhs) const
+		{
+			return !operator==(rhs);
+		}
+
+		friend Quaternion operator* (float lhs, const Quaternion& rhs);
+
+        /**
+         * @brief	Calculates the dot product of this quaternion and another.
+         */
+        float dot(const Quaternion& other) const;  
+
+        /**
+         * @brief	Normalizes this quaternion, and returns the previous length.
+         */
+        float normalize(); 
+
+        /**
+         * @brief	Gets the inverse.
+         *
+         * @note	Quaternion must be non-zero.
+         */
+        Quaternion inverse() const; 
+
+        /**
+         * @brief	Rotates the provided vector.
+         */
+        Vector3 rotate(const Vector3& vec) const;
+
+        /**
+         * @brief	Orients the quaternion so its negative z axis points to the provided direction.
+		 *
+		 * @param	forwardDir	Direction to orient towards.
+         */
+		void lookRotation(const Vector3& forwardDir);
+
+        /**
+         * @brief	Orients the quaternion so its negative z axis points to the provided direction.
+		 *
+		 * @param	forwardDir	Direction to orient towards.
+		 * @param	upDir		Constrains y axis orientation to a plane this vector lies on. This rule might be broken
+		 *						if forward and up direction are nearly parallel.
+         */
+		void lookRotation(const Vector3& forwardDir, const Vector3& upDir);
+
+		/**
+		* @brief	Query if any of the components of the quaternion are NaN.
+		 */
+		bool isNaN() const
+		{
+			return Math::isNaN(x) || Math::isNaN(y) || Math::isNaN(z) || Math::isNaN(w);
+		}
+
+        /**
+         * @brief	Performs spherical interpolation between two quaternions. Spherical interpolation 
+         *          neatly interpolates between two rotations without modifying the size of the vector 
+         *          it is applied to (unlike linear interpolation).
+         */
+        static Quaternion slerp(float t, const Quaternion& p,
+            const Quaternion& q, bool shortestPath = true);
+
+        /**
+		 * @brief	Gets the shortest arc quaternion to rotate this vector to the destination
+		 *		vector.
+         */
+        static Quaternion getRotationFromTo(const Vector3& from, const Vector3& dest, const Vector3& fallbackAxis = Vector3::ZERO);
+
+        static const float EPSILON;
+
+        static const Quaternion ZERO;
+        static const Quaternion IDENTITY;
+
+		float x, y, z, w;
+
+		private:
+			static const EulerAngleOrderData EA_LOOKUP[6];
+    };
+
+	BS_ALLOW_MEMCPY_SERIALIZATION(Quaternion);
 }

+ 461 - 418
BansheeUtility/Source/BsQuaternion.cpp

@@ -1,418 +1,461 @@
-#include "BsQuaternion.h"
-
-#include "BsMath.h"
-#include "BsMatrix3.h"
-#include "BsVector3.h"
-
-namespace BansheeEngine 
-{
-    const float Quaternion::EPSILON = 1e-03f;
-    const Quaternion Quaternion::ZERO(0.0f, 0.0f, 0.0f, 0.0f);
-    const Quaternion Quaternion::IDENTITY(1.0f, 0.0f, 0.0f, 0.0f);
-	const Quaternion::EulerAngleOrderData Quaternion::EA_LOOKUP[6] = 
-		{ { 0, 1, 2}, { 0, 2, 1}, { 1, 0, 2},
-		{ 1, 2, 0}, { 2, 0, 1}, { 2, 1, 0} };;
-
-    void Quaternion::fromRotationMatrix(const Matrix3& mat)
-    {
-        // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
-        // article "Quaternion Calculus and Fast Animation".
-
-        float trace = mat[0][0]+mat[1][1]+mat[2][2];
-        float root;
-
-        if (trace > 0.0f)
-        {
-            // |w| > 1/2, may as well choose w > 1/2
-            root = Math::sqrt(trace + 1.0f);  // 2w
-            w = 0.5f*root;
-            root = 0.5f/root;  // 1/(4w)
-            x = (mat[2][1]-mat[1][2])*root;
-            y = (mat[0][2]-mat[2][0])*root;
-            z = (mat[1][0]-mat[0][1])*root;
-        }
-        else
-        {
-            // |w| <= 1/2
-            static UINT32 nextLookup[3] = { 1, 2, 0 };
-            UINT32 i = 0;
-
-            if (mat[1][1] > mat[0][0])
-                i = 1;
-
-            if (mat[2][2] > mat[i][i])
-                i = 2;
-
-            UINT32 j = nextLookup[i];
-            UINT32 k = nextLookup[j];
-
-            root = Math::sqrt(mat[i][i]-mat[j][j]-mat[k][k] + 1.0f);
-
-            float* cmpntLookup[3] = { &x, &y, &z };
-            *cmpntLookup[i] = 0.5f*root;
-            root = 0.5f/root;
-
-            w = (mat[k][j]-mat[j][k])*root;
-            *cmpntLookup[j] = (mat[j][i]+mat[i][j])*root;
-            *cmpntLookup[k] = (mat[k][i]+mat[i][k])*root;
-        }
-
-		normalize();
-    }
-
-    void Quaternion::fromAxisAngle(const Vector3& axis, const Radian& angle)
-    {
-        Radian halfAngle (0.5f*angle);
-        float sin = Math::sin(halfAngle);
-
-        w = Math::cos(halfAngle);
-        x = sin*axis.x;
-        y = sin*axis.y;
-        z = sin*axis.z;
-    }
-
-    void Quaternion::fromAxes(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
-    {
-        Matrix3 kRot;
-
-        kRot[0][0] = xaxis.x;
-        kRot[1][0] = xaxis.y;
-        kRot[2][0] = xaxis.z;
-
-        kRot[0][1] = yaxis.x;
-        kRot[1][1] = yaxis.y;
-        kRot[2][1] = yaxis.z;
-
-        kRot[0][2] = zaxis.x;
-        kRot[1][2] = zaxis.y;
-        kRot[2][2] = zaxis.z;
-
-        fromRotationMatrix(kRot);
-    }
-
-	void Quaternion::fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle)
-	{
-		Radian halfXAngle = xAngle * 0.5f;
-		Radian halfYAngle = yAngle * 0.5f;
-		Radian halfZAngle = zAngle * 0.5f;
-
-		float cx = Math::cos(halfXAngle);
-		float sx = Math::sin(halfXAngle);
-
-		float cy = Math::cos(halfYAngle);
-		float sy = Math::sin(halfYAngle);
-
-		float cz = Math::cos(halfZAngle);
-		float sz = Math::sin(halfZAngle);
-
-		Quaternion quatX(cx, sx, 0.0f, 0.0f);
-		Quaternion quatY(cy, 0.0f, sy, 0.0f);
-		Quaternion quatZ(cz, 0.0f, 0.0f, sz);
-
-		*this = (quatY * quatX) * quatZ;
-	}
-
-	void Quaternion::fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order)
-	{
-		const EulerAngleOrderData& l = EA_LOOKUP[(int)order];
-
-		Radian halfXAngle = xAngle * 0.5f;
-		Radian halfYAngle = yAngle * 0.5f;
-		Radian halfZAngle = zAngle * 0.5f;
-
-		float cx = Math::cos(halfXAngle);
-		float sx = Math::sin(halfXAngle);
-
-		float cy = Math::cos(halfYAngle);
-		float sy = Math::sin(halfYAngle);
-
-		float cz = Math::cos(halfZAngle);
-		float sz = Math::sin(halfZAngle);
-
-		Quaternion quats[3];
-		quats[0] = Quaternion(cx, sx, 0.0f, 0.0f);
-		quats[1] = Quaternion(cy, 0.0f, sy, 0.0f);
-		quats[2] = Quaternion(cz, 0.0f, 0.0f, sz);
-
-		*this = (quats[l.a] * quats[l.b]) * quats[l.c];
-	}
-
-	void Quaternion::toRotationMatrix(Matrix3& mat) const
-	{
-		float tx  = x+x;
-		float ty  = y+y;
-		float fTz  = z+z;
-		float twx = tx*w;
-		float twy = ty*w;
-		float twz = fTz*w;
-		float txx = tx*x;
-		float txy = ty*x;
-		float txz = fTz*x;
-		float tyy = ty*y;
-		float tyz = fTz*y;
-		float tzz = fTz*z;
-
-		mat[0][0] = 1.0f-(tyy+tzz);
-		mat[0][1] = txy-twz;
-		mat[0][2] = txz+twy;
-		mat[1][0] = txy+twz;
-		mat[1][1] = 1.0f-(txx+tzz);
-		mat[1][2] = tyz-twx;
-		mat[2][0] = txz-twy;
-		mat[2][1] = tyz+twx;
-		mat[2][2] = 1.0f-(txx+tyy);
-	}
-
-	void Quaternion::toAxisAngle(Vector3& axis, Radian& angle) const
-	{
-		float sqrLength = x*x+y*y+z*z;
-		if ( sqrLength > 0.0 )
-		{
-			angle = 2.0*Math::acos(w);
-			float invLength = Math::invSqrt(sqrLength);
-			axis.x = x*invLength;
-			axis.y = y*invLength;
-			axis.z = z*invLength;
-		}
-		else
-		{
-			// Angle is 0 (mod 2*pi), so any axis will do
-			angle = Radian(0.0);
-			axis.x = 1.0;
-			axis.y = 0.0;
-			axis.z = 0.0;
-		}
-	}
-
-	void Quaternion::toAxes(Vector3& xaxis, Vector3& yaxis, Vector3& zaxis) const
-	{
-		Matrix3 matRot;
-		toRotationMatrix(matRot);
-
-		xaxis.x = matRot[0][0];
-		xaxis.y = matRot[1][0];
-		xaxis.z = matRot[2][0];
-
-		yaxis.x = matRot[0][1];
-		yaxis.y = matRot[1][1];
-		yaxis.z = matRot[2][1];
-
-		zaxis.x = matRot[0][2];
-		zaxis.y = matRot[1][2];
-		zaxis.z = matRot[2][2];
-	}
-
-	bool Quaternion::toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const
-	{
-		Matrix3 matRot;
-		toRotationMatrix(matRot);
-		return matRot.toEulerAngles(xAngle, yAngle, zAngle);
-	}
-
-    Vector3 Quaternion::xAxis() const
-    {
-        float fTy  = 2.0f*y;
-        float fTz  = 2.0f*z;
-        float fTwy = fTy*w;
-        float fTwz = fTz*w;
-        float fTxy = fTy*x;
-        float fTxz = fTz*x;
-        float fTyy = fTy*y;
-        float fTzz = fTz*z;
-
-        return Vector3(1.0f-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
-    }
-
-    Vector3 Quaternion::yAxis() const
-    {
-        float fTx  = 2.0f*x;
-        float fTy  = 2.0f*y;
-        float fTz  = 2.0f*z;
-        float fTwx = fTx*w;
-        float fTwz = fTz*w;
-        float fTxx = fTx*x;
-        float fTxy = fTy*x;
-        float fTyz = fTz*y;
-        float fTzz = fTz*z;
-
-        return Vector3(fTxy-fTwz, 1.0f-(fTxx+fTzz), fTyz+fTwx);
-    }
-
-    Vector3 Quaternion::zAxis() const
-    {
-        float fTx  = 2.0f*x;
-        float fTy  = 2.0f*y;
-        float fTz  = 2.0f*z;
-        float fTwx = fTx*w;
-        float fTwy = fTy*w;
-        float fTxx = fTx*x;
-        float fTxz = fTz*x;
-        float fTyy = fTy*y;
-        float fTyz = fTz*y;
-
-        return Vector3(fTxz+fTwy, fTyz-fTwx, 1.0f-(fTxx+fTyy));
-    }
-
-    Quaternion Quaternion::operator+ (const Quaternion& rhs) const
-    {
-        return Quaternion(w+rhs.w,x+rhs.x,y+rhs.y,z+rhs.z);
-    }
-
-    Quaternion Quaternion::operator- (const Quaternion& rhs) const
-    {
-        return Quaternion(w-rhs.w,x-rhs.x,y-rhs.y,z-rhs.z);
-    }
-
-    Quaternion Quaternion::operator* (const Quaternion& rhs) const
-    {
-        return Quaternion
-        (
-            w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z,
-            w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y,
-            w * rhs.y + y * rhs.w + z * rhs.x - x * rhs.z,
-            w * rhs.z + z * rhs.w + x * rhs.y - y * rhs.x
-        );
-    }
-
-    Quaternion Quaternion::operator* (float rhs) const
-    {
-        return Quaternion(rhs*w,rhs*x,rhs*y,rhs*z);
-    }
-
-    Quaternion Quaternion::operator- () const
-    {
-        return Quaternion(-w,-x,-y,-z);
-    }
-
-    float Quaternion::dot(const Quaternion& other) const
-    {
-        return w*other.w+x*other.x+y*other.y+z*other.z;
-    }
-
-    Quaternion Quaternion::inverse() const
-    {
-        float fNorm = w*w+x*x+y*y+z*z;
-        if (fNorm > 0.0f)
-        {
-            float fInvNorm = 1.0f/fNorm;
-            return Quaternion(w*fInvNorm,-x*fInvNorm,-y*fInvNorm,-z*fInvNorm);
-        }
-        else
-        {
-            // Return an invalid result to flag the error
-            return ZERO;
-        }
-    }
-
-    Vector3 Quaternion::rotate(const Vector3& v) const
-    {
-		Matrix3 rot;
-		toRotationMatrix(rot);
-		return rot.transform(v);
-    }
-
-    Quaternion Quaternion::slerp(float t, const Quaternion& p, const Quaternion& q, bool shortestPath)
-    {
-        float cos = p.dot(q);
-        Quaternion quat;
-
-        if (cos < 0.0f && shortestPath)
-        {
-            cos = -cos;
-            quat = -q;
-        }
-        else
-        {
-            quat = q;
-        }
-
-        if (Math::abs(cos) < 1 - EPSILON)
-        {
-            // Standard case (slerp)
-            float sin = Math::sqrt(1 - Math::sqr(cos));
-            Radian angle = Math::atan2(sin, cos);
-            float invSin = 1.0f / sin;
-            float coeff0 = Math::sin((1.0f - t) * angle) * invSin;
-            float coeff1 = Math::sin(t * angle) * invSin;
-            return coeff0 * p + coeff1 * quat;
-        }
-        else
-        {
-            // There are two situations:
-            // 1. "p" and "q" are very close (fCos ~= +1), so we can do a linear
-            //    interpolation safely.
-            // 2. "p" and "q" are almost inverse of each other (fCos ~= -1), there
-            //    are an infinite number of possibilities interpolation. but we haven't
-            //    have method to fix this case, so just use linear interpolation here.
-            Quaternion ret = (1.0f - t) * p + t * quat;
-
-            // Taking the complement requires renormalization
-            ret.normalize();
-            return ret;
-        }
-    }
-
-    float Quaternion::normalize()
-    {
-        float len = w*w+x*x+y*y+z*z;
-        float factor = 1.0f / Math::sqrt(len);
-        *this = *this * factor;
-        return len;
-    }
-
-	Quaternion Quaternion::getRotationFromTo(const Vector3& from, const Vector3& dest, const Vector3& fallbackAxis)
-	{
-		// Based on Stan Melax's article in Game Programming Gems
-		Quaternion q;
-
-		Vector3 v0 = from;
-		Vector3 v1 = dest;
-		v0.normalize();
-		v1.normalize();
-
-		float d = v0.dot(v1);
-
-		// If dot == 1, vectors are the same
-		if (d >= 1.0f)
-			return Quaternion::IDENTITY;
-
-		if (d < (1e-6f - 1.0f))
-		{
-			if (fallbackAxis != Vector3::ZERO)
-			{
-				// Rotate 180 degrees about the fallback axis
-				q.fromAxisAngle(fallbackAxis, Radian(Math::PI));
-			}
-			else
-			{
-				// Generate an axis
-				Vector3 axis = Vector3::UNIT_X.cross(from);
-				if (axis.isZeroLength()) // Pick another if colinear
-					axis = Vector3::UNIT_Y.cross(from);
-				axis.normalize();
-				q.fromAxisAngle(axis, Radian(Math::PI));
-			}
-		}
-		else
-		{
-			float s = Math::sqrt( (1+d)*2 );
-			float invs = 1 / s;
-
-			Vector3 c = v0.cross(v1);
-
-			q.x = c.x * invs;
-			q.y = c.y * invs;
-			q.z = c.z * invs;
-			q.w = s * 0.5f;
-			q.normalize();
-		}
-
-		return q;
-	}
-
-	Quaternion operator* (float lhs, const Quaternion& rhs)
-	{
-		return Quaternion(lhs*rhs.w,lhs*rhs.x,lhs*rhs.y,
-			lhs*rhs.z);
-	}
-}
+#include "BsQuaternion.h"
+
+#include "BsMath.h"
+#include "BsMatrix3.h"
+#include "BsVector3.h"
+
+namespace BansheeEngine 
+{
+    const float Quaternion::EPSILON = 1e-03f;
+    const Quaternion Quaternion::ZERO(0.0f, 0.0f, 0.0f, 0.0f);
+    const Quaternion Quaternion::IDENTITY(1.0f, 0.0f, 0.0f, 0.0f);
+	const Quaternion::EulerAngleOrderData Quaternion::EA_LOOKUP[6] = 
+		{ { 0, 1, 2}, { 0, 2, 1}, { 1, 0, 2},
+		{ 1, 2, 0}, { 2, 0, 1}, { 2, 1, 0} };;
+
+    void Quaternion::fromRotationMatrix(const Matrix3& mat)
+    {
+        // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
+        // article "Quaternion Calculus and Fast Animation".
+
+        float trace = mat[0][0]+mat[1][1]+mat[2][2];
+        float root;
+
+        if (trace > 0.0f)
+        {
+            // |w| > 1/2, may as well choose w > 1/2
+            root = Math::sqrt(trace + 1.0f);  // 2w
+            w = 0.5f*root;
+            root = 0.5f/root;  // 1/(4w)
+            x = (mat[2][1]-mat[1][2])*root;
+            y = (mat[0][2]-mat[2][0])*root;
+            z = (mat[1][0]-mat[0][1])*root;
+        }
+        else
+        {
+            // |w| <= 1/2
+            static UINT32 nextLookup[3] = { 1, 2, 0 };
+            UINT32 i = 0;
+
+            if (mat[1][1] > mat[0][0])
+                i = 1;
+
+            if (mat[2][2] > mat[i][i])
+                i = 2;
+
+            UINT32 j = nextLookup[i];
+            UINT32 k = nextLookup[j];
+
+            root = Math::sqrt(mat[i][i]-mat[j][j]-mat[k][k] + 1.0f);
+
+            float* cmpntLookup[3] = { &x, &y, &z };
+            *cmpntLookup[i] = 0.5f*root;
+            root = 0.5f/root;
+
+            w = (mat[k][j]-mat[j][k])*root;
+            *cmpntLookup[j] = (mat[j][i]+mat[i][j])*root;
+            *cmpntLookup[k] = (mat[k][i]+mat[i][k])*root;
+        }
+
+		normalize();
+    }
+
+    void Quaternion::fromAxisAngle(const Vector3& axis, const Radian& angle)
+    {
+        Radian halfAngle (0.5f*angle);
+        float sin = Math::sin(halfAngle);
+
+        w = Math::cos(halfAngle);
+        x = sin*axis.x;
+        y = sin*axis.y;
+        z = sin*axis.z;
+    }
+
+    void Quaternion::fromAxes(const Vector3& xaxis, const Vector3& yaxis, const Vector3& zaxis)
+    {
+        Matrix3 kRot;
+
+        kRot[0][0] = xaxis.x;
+        kRot[1][0] = xaxis.y;
+        kRot[2][0] = xaxis.z;
+
+        kRot[0][1] = yaxis.x;
+        kRot[1][1] = yaxis.y;
+        kRot[2][1] = yaxis.z;
+
+        kRot[0][2] = zaxis.x;
+        kRot[1][2] = zaxis.y;
+        kRot[2][2] = zaxis.z;
+
+        fromRotationMatrix(kRot);
+    }
+
+	void Quaternion::fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle)
+	{
+		Radian halfXAngle = xAngle * 0.5f;
+		Radian halfYAngle = yAngle * 0.5f;
+		Radian halfZAngle = zAngle * 0.5f;
+
+		float cx = Math::cos(halfXAngle);
+		float sx = Math::sin(halfXAngle);
+
+		float cy = Math::cos(halfYAngle);
+		float sy = Math::sin(halfYAngle);
+
+		float cz = Math::cos(halfZAngle);
+		float sz = Math::sin(halfZAngle);
+
+		Quaternion quatX(cx, sx, 0.0f, 0.0f);
+		Quaternion quatY(cy, 0.0f, sy, 0.0f);
+		Quaternion quatZ(cz, 0.0f, 0.0f, sz);
+
+		*this = (quatY * quatX) * quatZ;
+	}
+
+	void Quaternion::fromEulerAngles(const Radian& xAngle, const Radian& yAngle, const Radian& zAngle, EulerAngleOrder order)
+	{
+		const EulerAngleOrderData& l = EA_LOOKUP[(int)order];
+
+		Radian halfXAngle = xAngle * 0.5f;
+		Radian halfYAngle = yAngle * 0.5f;
+		Radian halfZAngle = zAngle * 0.5f;
+
+		float cx = Math::cos(halfXAngle);
+		float sx = Math::sin(halfXAngle);
+
+		float cy = Math::cos(halfYAngle);
+		float sy = Math::sin(halfYAngle);
+
+		float cz = Math::cos(halfZAngle);
+		float sz = Math::sin(halfZAngle);
+
+		Quaternion quats[3];
+		quats[0] = Quaternion(cx, sx, 0.0f, 0.0f);
+		quats[1] = Quaternion(cy, 0.0f, sy, 0.0f);
+		quats[2] = Quaternion(cz, 0.0f, 0.0f, sz);
+
+		*this = (quats[l.a] * quats[l.b]) * quats[l.c];
+	}
+
+	void Quaternion::toRotationMatrix(Matrix3& mat) const
+	{
+		float tx  = x+x;
+		float ty  = y+y;
+		float fTz  = z+z;
+		float twx = tx*w;
+		float twy = ty*w;
+		float twz = fTz*w;
+		float txx = tx*x;
+		float txy = ty*x;
+		float txz = fTz*x;
+		float tyy = ty*y;
+		float tyz = fTz*y;
+		float tzz = fTz*z;
+
+		mat[0][0] = 1.0f-(tyy+tzz);
+		mat[0][1] = txy-twz;
+		mat[0][2] = txz+twy;
+		mat[1][0] = txy+twz;
+		mat[1][1] = 1.0f-(txx+tzz);
+		mat[1][2] = tyz-twx;
+		mat[2][0] = txz-twy;
+		mat[2][1] = tyz+twx;
+		mat[2][2] = 1.0f-(txx+tyy);
+	}
+
+	void Quaternion::toAxisAngle(Vector3& axis, Radian& angle) const
+	{
+		float sqrLength = x*x+y*y+z*z;
+		if ( sqrLength > 0.0 )
+		{
+			angle = 2.0*Math::acos(w);
+			float invLength = Math::invSqrt(sqrLength);
+			axis.x = x*invLength;
+			axis.y = y*invLength;
+			axis.z = z*invLength;
+		}
+		else
+		{
+			// Angle is 0 (mod 2*pi), so any axis will do
+			angle = Radian(0.0);
+			axis.x = 1.0;
+			axis.y = 0.0;
+			axis.z = 0.0;
+		}
+	}
+
+	void Quaternion::toAxes(Vector3& xaxis, Vector3& yaxis, Vector3& zaxis) const
+	{
+		Matrix3 matRot;
+		toRotationMatrix(matRot);
+
+		xaxis.x = matRot[0][0];
+		xaxis.y = matRot[1][0];
+		xaxis.z = matRot[2][0];
+
+		yaxis.x = matRot[0][1];
+		yaxis.y = matRot[1][1];
+		yaxis.z = matRot[2][1];
+
+		zaxis.x = matRot[0][2];
+		zaxis.y = matRot[1][2];
+		zaxis.z = matRot[2][2];
+	}
+
+	bool Quaternion::toEulerAngles(Radian& xAngle, Radian& yAngle, Radian& zAngle) const
+	{
+		Matrix3 matRot;
+		toRotationMatrix(matRot);
+		return matRot.toEulerAngles(xAngle, yAngle, zAngle);
+	}
+
+    Vector3 Quaternion::xAxis() const
+    {
+        float fTy  = 2.0f*y;
+        float fTz  = 2.0f*z;
+        float fTwy = fTy*w;
+        float fTwz = fTz*w;
+        float fTxy = fTy*x;
+        float fTxz = fTz*x;
+        float fTyy = fTy*y;
+        float fTzz = fTz*z;
+
+        return Vector3(1.0f-(fTyy+fTzz), fTxy+fTwz, fTxz-fTwy);
+    }
+
+    Vector3 Quaternion::yAxis() const
+    {
+        float fTx  = 2.0f*x;
+        float fTy  = 2.0f*y;
+        float fTz  = 2.0f*z;
+        float fTwx = fTx*w;
+        float fTwz = fTz*w;
+        float fTxx = fTx*x;
+        float fTxy = fTy*x;
+        float fTyz = fTz*y;
+        float fTzz = fTz*z;
+
+        return Vector3(fTxy-fTwz, 1.0f-(fTxx+fTzz), fTyz+fTwx);
+    }
+
+    Vector3 Quaternion::zAxis() const
+    {
+        float fTx  = 2.0f*x;
+        float fTy  = 2.0f*y;
+        float fTz  = 2.0f*z;
+        float fTwx = fTx*w;
+        float fTwy = fTy*w;
+        float fTxx = fTx*x;
+        float fTxz = fTz*x;
+        float fTyy = fTy*y;
+        float fTyz = fTz*y;
+
+        return Vector3(fTxz+fTwy, fTyz-fTwx, 1.0f-(fTxx+fTyy));
+    }
+
+    Quaternion Quaternion::operator+ (const Quaternion& rhs) const
+    {
+        return Quaternion(w+rhs.w,x+rhs.x,y+rhs.y,z+rhs.z);
+    }
+
+    Quaternion Quaternion::operator- (const Quaternion& rhs) const
+    {
+        return Quaternion(w-rhs.w,x-rhs.x,y-rhs.y,z-rhs.z);
+    }
+
+    Quaternion Quaternion::operator* (const Quaternion& rhs) const
+    {
+        return Quaternion
+        (
+            w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z,
+            w * rhs.x + x * rhs.w + y * rhs.z - z * rhs.y,
+            w * rhs.y + y * rhs.w + z * rhs.x - x * rhs.z,
+            w * rhs.z + z * rhs.w + x * rhs.y - y * rhs.x
+        );
+    }
+
+    Quaternion Quaternion::operator* (float rhs) const
+    {
+        return Quaternion(rhs*w,rhs*x,rhs*y,rhs*z);
+    }
+
+    Quaternion Quaternion::operator- () const
+    {
+        return Quaternion(-w,-x,-y,-z);
+    }
+
+    float Quaternion::dot(const Quaternion& other) const
+    {
+        return w*other.w+x*other.x+y*other.y+z*other.z;
+    }
+
+    Quaternion Quaternion::inverse() const
+    {
+        float fNorm = w*w+x*x+y*y+z*z;
+        if (fNorm > 0.0f)
+        {
+            float fInvNorm = 1.0f/fNorm;
+            return Quaternion(w*fInvNorm,-x*fInvNorm,-y*fInvNorm,-z*fInvNorm);
+        }
+        else
+        {
+            // Return an invalid result to flag the error
+            return ZERO;
+        }
+    }
+
+    Vector3 Quaternion::rotate(const Vector3& v) const
+    {
+		Matrix3 rot;
+		toRotationMatrix(rot);
+		return rot.transform(v);
+    }
+
+	void Quaternion::lookRotation(const Vector3& forwardDir)
+    {
+		if (forwardDir == Vector3::ZERO)
+			return;
+
+		Vector3 nrmForwardDir = Vector3::normalize(forwardDir);
+		Vector3 currentForwardDir = -zAxis();
+
+		Quaternion targetRotation;
+		if ((nrmForwardDir + currentForwardDir).squaredLength() < 0.00005f)
+		{
+			// Oops, a 180 degree turn (infinite possible rotation axes)
+			// Default to yaw i.e. use current UP
+			*this = Quaternion(-y, -z, w, x);
+		}
+		else
+		{
+			// Derive shortest arc to new direction
+			Quaternion rotQuat = getRotationFromTo(currentForwardDir, nrmForwardDir);
+			*this = rotQuat * *this;
+		}
+    }
+
+	void Quaternion::lookRotation(const Vector3& forwardDir, const Vector3& upDir)
+	{
+		Vector3 forward = Vector3::normalize(forwardDir);
+		Vector3 up = Vector3::normalize(upDir);
+
+		if (Math::approxEquals(Vector3::dot(forward, up), 1.0f))
+		{
+			lookRotation(forward);
+			return;
+		}
+
+		Vector3 x = Vector3::cross(forward, up);
+		Vector3 y = Vector3::cross(x, forward);
+
+		x.normalize();
+		y.normalize();
+
+		*this = Quaternion(x, y, -forward);
+	}
+
+    Quaternion Quaternion::slerp(float t, const Quaternion& p, const Quaternion& q, bool shortestPath)
+    {
+        float cos = p.dot(q);
+        Quaternion quat;
+
+        if (cos < 0.0f && shortestPath)
+        {
+            cos = -cos;
+            quat = -q;
+        }
+        else
+        {
+            quat = q;
+        }
+
+        if (Math::abs(cos) < 1 - EPSILON)
+        {
+            // Standard case (slerp)
+            float sin = Math::sqrt(1 - Math::sqr(cos));
+            Radian angle = Math::atan2(sin, cos);
+            float invSin = 1.0f / sin;
+            float coeff0 = Math::sin((1.0f - t) * angle) * invSin;
+            float coeff1 = Math::sin(t * angle) * invSin;
+            return coeff0 * p + coeff1 * quat;
+        }
+        else
+        {
+            // There are two situations:
+            // 1. "p" and "q" are very close (fCos ~= +1), so we can do a linear
+            //    interpolation safely.
+            // 2. "p" and "q" are almost inverse of each other (fCos ~= -1), there
+            //    are an infinite number of possibilities interpolation. but we haven't
+            //    have method to fix this case, so just use linear interpolation here.
+            Quaternion ret = (1.0f - t) * p + t * quat;
+
+            // Taking the complement requires renormalization
+            ret.normalize();
+            return ret;
+        }
+    }
+
+    float Quaternion::normalize()
+    {
+        float len = w*w+x*x+y*y+z*z;
+        float factor = 1.0f / Math::sqrt(len);
+        *this = *this * factor;
+        return len;
+    }
+
+	Quaternion Quaternion::getRotationFromTo(const Vector3& from, const Vector3& dest, const Vector3& fallbackAxis)
+	{
+		// Based on Stan Melax's article in Game Programming Gems
+		Quaternion q;
+
+		Vector3 v0 = from;
+		Vector3 v1 = dest;
+		v0.normalize();
+		v1.normalize();
+
+		float d = v0.dot(v1);
+
+		// If dot == 1, vectors are the same
+		if (d >= 1.0f)
+			return Quaternion::IDENTITY;
+
+		if (d < (1e-6f - 1.0f))
+		{
+			if (fallbackAxis != Vector3::ZERO)
+			{
+				// Rotate 180 degrees about the fallback axis
+				q.fromAxisAngle(fallbackAxis, Radian(Math::PI));
+			}
+			else
+			{
+				// Generate an axis
+				Vector3 axis = Vector3::UNIT_X.cross(from);
+				if (axis.isZeroLength()) // Pick another if colinear
+					axis = Vector3::UNIT_Y.cross(from);
+				axis.normalize();
+				q.fromAxisAngle(axis, Radian(Math::PI));
+			}
+		}
+		else
+		{
+			float s = Math::sqrt( (1+d)*2 );
+			float invs = 1 / s;
+
+			Vector3 c = v0.cross(v1);
+
+			q.x = c.x * invs;
+			q.y = c.y * invs;
+			q.z = c.z * invs;
+			q.w = s * 0.5f;
+			q.normalize();
+		}
+
+		return q;
+	}
+
+	Quaternion operator* (float lhs, const Quaternion& rhs)
+	{
+		return Quaternion(lhs*rhs.w,lhs*rhs.x,lhs*rhs.y,
+			lhs*rhs.z);
+	}
+}

+ 1 - 0
MBansheeEditor/EditorApplication.cs

@@ -228,6 +228,7 @@ namespace BansheeEditor
 
             inputConfig.RegisterButton(SceneWindow.ToggleProfilerOverlayBinding, ButtonCode.P, ButtonModifier.CtrlAlt);
             inputConfig.RegisterButton(SceneWindow.ViewToolBinding, ButtonCode.Q);
+            inputConfig.RegisterButton(SceneWindow.FrameBinding, ButtonCode.F);
             inputConfig.RegisterButton(SceneWindow.MoveToolBinding, ButtonCode.W);
             inputConfig.RegisterButton(SceneWindow.RotateToolBinding, ButtonCode.E);
             inputConfig.RegisterButton(SceneWindow.ScaleToolBinding, ButtonCode.R);

+ 370 - 128
MBansheeEditor/Scene/SceneCamera.cs

@@ -1,128 +1,370 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Handles camera movement in the scene view.
-    /// </summary>
-    [RunInEditor]
-    internal sealed class SceneCamera : Component
-    {
-        public const string MoveForwardBinding = "SceneForward";
-	    public const string MoveLeftBinding = "SceneLeft";
-	    public const string MoveRightBinding = "SceneRight";
-	    public const string MoveBackBinding = "SceneBackward";
-	    public const string FastMoveBinding = "SceneFastMove";
-	    public const string RotateBinding = "SceneRotate";
-	    public const string HorizontalAxisBinding = "SceneHorizontal";
-	    public const string VerticalAxisBinding = "SceneVertical";
-
-	    private const float StartSpeed = 4.0f;
-	    private const float TopSpeed = 12.0f;
-	    private const float Acceleration = 1.0f;
-	    private const float FastModeMultiplier = 2.0f;
-	    private const float RotationalSpeed = 360.0f; // Degrees/second
-
-        private VirtualButton moveForwardBtn;
-        private VirtualButton moveLeftBtn;
-        private VirtualButton moveRightBtn;
-        private VirtualButton moveBackwardBtn;
-        private VirtualButton fastMoveBtn;
-        private VirtualButton activeBtn;
-        private VirtualAxis horizontalAxis;
-        private VirtualAxis verticalAxis;
-
-        private float currentSpeed;
-        private Degree yaw;
-        private Radian pitch;
-        private bool lastButtonState;
-
-        private void OnReset()
-        {
-            moveForwardBtn = new VirtualButton(MoveForwardBinding);
-            moveLeftBtn = new VirtualButton(MoveLeftBinding);
-            moveRightBtn = new VirtualButton(MoveRightBinding);
-            moveBackwardBtn = new VirtualButton(MoveBackBinding);
-            fastMoveBtn = new VirtualButton(FastMoveBinding);
-            activeBtn = new VirtualButton(RotateBinding);
-            horizontalAxis = new VirtualAxis(HorizontalAxisBinding);
-            verticalAxis = new VirtualAxis(VerticalAxisBinding);
-        }
-
-        private void OnDisable()
-        {
-            if (VirtualInput.IsButtonHeld(activeBtn))
-                Cursor.Show();
-        }
-
-        private void OnUpdate()
-        {
-		    bool goingForward = VirtualInput.IsButtonHeld(moveForwardBtn);
-		    bool goingBack = VirtualInput.IsButtonHeld(moveBackwardBtn);
-		    bool goingLeft = VirtualInput.IsButtonHeld(moveLeftBtn);
-		    bool goingRight = VirtualInput.IsButtonHeld(moveRightBtn);
-		    bool fastMove = VirtualInput.IsButtonHeld(fastMoveBtn);
-		    bool camActive = VirtualInput.IsButtonHeld(activeBtn);
-
-            if (camActive != lastButtonState)
-            {
-                if (camActive)
-                    Cursor.Hide();
-                else
-                    Cursor.Show();
-
-                lastButtonState = camActive;
-            }
-
-		    float frameDelta = Time.FrameDelta;
-		    if (camActive)
-		    {
-		        float horzValue = VirtualInput.GetAxisValue(horizontalAxis);
-                float vertValue = VirtualInput.GetAxisValue(verticalAxis);
-
-                yaw += new Degree(horzValue * RotationalSpeed * frameDelta);
-                pitch += new Degree(vertValue * RotationalSpeed * frameDelta);
-
-                yaw = MathEx.WrapAngle(yaw);
-                pitch = MathEx.WrapAngle(pitch);
-
-		        Quaternion yRot = Quaternion.FromAxisAngle(Vector3.YAxis, yaw);
-                Quaternion xRot = Quaternion.FromAxisAngle(Vector3.XAxis, pitch);
-
-                Quaternion camRot = yRot * xRot;
-                camRot.Normalize();
-
-                SceneObject.Rotation = camRot;
-
-                Vector3 direction = Vector3.Zero;
-                if (goingForward) direction += SceneObject.Forward;
-                if (goingBack) direction -= SceneObject.Forward;
-                if (goingRight) direction += SceneObject.Right;
-                if (goingLeft) direction -= SceneObject.Right;
-
-                if (direction.SqrdLength != 0)
-                {
-                    direction.Normalize();
-
-                    float multiplier = 1.0f;
-                    if (fastMove)
-                        multiplier = FastModeMultiplier;
-
-                    currentSpeed = MathEx.Clamp(currentSpeed + Acceleration * frameDelta, StartSpeed, TopSpeed);
-                    currentSpeed *= multiplier;
-                }
-                else
-                {
-                    currentSpeed = 0.0f;
-                }
-
-                const float tooSmall = 0.0001f;
-                if (currentSpeed > tooSmall)
-                {
-                    Vector3 velocity = direction * currentSpeed;
-                    SceneObject.Move(velocity * frameDelta);
-                }
-		    }
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Handles camera movement in the scene view.
+    /// </summary>
+    [RunInEditor]
+    internal sealed class SceneCamera : Component
+    {
+        public const string MoveForwardBinding = "SceneForward";
+	    public const string MoveLeftBinding = "SceneLeft";
+	    public const string MoveRightBinding = "SceneRight";
+	    public const string MoveBackBinding = "SceneBackward";
+	    public const string FastMoveBinding = "SceneFastMove";
+	    public const string RotateBinding = "SceneRotate";
+	    public const string HorizontalAxisBinding = "SceneHorizontal";
+	    public const string VerticalAxisBinding = "SceneVertical";
+
+	    private const float StartSpeed = 4.0f;
+	    private const float TopSpeed = 12.0f;
+	    private const float Acceleration = 1.0f;
+	    private const float FastModeMultiplier = 2.0f;
+	    private const float RotationalSpeed = 360.0f; // Degrees/second
+
+        private VirtualButton moveForwardBtn;
+        private VirtualButton moveLeftBtn;
+        private VirtualButton moveRightBtn;
+        private VirtualButton moveBackwardBtn;
+        private VirtualButton fastMoveBtn;
+        private VirtualButton activeBtn;
+        private VirtualAxis horizontalAxis;
+        private VirtualAxis verticalAxis;
+
+        private float currentSpeed;
+        private Degree yaw;
+        private Degree pitch;
+        private bool lastButtonState;
+        private Camera camera;
+        private bool inputEnabled = true;
+
+        // Animating camera transitions
+        private CameraAnimation animation = new CameraAnimation();
+        private float currentSize = 50.0f;
+        private float lerp;
+        private bool isAnimating;
+
+        private void OnReset()
+        {
+            camera = SceneObject.GetComponent<Camera>();
+
+            moveForwardBtn = new VirtualButton(MoveForwardBinding);
+            moveLeftBtn = new VirtualButton(MoveLeftBinding);
+            moveRightBtn = new VirtualButton(MoveRightBinding);
+            moveBackwardBtn = new VirtualButton(MoveBackBinding);
+            fastMoveBtn = new VirtualButton(FastMoveBinding);
+            activeBtn = new VirtualButton(RotateBinding);
+            horizontalAxis = new VirtualAxis(HorizontalAxisBinding);
+            verticalAxis = new VirtualAxis(VerticalAxisBinding);
+        }
+
+        private void OnUpdate()
+        {
+		    bool goingForward = VirtualInput.IsButtonHeld(moveForwardBtn);
+		    bool goingBack = VirtualInput.IsButtonHeld(moveBackwardBtn);
+		    bool goingLeft = VirtualInput.IsButtonHeld(moveLeftBtn);
+		    bool goingRight = VirtualInput.IsButtonHeld(moveRightBtn);
+		    bool fastMove = VirtualInput.IsButtonHeld(fastMoveBtn);
+		    bool camActive = VirtualInput.IsButtonHeld(activeBtn);
+
+            if (camActive != lastButtonState)
+            {
+                if (camActive)
+                    Cursor.Hide();
+                else
+                    Cursor.Show();
+
+                lastButtonState = camActive;
+            }
+
+		    float frameDelta = Time.FrameDelta;
+		    if (camActive)
+		    {
+		        float horzValue = VirtualInput.GetAxisValue(horizontalAxis);
+                float vertValue = VirtualInput.GetAxisValue(verticalAxis);
+
+                yaw += new Degree(horzValue * RotationalSpeed * frameDelta);
+                pitch += new Degree(vertValue * RotationalSpeed * frameDelta);
+
+                yaw = MathEx.WrapAngle(yaw);
+                pitch = MathEx.WrapAngle(pitch);
+
+		        Quaternion yRot = Quaternion.FromAxisAngle(Vector3.YAxis, yaw);
+                Quaternion xRot = Quaternion.FromAxisAngle(Vector3.XAxis, pitch);
+
+                Quaternion camRot = yRot * xRot;
+                camRot.Normalize();
+
+                SceneObject.Rotation = camRot;
+
+                Vector3 direction = Vector3.Zero;
+                if (goingForward) direction += SceneObject.Forward;
+                if (goingBack) direction -= SceneObject.Forward;
+                if (goingRight) direction += SceneObject.Right;
+                if (goingLeft) direction -= SceneObject.Right;
+
+                if (direction.SqrdLength != 0)
+                {
+                    direction.Normalize();
+
+                    float multiplier = 1.0f;
+                    if (fastMove)
+                        multiplier = FastModeMultiplier;
+
+                    currentSpeed = MathEx.Clamp(currentSpeed + Acceleration * frameDelta, StartSpeed, TopSpeed);
+                    currentSpeed *= multiplier;
+                }
+                else
+                {
+                    currentSpeed = 0.0f;
+                }
+
+                const float tooSmall = 0.0001f;
+                if (currentSpeed > tooSmall)
+                {
+                    Vector3 velocity = direction * currentSpeed;
+                    SceneObject.Move(velocity * frameDelta);
+                }
+		    }
+
+            UpdateAnim();
+        }
+
+        /// <summary>
+        /// Enables or disables camera controls.
+        /// </summary>
+        /// <param name="enable">True to enable controls, false to disable.</param>
+        public void EnableInput(bool enable)
+        {
+            if (inputEnabled == enable)
+                return;
+
+            inputEnabled = enable;
+
+            if (!inputEnabled)
+            {
+                if (VirtualInput.IsButtonHeld(activeBtn))
+                    Cursor.Show();
+            }
+        }
+
+        /// <summary>
+        /// Focuses the camera on the currently selected object(s).
+        /// </summary>
+        public void FrameSelected()
+        {
+            SceneObject[] selectedObjects = Selection.SceneObjects;
+            if (selectedObjects.Length > 0)
+            {
+                AABox box = EditorUtility.CalculateBounds(Selection.SceneObjects);
+                FrameBounds(box);
+            }
+        }
+
+        /// <summary>
+        /// Moves and orients a camera so that the provided bounds end covering the camera's viewport.
+        /// </summary>
+        /// <param name="bounds">Bounds to frame in camera's view.</param>
+        /// <param name="padding">Amount of padding to leave on the borders of the viewport, in percent [0, 1].</param>
+        private void FrameBounds(AABox bounds, float padding = 0.2f)
+        {
+            // TODO - Use AABox bounds directly instead of a sphere to be more accurate
+            float worldWidth = bounds.Size.Length;
+            float worldHeight = worldWidth;
+
+            if (worldWidth == 0.0f)
+                worldWidth = 1.0f;
+
+            if (worldHeight == 0.0f)
+                worldHeight = 1.0f;
+
+            float boundsAspect = worldWidth / worldHeight;
+            float paddingScale = MathEx.Clamp01(padding) + 1.0f;
+            float frustumWidth;
+
+            // If camera has wider aspect than bounds then height will be the limiting dimension
+            if (camera.AspectRatio > boundsAspect)
+                frustumWidth = worldHeight * 1.0f/camera.AspectRatio * paddingScale;
+            else // Otherwise width
+                frustumWidth = worldWidth * paddingScale;
+
+            float distance = CalcDistanceForFrustumWidth(frustumWidth);
+
+            Vector3 forward = bounds.Center - SceneObject.Position;
+            forward.Normalize();
+
+            CameraState state = new CameraState();
+            state.Position = bounds.Center - forward * distance;
+            state.Rotation = Quaternion.LookRotation(forward, Vector3.YAxis);
+            state.Ortographic = camera.ProjectionType == ProjectionType.Orthographic;
+            state.Size = distance;
+
+            SetState(state);
+        }
+
+        /// <summary>
+        /// Changes the state of the camera, either instantly or animated over several frames. The state includes
+        /// camera position, rotation, type and possibly other parameters.
+        /// </summary>
+        /// <param name="state">New state of the camera.</param>
+        /// <param name="animated">Should the state be linearly interpolated over a course of several frames.</param>
+        private void SetState(CameraState state, bool animated = true)
+        {
+            CameraState startState = new CameraState();
+
+            startState.Position = SceneObject.Position;
+            startState.Rotation = SceneObject.Rotation;
+            startState.Ortographic = camera.ProjectionType == ProjectionType.Orthographic;
+            startState.Size = currentSize;
+
+            animation.Start(startState, state);
+            if (!animated)
+            {
+                ApplyState(1.0f);
+
+                isAnimating = false;
+            }
+            else
+            {
+                isAnimating = true;
+                lerp = 0.0f;
+            }
+        }
+
+        /// <summary>
+        /// Applies the animation target state depending on the interpolation parameter. <see cref="SetState"/>.
+        /// </summary>
+        /// <param name="t">Interpolation parameter ranging [0, 1] that interpolated between the start state and the
+        ///                 target state.</param>
+        private void ApplyState(float t)
+        {
+            animation.Update(t);
+
+            SceneObject.Position = animation.State.Position;
+            SceneObject.Rotation = animation.State.Rotation;
+            camera.ProjectionType = animation.State.Ortographic ? ProjectionType.Orthographic : ProjectionType.Perspective;
+            currentSize = animation.State.Size;
+
+            Vector3 eulerAngles = SceneObject.Rotation.ToEuler();
+            pitch = eulerAngles.x;
+            yaw = eulerAngles.y;
+
+            // Note: Consider having a global setting for near/far planes as changing it here might confuse the user
+            if (currentSize < 1)
+            {
+                camera.NearClipPlane = 0.005f;
+                camera.FarClipPlane = 1000f;
+            }
+            if (currentSize < 100)
+            {
+                camera.NearClipPlane = 0.05f;
+                camera.FarClipPlane = 2500f;
+            }
+            else if (currentSize < 1000)
+            {
+                camera.NearClipPlane = 0.5f;
+                camera.FarClipPlane = 10000f;
+            }
+            else
+            {
+                camera.NearClipPlane = 5.0f;
+                camera.FarClipPlane = 1000000f;
+            }
+        }
+
+        /// <summary>
+        /// Calculates distance at which the camera's frustum width is equal to the provided width.
+        /// </summary>
+        /// <param name="frustumWidth">Frustum width to find the distance for, in world units.</param>
+        /// <returns>Distance at which the camera's frustum is the specified width, in world units.</returns>
+        private float CalcDistanceForFrustumWidth(float frustumWidth)
+        {
+            if (camera.ProjectionType == ProjectionType.Perspective)
+                return (frustumWidth*0.5f)/MathEx.Tan(camera.FieldOfView*0.5f);
+            else
+                return frustumWidth * 2.0f;
+        }
+
+        /// <summary>
+        /// Updates camera state transition animation. Should be called every frame.
+        /// </summary>
+        private void UpdateAnim()
+        {
+            if (!isAnimating)
+                return;
+
+            const float ANIM_TIME = 0.5f; // 0.5f seconds
+            lerp += Time.FrameDelta * (1.0f / ANIM_TIME);
+
+            if (lerp >= 1.0f)
+            {
+                lerp = 1.0f;
+                isAnimating = false;
+            }
+
+            ApplyState(lerp);
+        }
+
+        /// <summary>
+        /// Contains data for a possible camera state. Camera states can be interpolated between each other as needed.
+        /// </summary>
+        private struct CameraState
+        {
+            internal float _ortographic;
+
+            public Vector3 Position { get; set; }
+            public Quaternion Rotation { get; set; }
+            public float Size { get; set; }
+
+            public bool Ortographic
+            {
+                get { return _ortographic > 0.5; }
+                set { _ortographic = value ? 1.0f : 0.0f; }
+            }
+        }
+
+        /// <summary>
+        /// Helper class that performs linear interpolation between two camera states.
+        /// </summary>
+        private struct CameraAnimation
+        {
+            private CameraState start;
+            private CameraState target;
+            private CameraState interpolated;
+
+            /// <summary>
+            /// Returns currently interpolated animation state.
+            /// </summary>
+            public CameraState State
+            {
+                get { return interpolated; }
+            }
+
+            /// <summary>
+            /// Initializes the animation with initial and target states.
+            /// </summary>
+            /// <param name="start">Initial state to animate from.</param>
+            /// <param name="target">Target state to animate towards.</param>
+            public void Start(CameraState start, CameraState target)
+            {
+                this.start = start;
+                this.target = target;
+            }
+
+            /// <summary>
+            /// Updates the animation by interpolating between the start and target states.
+            /// </summary>
+            /// <param name="t">Interpolation parameter in range [0, 1] that determines how much to interpolate between
+            ///                 start and target states.</param>
+            public void Update(float t)
+            {
+                interpolated.Position = start.Position * (1.0f - t) + target.Position * t;
+                interpolated.Rotation = Quaternion.Slerp(start.Rotation, target.Rotation, t);
+                interpolated._ortographic = start._ortographic * (1.0f - t) + target._ortographic * t;
+
+                interpolated.Size = start.Size * (1.0f - t) + target.Size * t;
+            }
+        };
+    }
+}

+ 688 - 669
MBansheeEditor/Scene/SceneWindow.cs

@@ -1,669 +1,688 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays the scene view camera and various scene controls.
-    /// </summary>
-    internal sealed class SceneWindow : EditorWindow
-    {
-        internal const string ToggleProfilerOverlayBinding = "ToggleProfilerOverlay";
-        internal const string ViewToolBinding = "ViewTool";
-        internal const string MoveToolBinding = "MoveTool";
-        internal const string RotateToolBinding = "RotateTool";
-        internal const string ScaleToolBinding = "ScaleTool";
-        internal const string DuplicateBinding = "Duplicate";
-        internal const string DeleteBinding = "Delete";
-
-        private const int HeaderHeight = 20;
-        private const float DefaultPlacementDepth = 5.0f;
-        private static readonly Color ClearColor = new Color(83.0f/255.0f, 83.0f/255.0f, 83.0f/255.0f);
-        private const string ProfilerOverlayActiveKey = "_Internal_ProfilerOverlayActive";
-
-        private Camera camera;
-        private SceneCamera cameraController;
-        private RenderTexture2D renderTexture;
-        private GUILayoutY mainLayout;
-        private GUIPanel rtPanel;
-
-        private GUIRenderTexture renderTextureGUI;
-        private SceneViewHandler sceneViewHandler;
-
-        private GUIToggle viewButton;
-        private GUIToggle moveButton;
-        private GUIToggle rotateButton;
-        private GUIToggle scaleButton;
-
-        private GUIToggle localCoordButton;
-        private GUIToggle worldCoordButton;
-
-        private GUIToggle pivotButton;
-        private GUIToggle centerButton;
-
-        private GUIToggle moveSnapButton;
-        private GUIFloatField moveSnapInput;
-
-        private GUIToggle rotateSnapButton;
-        private GUIFloatField rotateSnapInput;
-
-        private int editorSettingsHash = int.MaxValue;
-
-        private VirtualButton duplicateKey;
-        private VirtualButton deleteKey;
-
-        // Tool shortcuts
-        private VirtualButton viewToolKey;
-        private VirtualButton moveToolKey;
-        private VirtualButton rotateToolKey;
-        private VirtualButton scaleToolKey;
-
-        // Profiler overlay
-        private ProfilerOverlay activeProfilerOverlay;
-        private Camera profilerCamera;
-        private VirtualButton toggleProfilerOverlayKey;
-
-        // Drag & drop
-        private bool dragActive;
-        private SceneObject draggedSO;
-
-        /// <summary>
-        /// Returns the scene camera.
-        /// </summary>
-        public Camera Camera
-        {
-            get { return camera; }
-        }
-
-        /// <summary>
-        /// Constructs a new scene window.
-        /// </summary>
-        internal SceneWindow()
-        { }
-
-        /// <summary>
-        /// Opens a scene window if its not open already.
-        /// </summary>
-        [MenuItem("Windows/Scene", ButtonModifier.CtrlAlt, ButtonCode.S, 6000)]
-        private static void OpenSceneWindow()
-        {
-            OpenWindow<SceneWindow>();
-        }
-
-        /// <inheritdoc/>
-        protected override LocString GetDisplayName()
-        {
-            return new LocEdString("Scene");
-        }
-
-        private void OnInitialize()
-        {
-            mainLayout = GUI.AddLayoutY();
-
-            GUIContent viewIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.View));
-            GUIContent moveIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Move));
-            GUIContent rotateIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Rotate));
-            GUIContent scaleIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Scale));
-
-            GUIContent localIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Local));
-            GUIContent worldIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.World));
-
-            GUIContent pivotIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Pivot));
-            GUIContent centerIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Center));
-
-            GUIContent moveSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.MoveSnap));
-            GUIContent rotateSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.RotateSnap));
-
-            GUIToggleGroup handlesTG = new GUIToggleGroup();
-            viewButton = new GUIToggle(viewIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            moveButton = new GUIToggle(moveIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            rotateButton = new GUIToggle(rotateIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            scaleButton = new GUIToggle(scaleIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-
-            GUIToggleGroup coordModeTG = new GUIToggleGroup();
-            localCoordButton = new GUIToggle(localIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
-            worldCoordButton = new GUIToggle(worldIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
-
-            GUIToggleGroup pivotModeTG = new GUIToggleGroup();
-            pivotButton = new GUIToggle(pivotIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            centerButton = new GUIToggle(centerIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-
-            moveSnapButton = new GUIToggle(moveSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            moveSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
-
-            rotateSnapButton = new GUIToggle(rotateSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
-            rotateSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
-
-            viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);
-            moveButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Move);
-            rotateButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Rotate);
-            scaleButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Scale);
-
-            localCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.Local);
-            worldCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.World);
-
-            pivotButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Pivot);
-            centerButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Center);
-
-            moveSnapButton.OnToggled += (bool active) => OnMoveSnapToggled(active);
-            moveSnapInput.OnChanged += (float value) => OnMoveSnapValueChanged(value);
-
-            rotateSnapButton.OnToggled += (bool active) => OnRotateSnapToggled(active);
-            rotateSnapInput.OnChanged += (float value) => OnRotateSnapValueChanged(value);
-
-            GUILayout handlesLayout = mainLayout.AddLayoutX();
-            handlesLayout.AddElement(viewButton);
-            handlesLayout.AddElement(moveButton);
-            handlesLayout.AddElement(rotateButton);
-            handlesLayout.AddElement(scaleButton);
-            handlesLayout.AddSpace(10);
-            handlesLayout.AddElement(localCoordButton);
-            handlesLayout.AddElement(worldCoordButton);
-            handlesLayout.AddSpace(10);
-            handlesLayout.AddElement(pivotButton);
-            handlesLayout.AddElement(centerButton);
-            handlesLayout.AddFlexibleSpace();
-            handlesLayout.AddElement(moveSnapButton);
-            handlesLayout.AddElement(moveSnapInput);
-            handlesLayout.AddSpace(10);
-            handlesLayout.AddElement(rotateSnapButton);
-            handlesLayout.AddElement(rotateSnapInput);
-
-            rtPanel = mainLayout.AddPanel();
-
-            toggleProfilerOverlayKey = new VirtualButton(ToggleProfilerOverlayBinding);
-            viewToolKey = new VirtualButton(ViewToolBinding);
-            moveToolKey = new VirtualButton(MoveToolBinding);
-            rotateToolKey = new VirtualButton(RotateToolBinding);
-            scaleToolKey = new VirtualButton(ScaleToolBinding);
-            duplicateKey = new VirtualButton(DuplicateBinding);
-            deleteKey = new VirtualButton(DeleteBinding);
-
-            UpdateRenderTexture(Width, Height - HeaderHeight);
-            UpdateProfilerOverlay();
-        }
-
-        private void OnDestroy()
-        {
-            if (camera != null)
-            {
-                camera.SceneObject.Destroy();
-                camera = null;
-            }
-        }
-        
-        /// <summary>
-        /// Converts screen coordinates into coordinates relative to the scene view render texture.
-        /// </summary>
-        /// <param name="screenPos">Coordinates relative to the screen.</param>
-        /// <param name="scenePos">Output coordinates relative to the scene view texture.</param>
-        /// <returns>True if the coordinates are within the scene view texture, false otherwise.</returns>
-        private bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
-        {
-            scenePos = screenPos;
-            Vector2I windowPos = ScreenToWindowPos(screenPos);
-
-            Rect2I bounds = GUILayoutUtility.CalculateBounds(renderTextureGUI);
-
-            if (bounds.Contains(windowPos))
-            {
-                scenePos.x = windowPos.x - bounds.x;
-                scenePos.y = windowPos.y - bounds.y;
-
-                return true;
-            }
-
-            return false;
-        }
-
-        private void OnEditorUpdate()
-        {
-            if (HasFocus)
-            {
-                if (!Input.IsPointerButtonHeld(PointerButton.Right))
-                {
-                    if (VirtualInput.IsButtonUp(toggleProfilerOverlayKey))
-                        EditorSettings.SetBool(ProfilerOverlayActiveKey, !EditorSettings.GetBool(ProfilerOverlayActiveKey));
-
-                    if (VirtualInput.IsButtonUp(viewToolKey))
-                        EditorApplication.ActiveSceneTool = SceneViewTool.View;
-
-                    if (VirtualInput.IsButtonUp(moveToolKey))
-                        EditorApplication.ActiveSceneTool = SceneViewTool.Move;
-
-                    if (VirtualInput.IsButtonUp(rotateToolKey))
-                        EditorApplication.ActiveSceneTool = SceneViewTool.Rotate;
-
-                    if (VirtualInput.IsButtonUp(scaleToolKey))
-                        EditorApplication.ActiveSceneTool = SceneViewTool.Scale;
-
-                    if (VirtualInput.IsButtonUp(duplicateKey))
-                    {
-                        SceneObject[] selectedObjects = Selection.SceneObjects;
-                        CleanDuplicates(ref selectedObjects);
-
-                        if (selectedObjects.Length > 0)
-                        {
-                            String message;
-                            if (selectedObjects.Length == 1)
-                                message = "Duplicated " + selectedObjects[0].Name;
-                            else
-                                message = "Duplicated " + selectedObjects.Length + " elements";
-
-                            UndoRedo.CloneSO(selectedObjects, message);
-                            EditorApplication.SetSceneDirty();
-                        }
-                    }
-
-                    if (VirtualInput.IsButtonUp(deleteKey))
-                    {
-                        SceneObject[] selectedObjects = Selection.SceneObjects;
-                        CleanDuplicates(ref selectedObjects);
-
-                        if (selectedObjects.Length > 0)
-                        {
-                            foreach (var so in selectedObjects)
-                            {
-                                string message = "Deleted " + so.Name;
-                                UndoRedo.DeleteSO(so, message);
-                            }
-
-                            EditorApplication.SetSceneDirty();
-                        }
-                    }
-                }
-            }
-
-            // Refresh GUI buttons if needed (in case someones changes the values from script)
-            if (editorSettingsHash != EditorSettings.Hash)
-            {
-                UpdateButtonStates();
-                UpdateProfilerOverlay();
-                editorSettingsHash = EditorSettings.Hash;
-            }
-
-            // Update scene view handles and selection
-            sceneViewHandler.Update();
-
-            bool handleActive = false;
-            if (Input.IsPointerButtonUp(PointerButton.Left))
-            {
-                if (sceneViewHandler.IsHandleActive())
-                {
-                    sceneViewHandler.ClearHandleSelection();
-                    handleActive = true;
-                }
-            }
-
-            Vector2I scenePos;
-            bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
-
-            bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
-            draggedOver &= inBounds && DragDrop.Type == DragDropType.Resource;
-
-            if (draggedOver)
-            {
-                if (DragDrop.DropInProgress)
-                {
-                    dragActive = false;
-                    draggedSO = null;
-                }
-                else
-                {
-                    if (!dragActive)
-                    {
-                        dragActive = true;
-
-                        ResourceDragDropData dragData = (ResourceDragDropData)DragDrop.Data;
-
-                        string draggedMeshPath = "";
-                        string[] draggedPaths = dragData.Paths;
-
-                        for (int i = 0; i < draggedPaths.Length; i++)
-                        {
-                            LibraryEntry entry = ProjectLibrary.GetEntry(draggedPaths[i]);
-                            if (entry != null && entry.Type == LibraryEntryType.File)
-                            {
-                                FileEntry fileEntry = (FileEntry) entry;
-                                if (fileEntry.ResType == ResourceType.Mesh)
-                                {
-                                    draggedMeshPath = draggedPaths[i];
-                                    break;
-                                }
-                            }
-                        }
-
-                        if (!string.IsNullOrEmpty(draggedMeshPath))
-                        {
-                            string meshName = Path.GetFileNameWithoutExtension(draggedMeshPath);
-
-                            draggedSO = new SceneObject(meshName);
-                            Mesh mesh = ProjectLibrary.Load<Mesh>(draggedMeshPath);
-
-                            Renderable renderable = draggedSO.AddComponent<Renderable>();
-                            renderable.Mesh = mesh;
-
-                            EditorApplication.SetSceneDirty();
-                        }
-                    }
-
-                    if (draggedSO != null)
-                    {
-                        Ray worldRay = camera.ScreenToWorldRay(scenePos);
-                        draggedSO.Position = worldRay*DefaultPlacementDepth;
-                    }
-                }
-
-                return;
-            }
-            else
-            {
-                if (dragActive)
-                {
-                    dragActive = false;
-
-                    if (draggedSO != null)
-                    {
-                        draggedSO.Destroy();
-                        draggedSO = null;
-                    }
-                }
-            }
-
-            if (HasFocus)
-            {
-                cameraController.SceneObject.Active = true;
-
-                if (inBounds)
-                {
-                    if (Input.IsPointerButtonDown(PointerButton.Left))
-                    {
-                        sceneViewHandler.TrySelectHandle(scenePos);
-                    }
-                    else if (Input.IsPointerButtonUp(PointerButton.Left))
-                    {
-                        if (!handleActive)
-                        {
-                            bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
-                                            Input.IsButtonHeld(ButtonCode.RightControl);
-
-                            sceneViewHandler.PickObject(scenePos, ctrlHeld);
-                        }
-                    }
-                }
-            }
-            else
-                cameraController.SceneObject.Active = false;
-
-            sceneViewHandler.UpdateHandle(scenePos, Input.PointerDelta);
-            sceneViewHandler.UpdateSelection();
-        }
-
-        /// <inheritdoc/>
-        protected override void WindowResized(int width, int height)
-        {
-            UpdateRenderTexture(width, height - HeaderHeight);
-
-            base.WindowResized(width, height);
-        }
-
-        /// <inheritdoc/>
-        protected override void FocusChanged(bool inFocus)
-        {
-            if (!inFocus)
-            {
-                sceneViewHandler.ClearHandleSelection();
-            }
-        }
-
-        /// <summary>
-        /// Triggered when one of the scene tool buttons is clicked, changing the active scene handle.
-        /// </summary>
-        /// <param name="tool">Clicked scene tool to activate.</param>
-        private void OnSceneToolButtonClicked(SceneViewTool tool)
-        {
-            EditorApplication.ActiveSceneTool = tool;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when one of the coordinate mode buttons is clicked, changing the active coordinate mode.
-        /// </summary>
-        /// <param name="mode">Clicked coordinate mode to activate.</param>
-        private void OnCoordinateModeButtonClicked(HandleCoordinateMode mode)
-        {
-            EditorApplication.ActiveCoordinateMode = mode;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when one of the pivot buttons is clicked, changing the active pivot mode.
-        /// </summary>
-        /// <param name="mode">Clicked pivot mode to activate.</param>
-        private void OnPivotModeButtonClicked(HandlePivotMode mode)
-        {
-            EditorApplication.ActivePivotMode = mode;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the move snap button is toggled.
-        /// </summary>
-        /// <param name="active">Determins should be move snap be activated or deactivated.</param>
-        private void OnMoveSnapToggled(bool active)
-        {
-            Handles.MoveHandleSnapActive = active;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the move snap increment value changes.
-        /// </summary>
-        /// <param name="value">Value that determines in what increments to perform move snapping.</param>
-        private void OnMoveSnapValueChanged(float value)
-        {
-            Handles.MoveSnapAmount = MathEx.Clamp(value, 0.01f, 1000.0f);
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the rotate snap button is toggled.
-        /// </summary>
-        /// <param name="active">Determins should be rotate snap be activated or deactivated.</param>
-        private void OnRotateSnapToggled(bool active)
-        {
-            Handles.RotateHandleSnapActive = active;
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Triggered when the rotate snap increment value changes.
-        /// </summary>
-        /// <param name="value">Value that determines in what increments to perform rotate snapping.</param>
-        private void OnRotateSnapValueChanged(float value)
-        {
-            Handles.RotateSnapAmount = MathEx.Clamp(value, 0.01f, 360.0f);
-            editorSettingsHash = EditorSettings.Hash;
-        }
-
-        /// <summary>
-        /// Updates toggle button states according to current editor options. This is useful if tools, coordinate mode,
-        /// pivot or other scene view options have been modified externally.
-        /// </summary>
-        private void UpdateButtonStates()
-        {
-            switch (EditorApplication.ActiveSceneTool)
-            {
-                case SceneViewTool.View:
-                    viewButton.Value = true;
-                    break;
-                case SceneViewTool.Move:
-                    moveButton.Value = true;
-                    break;
-                case SceneViewTool.Rotate:
-                    rotateButton.Value = true;
-                    break;
-                case SceneViewTool.Scale:
-                    scaleButton.Value = true;
-                    break;
-            }
-
-            switch (EditorApplication.ActiveCoordinateMode)
-            {
-                case HandleCoordinateMode.Local:
-                    localCoordButton.Value = true;
-                    break;
-                case HandleCoordinateMode.World:
-                    worldCoordButton.Value = true;
-                    break;
-            }
-
-            switch (EditorApplication.ActivePivotMode)
-            {
-                case HandlePivotMode.Center:
-                    centerButton.Value = true;
-                    break;
-                case HandlePivotMode.Pivot:
-                    pivotButton.Value = true;
-                    break;
-            }
-
-            if (Handles.MoveHandleSnapActive)
-                moveSnapButton.Value = true;
-            else
-                moveSnapButton.Value = false;
-
-            moveSnapInput.Value = Handles.MoveSnapAmount;
-
-            if (Handles.RotateHandleSnapActive)
-                rotateSnapButton.Value = true;
-            else
-                rotateSnapButton.Value = false;
-
-            moveSnapInput.Value = Handles.RotateSnapAmount.Degrees;
-        }
-
-        /// <summary>
-        /// Activates or deactivates the profiler overlay according to current editor settings.
-        /// </summary>
-        private void UpdateProfilerOverlay()
-        {
-            if (EditorSettings.GetBool(ProfilerOverlayActiveKey))
-            {
-                if (activeProfilerOverlay == null)
-                {
-                    SceneObject profilerSO = new SceneObject("EditorProfilerOverlay");
-                    profilerCamera = profilerSO.AddComponent<Camera>();
-                    profilerCamera.Target = renderTexture;
-                    profilerCamera.ClearFlags = ClearFlags.None;
-                    profilerCamera.Priority = 1;
-                    profilerCamera.Layers = 0;
-
-                    activeProfilerOverlay = profilerSO.AddComponent<ProfilerOverlay>();
-                }
-            }
-            else
-            {
-                if (activeProfilerOverlay != null)
-                {
-                    activeProfilerOverlay.SceneObject.Destroy();
-                    activeProfilerOverlay = null;
-                    profilerCamera = null;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Creates the scene camera and updates the render texture. Should be called at least once before using the
-        /// scene view. Should be called whenever the window is resized.
-        /// </summary>
-        /// <param name="width">Width of the scene render target, in pixels.</param>
-        /// <param name="height">Height of the scene render target, in pixels.</param>
-        private void UpdateRenderTexture(int width, int height)
-	    {
-            width = MathEx.Max(20, width);
-            height = MathEx.Max(20, height);
-
-            renderTexture = new RenderTexture2D(PixelFormat.R8G8B8A8, width, height);
-            renderTexture.Priority = 1;
-
-		    if (camera == null)
-		    {
-                SceneObject sceneCameraSO = new SceneObject("SceneCamera", true);
-                camera = sceneCameraSO.AddComponent<Camera>();
-                camera.Target = renderTexture;
-                camera.ViewportRect = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);
-
-                sceneCameraSO.Position = new Vector3(0, 0.5f, 1);
-                sceneCameraSO.LookAt(new Vector3(0, 0, 0));
-
-                camera.Priority = 2;
-                camera.NearClipPlane = 0.005f;
-                camera.FarClipPlane = 1000.0f;
-                camera.ClearColor = ClearColor;
-
-                cameraController = sceneCameraSO.AddComponent<SceneCamera>();
-
-                renderTextureGUI = new GUIRenderTexture(renderTexture);
-                rtPanel.AddElement(renderTextureGUI);
-
-                sceneViewHandler = new SceneViewHandler(this, camera);
-		    }
-		    else
-		    {
-		        camera.Target = renderTexture;
-		        renderTextureGUI.RenderTexture = renderTexture;
-		    }
-
-            Rect2I rtBounds = new Rect2I(0, 0, width, height);
-            renderTextureGUI.Bounds = rtBounds;
-
-		    // TODO - Consider only doing the resize once user stops resizing the widget in order to reduce constant
-		    // render target destroy/create cycle for every single pixel.
-
-		    camera.AspectRatio = width / (float)height;
-
-            if (profilerCamera != null)
-                profilerCamera.Target = renderTexture;
-	    }
-
-        /// <summary>
-        /// Parses an array of scene objects and removes elements that are children of elements that are also in the array.
-        /// </summary>
-        /// <param name="objects">Array containing duplicate objects as input, and array without duplicate objects as
-        ///                       output.</param>
-        private void CleanDuplicates(ref SceneObject[] objects)
-	    {
-		    List<SceneObject> cleanList = new List<SceneObject>();
-		    for (int i = 0; i < objects.Length; i++)
-		    {
-			    bool foundParent = false;
-                for (int j = 0; j < objects.Length; j++)
-                {
-                    SceneObject elem = objects[i];
-
-                    while (elem != null && elem != objects[j])
-                        elem = elem.Parent;
-
-                    bool isChildOf = elem == objects[j];
-
-				    if (i != j && isChildOf)
-				    {
-					    foundParent = true;
-					    break;
-				    }
-			    }
-
-			    if (!foundParent)
-				    cleanList.Add(objects[i]);
-		    }
-
-		    objects = cleanList.ToArray();
-	    }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays the scene view camera and various scene controls.
+    /// </summary>
+    internal sealed class SceneWindow : EditorWindow
+    {
+        internal const string ToggleProfilerOverlayBinding = "ToggleProfilerOverlay";
+        internal const string ViewToolBinding = "ViewTool";
+        internal const string MoveToolBinding = "MoveTool";
+        internal const string RotateToolBinding = "RotateTool";
+        internal const string ScaleToolBinding = "ScaleTool";
+        internal const string DuplicateBinding = "Duplicate";
+        internal const string DeleteBinding = "Delete";
+        internal const string FrameBinding = "SceneFrame";
+
+        private const int HeaderHeight = 20;
+        private const float DefaultPlacementDepth = 5.0f;
+        private static readonly Color ClearColor = new Color(83.0f/255.0f, 83.0f/255.0f, 83.0f/255.0f);
+        private const string ProfilerOverlayActiveKey = "_Internal_ProfilerOverlayActive";
+
+        private Camera camera;
+        private SceneCamera cameraController;
+        private RenderTexture2D renderTexture;
+        private GUILayoutY mainLayout;
+        private GUIPanel rtPanel;
+
+        private GUIRenderTexture renderTextureGUI;
+        private SceneViewHandler sceneViewHandler;
+
+        private GUIToggle viewButton;
+        private GUIToggle moveButton;
+        private GUIToggle rotateButton;
+        private GUIToggle scaleButton;
+
+        private GUIToggle localCoordButton;
+        private GUIToggle worldCoordButton;
+
+        private GUIToggle pivotButton;
+        private GUIToggle centerButton;
+
+        private GUIToggle moveSnapButton;
+        private GUIFloatField moveSnapInput;
+
+        private GUIToggle rotateSnapButton;
+        private GUIFloatField rotateSnapInput;
+
+        private int editorSettingsHash = int.MaxValue;
+
+        private VirtualButton duplicateKey;
+        private VirtualButton deleteKey;
+        private VirtualButton frameKey;
+
+        // Tool shortcuts
+        private VirtualButton viewToolKey;
+        private VirtualButton moveToolKey;
+        private VirtualButton rotateToolKey;
+        private VirtualButton scaleToolKey;
+
+        // Profiler overlay
+        private ProfilerOverlay activeProfilerOverlay;
+        private Camera profilerCamera;
+        private VirtualButton toggleProfilerOverlayKey;
+
+        // Drag & drop
+        private bool dragActive;
+        private SceneObject draggedSO;
+
+        /// <summary>
+        /// Returns the scene camera.
+        /// </summary>
+        public Camera Camera
+        {
+            get { return camera; }
+        }
+
+        /// <summary>
+        /// Constructs a new scene window.
+        /// </summary>
+        internal SceneWindow()
+        { }
+
+        /// <summary>
+        /// Opens a scene window if its not open already.
+        /// </summary>
+        [MenuItem("Windows/Scene", ButtonModifier.CtrlAlt, ButtonCode.S, 6000)]
+        private static void OpenSceneWindow()
+        {
+            OpenWindow<SceneWindow>();
+        }
+
+        /// <summary>
+        /// Focuses on the currently selected object.
+        /// </summary>
+        [MenuItem("Tools/Frame Selected", ButtonModifier.None, ButtonCode.F, 9296, true)]
+        private static void OpenSettingsWindow()
+        {
+            SceneWindow window = GetWindow<SceneWindow>();
+            if (window != null)
+                window.cameraController.FrameSelected();
+        }
+
+        /// <inheritdoc/>
+        protected override LocString GetDisplayName()
+        {
+            return new LocEdString("Scene");
+        }
+
+        private void OnInitialize()
+        {
+            mainLayout = GUI.AddLayoutY();
+
+            GUIContent viewIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.View));
+            GUIContent moveIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Move));
+            GUIContent rotateIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Rotate));
+            GUIContent scaleIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Scale));
+
+            GUIContent localIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Local));
+            GUIContent worldIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.World));
+
+            GUIContent pivotIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Pivot));
+            GUIContent centerIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.Center));
+
+            GUIContent moveSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.MoveSnap));
+            GUIContent rotateSnapIcon = new GUIContent(EditorBuiltin.GetSceneWindowIcon(SceneWindowIcon.RotateSnap));
+
+            GUIToggleGroup handlesTG = new GUIToggleGroup();
+            viewButton = new GUIToggle(viewIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            moveButton = new GUIToggle(moveIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            rotateButton = new GUIToggle(rotateIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            scaleButton = new GUIToggle(scaleIcon, handlesTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+
+            GUIToggleGroup coordModeTG = new GUIToggleGroup();
+            localCoordButton = new GUIToggle(localIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
+            worldCoordButton = new GUIToggle(worldIcon, coordModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(75));
+
+            GUIToggleGroup pivotModeTG = new GUIToggleGroup();
+            pivotButton = new GUIToggle(pivotIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            centerButton = new GUIToggle(centerIcon, pivotModeTG, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+
+            moveSnapButton = new GUIToggle(moveSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            moveSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
+
+            rotateSnapButton = new GUIToggle(rotateSnapIcon, EditorStyles.Button, GUIOption.FlexibleWidth(35));
+            rotateSnapInput = new GUIFloatField("", GUIOption.FlexibleWidth(35));
+
+            viewButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.View);
+            moveButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Move);
+            rotateButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Rotate);
+            scaleButton.OnClick += () => OnSceneToolButtonClicked(SceneViewTool.Scale);
+
+            localCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.Local);
+            worldCoordButton.OnClick += () => OnCoordinateModeButtonClicked(HandleCoordinateMode.World);
+
+            pivotButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Pivot);
+            centerButton.OnClick += () => OnPivotModeButtonClicked(HandlePivotMode.Center);
+
+            moveSnapButton.OnToggled += (bool active) => OnMoveSnapToggled(active);
+            moveSnapInput.OnChanged += (float value) => OnMoveSnapValueChanged(value);
+
+            rotateSnapButton.OnToggled += (bool active) => OnRotateSnapToggled(active);
+            rotateSnapInput.OnChanged += (float value) => OnRotateSnapValueChanged(value);
+
+            GUILayout handlesLayout = mainLayout.AddLayoutX();
+            handlesLayout.AddElement(viewButton);
+            handlesLayout.AddElement(moveButton);
+            handlesLayout.AddElement(rotateButton);
+            handlesLayout.AddElement(scaleButton);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(localCoordButton);
+            handlesLayout.AddElement(worldCoordButton);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(pivotButton);
+            handlesLayout.AddElement(centerButton);
+            handlesLayout.AddFlexibleSpace();
+            handlesLayout.AddElement(moveSnapButton);
+            handlesLayout.AddElement(moveSnapInput);
+            handlesLayout.AddSpace(10);
+            handlesLayout.AddElement(rotateSnapButton);
+            handlesLayout.AddElement(rotateSnapInput);
+
+            rtPanel = mainLayout.AddPanel();
+
+            toggleProfilerOverlayKey = new VirtualButton(ToggleProfilerOverlayBinding);
+            viewToolKey = new VirtualButton(ViewToolBinding);
+            moveToolKey = new VirtualButton(MoveToolBinding);
+            rotateToolKey = new VirtualButton(RotateToolBinding);
+            scaleToolKey = new VirtualButton(ScaleToolBinding);
+            duplicateKey = new VirtualButton(DuplicateBinding);
+            deleteKey = new VirtualButton(DeleteBinding);
+            frameKey = new VirtualButton(FrameBinding);
+
+            UpdateRenderTexture(Width, Height - HeaderHeight);
+            UpdateProfilerOverlay();
+        }
+
+        private void OnDestroy()
+        {
+            if (camera != null)
+            {
+                camera.SceneObject.Destroy();
+                camera = null;
+            }
+        }
+        
+        /// <summary>
+        /// Converts screen coordinates into coordinates relative to the scene view render texture.
+        /// </summary>
+        /// <param name="screenPos">Coordinates relative to the screen.</param>
+        /// <param name="scenePos">Output coordinates relative to the scene view texture.</param>
+        /// <returns>True if the coordinates are within the scene view texture, false otherwise.</returns>
+        private bool ScreenToScenePos(Vector2I screenPos, out Vector2I scenePos)
+        {
+            scenePos = screenPos;
+            Vector2I windowPos = ScreenToWindowPos(screenPos);
+
+            Rect2I bounds = GUILayoutUtility.CalculateBounds(renderTextureGUI);
+
+            if (bounds.Contains(windowPos))
+            {
+                scenePos.x = windowPos.x - bounds.x;
+                scenePos.y = windowPos.y - bounds.y;
+
+                return true;
+            }
+
+            return false;
+        }
+
+        private void OnEditorUpdate()
+        {
+            if (HasFocus)
+            {
+                if (!Input.IsPointerButtonHeld(PointerButton.Right))
+                {
+                    if (VirtualInput.IsButtonUp(toggleProfilerOverlayKey))
+                        EditorSettings.SetBool(ProfilerOverlayActiveKey, !EditorSettings.GetBool(ProfilerOverlayActiveKey));
+
+                    if (VirtualInput.IsButtonUp(viewToolKey))
+                        EditorApplication.ActiveSceneTool = SceneViewTool.View;
+
+                    if (VirtualInput.IsButtonUp(moveToolKey))
+                        EditorApplication.ActiveSceneTool = SceneViewTool.Move;
+
+                    if (VirtualInput.IsButtonUp(rotateToolKey))
+                        EditorApplication.ActiveSceneTool = SceneViewTool.Rotate;
+
+                    if (VirtualInput.IsButtonUp(scaleToolKey))
+                        EditorApplication.ActiveSceneTool = SceneViewTool.Scale;
+
+                    if (VirtualInput.IsButtonUp(duplicateKey))
+                    {
+                        SceneObject[] selectedObjects = Selection.SceneObjects;
+                        CleanDuplicates(ref selectedObjects);
+
+                        if (selectedObjects.Length > 0)
+                        {
+                            String message;
+                            if (selectedObjects.Length == 1)
+                                message = "Duplicated " + selectedObjects[0].Name;
+                            else
+                                message = "Duplicated " + selectedObjects.Length + " elements";
+
+                            UndoRedo.CloneSO(selectedObjects, message);
+                            EditorApplication.SetSceneDirty();
+                        }
+                    }
+
+                    if (VirtualInput.IsButtonUp(deleteKey))
+                    {
+                        SceneObject[] selectedObjects = Selection.SceneObjects;
+                        CleanDuplicates(ref selectedObjects);
+
+                        if (selectedObjects.Length > 0)
+                        {
+                            foreach (var so in selectedObjects)
+                            {
+                                string message = "Deleted " + so.Name;
+                                UndoRedo.DeleteSO(so, message);
+                            }
+
+                            EditorApplication.SetSceneDirty();
+                        }
+                    }
+                }
+            }
+
+            // Refresh GUI buttons if needed (in case someones changes the values from script)
+            if (editorSettingsHash != EditorSettings.Hash)
+            {
+                UpdateButtonStates();
+                UpdateProfilerOverlay();
+                editorSettingsHash = EditorSettings.Hash;
+            }
+
+            // Update scene view handles and selection
+            sceneViewHandler.Update();
+
+            bool handleActive = false;
+            if (Input.IsPointerButtonUp(PointerButton.Left))
+            {
+                if (sceneViewHandler.IsHandleActive())
+                {
+                    sceneViewHandler.ClearHandleSelection();
+                    handleActive = true;
+                }
+            }
+
+            Vector2I scenePos;
+            bool inBounds = ScreenToScenePos(Input.PointerPosition, out scenePos);
+
+            bool draggedOver = DragDrop.DragInProgress || DragDrop.DropInProgress;
+            draggedOver &= inBounds && DragDrop.Type == DragDropType.Resource;
+
+            if (draggedOver)
+            {
+                if (DragDrop.DropInProgress)
+                {
+                    dragActive = false;
+                    draggedSO = null;
+                }
+                else
+                {
+                    if (!dragActive)
+                    {
+                        dragActive = true;
+
+                        ResourceDragDropData dragData = (ResourceDragDropData)DragDrop.Data;
+
+                        string draggedMeshPath = "";
+                        string[] draggedPaths = dragData.Paths;
+
+                        for (int i = 0; i < draggedPaths.Length; i++)
+                        {
+                            LibraryEntry entry = ProjectLibrary.GetEntry(draggedPaths[i]);
+                            if (entry != null && entry.Type == LibraryEntryType.File)
+                            {
+                                FileEntry fileEntry = (FileEntry) entry;
+                                if (fileEntry.ResType == ResourceType.Mesh)
+                                {
+                                    draggedMeshPath = draggedPaths[i];
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (!string.IsNullOrEmpty(draggedMeshPath))
+                        {
+                            string meshName = Path.GetFileNameWithoutExtension(draggedMeshPath);
+
+                            draggedSO = new SceneObject(meshName);
+                            Mesh mesh = ProjectLibrary.Load<Mesh>(draggedMeshPath);
+
+                            Renderable renderable = draggedSO.AddComponent<Renderable>();
+                            renderable.Mesh = mesh;
+
+                            EditorApplication.SetSceneDirty();
+                        }
+                    }
+
+                    if (draggedSO != null)
+                    {
+                        Ray worldRay = camera.ScreenToWorldRay(scenePos);
+                        draggedSO.Position = worldRay*DefaultPlacementDepth;
+                    }
+                }
+
+                return;
+            }
+            else
+            {
+                if (dragActive)
+                {
+                    dragActive = false;
+
+                    if (draggedSO != null)
+                    {
+                        draggedSO.Destroy();
+                        draggedSO = null;
+                    }
+                }
+            }
+
+            if (HasFocus)
+            {
+                cameraController.EnableInput(true);
+
+                if (inBounds)
+                {
+                    if (Input.IsPointerButtonDown(PointerButton.Left))
+                    {
+                        sceneViewHandler.TrySelectHandle(scenePos);
+                    }
+                    else if (Input.IsPointerButtonUp(PointerButton.Left))
+                    {
+                        if (!handleActive)
+                        {
+                            bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
+                                            Input.IsButtonHeld(ButtonCode.RightControl);
+
+                            sceneViewHandler.PickObject(scenePos, ctrlHeld);
+                        }
+                    }
+                }
+            }
+            else
+                cameraController.EnableInput(false);
+
+            sceneViewHandler.UpdateHandle(scenePos, Input.PointerDelta);
+            sceneViewHandler.UpdateSelection();
+
+            if (VirtualInput.IsButtonDown(frameKey))
+            {
+                cameraController.FrameSelected();
+            }
+        }
+
+        /// <inheritdoc/>
+        protected override void WindowResized(int width, int height)
+        {
+            UpdateRenderTexture(width, height - HeaderHeight);
+
+            base.WindowResized(width, height);
+        }
+
+        /// <inheritdoc/>
+        protected override void FocusChanged(bool inFocus)
+        {
+            if (!inFocus)
+            {
+                sceneViewHandler.ClearHandleSelection();
+            }
+        }
+
+        /// <summary>
+        /// Triggered when one of the scene tool buttons is clicked, changing the active scene handle.
+        /// </summary>
+        /// <param name="tool">Clicked scene tool to activate.</param>
+        private void OnSceneToolButtonClicked(SceneViewTool tool)
+        {
+            EditorApplication.ActiveSceneTool = tool;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when one of the coordinate mode buttons is clicked, changing the active coordinate mode.
+        /// </summary>
+        /// <param name="mode">Clicked coordinate mode to activate.</param>
+        private void OnCoordinateModeButtonClicked(HandleCoordinateMode mode)
+        {
+            EditorApplication.ActiveCoordinateMode = mode;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when one of the pivot buttons is clicked, changing the active pivot mode.
+        /// </summary>
+        /// <param name="mode">Clicked pivot mode to activate.</param>
+        private void OnPivotModeButtonClicked(HandlePivotMode mode)
+        {
+            EditorApplication.ActivePivotMode = mode;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the move snap button is toggled.
+        /// </summary>
+        /// <param name="active">Determins should be move snap be activated or deactivated.</param>
+        private void OnMoveSnapToggled(bool active)
+        {
+            Handles.MoveHandleSnapActive = active;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the move snap increment value changes.
+        /// </summary>
+        /// <param name="value">Value that determines in what increments to perform move snapping.</param>
+        private void OnMoveSnapValueChanged(float value)
+        {
+            Handles.MoveSnapAmount = MathEx.Clamp(value, 0.01f, 1000.0f);
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the rotate snap button is toggled.
+        /// </summary>
+        /// <param name="active">Determins should be rotate snap be activated or deactivated.</param>
+        private void OnRotateSnapToggled(bool active)
+        {
+            Handles.RotateHandleSnapActive = active;
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Triggered when the rotate snap increment value changes.
+        /// </summary>
+        /// <param name="value">Value that determines in what increments to perform rotate snapping.</param>
+        private void OnRotateSnapValueChanged(float value)
+        {
+            Handles.RotateSnapAmount = MathEx.Clamp(value, 0.01f, 360.0f);
+            editorSettingsHash = EditorSettings.Hash;
+        }
+
+        /// <summary>
+        /// Updates toggle button states according to current editor options. This is useful if tools, coordinate mode,
+        /// pivot or other scene view options have been modified externally.
+        /// </summary>
+        private void UpdateButtonStates()
+        {
+            switch (EditorApplication.ActiveSceneTool)
+            {
+                case SceneViewTool.View:
+                    viewButton.Value = true;
+                    break;
+                case SceneViewTool.Move:
+                    moveButton.Value = true;
+                    break;
+                case SceneViewTool.Rotate:
+                    rotateButton.Value = true;
+                    break;
+                case SceneViewTool.Scale:
+                    scaleButton.Value = true;
+                    break;
+            }
+
+            switch (EditorApplication.ActiveCoordinateMode)
+            {
+                case HandleCoordinateMode.Local:
+                    localCoordButton.Value = true;
+                    break;
+                case HandleCoordinateMode.World:
+                    worldCoordButton.Value = true;
+                    break;
+            }
+
+            switch (EditorApplication.ActivePivotMode)
+            {
+                case HandlePivotMode.Center:
+                    centerButton.Value = true;
+                    break;
+                case HandlePivotMode.Pivot:
+                    pivotButton.Value = true;
+                    break;
+            }
+
+            if (Handles.MoveHandleSnapActive)
+                moveSnapButton.Value = true;
+            else
+                moveSnapButton.Value = false;
+
+            moveSnapInput.Value = Handles.MoveSnapAmount;
+
+            if (Handles.RotateHandleSnapActive)
+                rotateSnapButton.Value = true;
+            else
+                rotateSnapButton.Value = false;
+
+            moveSnapInput.Value = Handles.RotateSnapAmount.Degrees;
+        }
+
+        /// <summary>
+        /// Activates or deactivates the profiler overlay according to current editor settings.
+        /// </summary>
+        private void UpdateProfilerOverlay()
+        {
+            if (EditorSettings.GetBool(ProfilerOverlayActiveKey))
+            {
+                if (activeProfilerOverlay == null)
+                {
+                    SceneObject profilerSO = new SceneObject("EditorProfilerOverlay");
+                    profilerCamera = profilerSO.AddComponent<Camera>();
+                    profilerCamera.Target = renderTexture;
+                    profilerCamera.ClearFlags = ClearFlags.None;
+                    profilerCamera.Priority = 1;
+                    profilerCamera.Layers = 0;
+
+                    activeProfilerOverlay = profilerSO.AddComponent<ProfilerOverlay>();
+                }
+            }
+            else
+            {
+                if (activeProfilerOverlay != null)
+                {
+                    activeProfilerOverlay.SceneObject.Destroy();
+                    activeProfilerOverlay = null;
+                    profilerCamera = null;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Creates the scene camera and updates the render texture. Should be called at least once before using the
+        /// scene view. Should be called whenever the window is resized.
+        /// </summary>
+        /// <param name="width">Width of the scene render target, in pixels.</param>
+        /// <param name="height">Height of the scene render target, in pixels.</param>
+        private void UpdateRenderTexture(int width, int height)
+	    {
+            width = MathEx.Max(20, width);
+            height = MathEx.Max(20, height);
+
+            renderTexture = new RenderTexture2D(PixelFormat.R8G8B8A8, width, height);
+            renderTexture.Priority = 1;
+
+		    if (camera == null)
+		    {
+                SceneObject sceneCameraSO = new SceneObject("SceneCamera", true);
+                camera = sceneCameraSO.AddComponent<Camera>();
+                camera.Target = renderTexture;
+                camera.ViewportRect = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);
+
+                sceneCameraSO.Position = new Vector3(0, 0.5f, 1);
+                sceneCameraSO.LookAt(new Vector3(0, 0.5f, 0));
+
+                camera.Priority = 2;
+                camera.NearClipPlane = 0.05f;
+                camera.FarClipPlane = 2500.0f;
+                camera.ClearColor = ClearColor;
+
+                cameraController = sceneCameraSO.AddComponent<SceneCamera>();
+
+                renderTextureGUI = new GUIRenderTexture(renderTexture);
+                rtPanel.AddElement(renderTextureGUI);
+
+                sceneViewHandler = new SceneViewHandler(this, camera);
+		    }
+		    else
+		    {
+		        camera.Target = renderTexture;
+		        renderTextureGUI.RenderTexture = renderTexture;
+		    }
+
+            Rect2I rtBounds = new Rect2I(0, 0, width, height);
+            renderTextureGUI.Bounds = rtBounds;
+
+		    // TODO - Consider only doing the resize once user stops resizing the widget in order to reduce constant
+		    // render target destroy/create cycle for every single pixel.
+
+		    camera.AspectRatio = width / (float)height;
+
+            if (profilerCamera != null)
+                profilerCamera.Target = renderTexture;
+	    }
+
+        /// <summary>
+        /// Parses an array of scene objects and removes elements that are children of elements that are also in the array.
+        /// </summary>
+        /// <param name="objects">Array containing duplicate objects as input, and array without duplicate objects as
+        ///                       output.</param>
+        private void CleanDuplicates(ref SceneObject[] objects)
+	    {
+		    List<SceneObject> cleanList = new List<SceneObject>();
+		    for (int i = 0; i < objects.Length; i++)
+		    {
+			    bool foundParent = false;
+                for (int j = 0; j < objects.Length; j++)
+                {
+                    SceneObject elem = objects[i];
+
+                    while (elem != null && elem != objects[j])
+                        elem = elem.Parent;
+
+                    bool isChildOf = elem == objects[j];
+
+				    if (i != j && isChildOf)
+				    {
+					    foundParent = true;
+					    break;
+				    }
+			    }
+
+			    if (!foundParent)
+				    cleanList.Add(objects[i]);
+		    }
+
+		    objects = cleanList.ToArray();
+	    }
+    }
+}

+ 98 - 76
MBansheeEngine/Math/AABox.cs

@@ -1,76 +1,98 @@
-using System.Runtime.InteropServices;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Axis aligned box represented by minimum and maximum point.
-    /// </summary>
-    [StructLayout(LayoutKind.Sequential), SerializeObject]
-	public struct AABox // Note: Must match C++ class AABox
-	{
-        [SerializeField]
-        private Vector3 _minimum;
-
-        [SerializeField]
-		private Vector3 _maximum;
-
-        /// <summary>
-        /// Corner of the box with minimum values (opposite to maximum corner).
-        /// </summary>
-        public Vector3 Minimum
-        {
-            get { return _minimum; }
-            set { _minimum = value; }
-        }
-
-        /// <summary>
-        /// Corner of the box with maximum values (opposite to minimum corner).
-        /// </summary>
-        public Vector3 Maximum
-        {
-            get { return _maximum; }
-            set { _maximum = value; }
-        }
-
-        /// <summary>
-        /// Returns the center of the box.
-        /// </summary>
-        public Vector3 Center
-        {
-            get 
-            { 		
-                return new Vector3((_maximum.x + _minimum.x) * 0.5f,
-			            (_maximum.y + _minimum.y) * 0.5f,
-			            (_maximum.z + _minimum.z) * 0.5f);
-            }
-        }
-
-        /// <summary>
-        /// Returns the width, height and depth of the box.
-        /// </summary>
-	    public Vector3 Size
-	    {
-	        get
-	        {
-	            return _maximum - _minimum;
-	        }
-	    }
-
-        /// <summary>
-        /// Creates a new axis aligned box.
-        /// </summary>
-        /// <param name="min">Corner of the box with minimum values.</param>
-        /// <param name="max">Corner of the box with maximum values.</param>
-        public AABox(Vector3 min, Vector3 max)
-        {
-            _minimum = min;
-            _maximum = max;
-        }
-
-        /// <inheritdoc/>
-        public override string ToString()
-        {
-            return "Min: " + _minimum + ". Max: " + _maximum;
-        }
-	};
-}
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Axis aligned box represented by minimum and maximum point.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential), SerializeObject]
+	public struct AABox // Note: Must match C++ class AABox
+	{
+        [SerializeField]
+        private Vector3 minimum;
+
+        [SerializeField]
+		private Vector3 maximum;
+
+        /// <summary>
+        /// Corner of the box with minimum values (opposite to maximum corner).
+        /// </summary>
+        public Vector3 Minimum
+        {
+            get { return minimum; }
+            set { minimum = value; }
+        }
+
+        /// <summary>
+        /// Corner of the box with maximum values (opposite to minimum corner).
+        /// </summary>
+        public Vector3 Maximum
+        {
+            get { return maximum; }
+            set { maximum = value; }
+        }
+
+        /// <summary>
+        /// Returns the center of the box.
+        /// </summary>
+        public Vector3 Center
+        {
+            get 
+            { 		
+                return new Vector3((maximum.x + minimum.x) * 0.5f,
+			            (maximum.y + minimum.y) * 0.5f,
+			            (maximum.z + minimum.z) * 0.5f);
+            }
+        }
+
+        /// <summary>
+        /// Returns the width, height and depth of the box.
+        /// </summary>
+	    public Vector3 Size
+	    {
+	        get
+	        {
+	            return maximum - minimum;
+	        }
+	    }
+
+        /// <summary>
+        /// Creates a new axis aligned box.
+        /// </summary>
+        /// <param name="min">Corner of the box with minimum values.</param>
+        /// <param name="max">Corner of the box with maximum values.</param>
+        public AABox(Vector3 min, Vector3 max)
+        {
+            minimum = min;
+            maximum = max;
+        }
+
+        /// <summary>
+        /// Transforms the bounding box by the given matrix.
+        /// 
+        /// As the resulting box will no longer be axis aligned, an axis align box is instead created by encompassing the 
+        /// transformed oriented bounding box. Retrieving the value as an actual OBB would provide a tighter fit.
+        /// </summary>
+        /// <param name="tfrm">Affine matrix to transform the box with.</param>
+        public void TransformAffine(Matrix4 tfrm)
+        {
+            Vector3 center = Center;
+            Vector3 halfSize = Size*0.5f;
+
+            Vector3 newCenter = tfrm.MultiplyAffine(center);
+            Vector3 newHalfSize = new Vector3(
+                MathEx.Abs(tfrm.m00) * halfSize.x + MathEx.Abs(tfrm.m01) * halfSize.y + MathEx.Abs(tfrm.m02) * halfSize.z,
+                MathEx.Abs(tfrm.m10) * halfSize.x + MathEx.Abs(tfrm.m11) * halfSize.y + MathEx.Abs(tfrm.m12) * halfSize.z,
+                MathEx.Abs(tfrm.m20) * halfSize.x + MathEx.Abs(tfrm.m21) * halfSize.y + MathEx.Abs(tfrm.m22) * halfSize.z);
+
+            minimum = newCenter - newHalfSize;
+            maximum = newCenter + newHalfSize;
+        }
+
+        /// <inheritdoc/>
+        public override string ToString()
+        {
+            return "Min: " + minimum + ". Max: " + maximum;
+        }
+	};
+}

+ 69 - 8
MBansheeEngine/Math/Quaternion.cs

@@ -354,7 +354,7 @@ namespace BansheeEngine
         /// <param name="forward">Direction to orient the object towards.</param>
         public void SetLookRotation(Vector3 forward)
         {
-            SetLookRotation(forward, Vector3.YAxis);
+            FromToRotation(-Vector3.ZAxis, forward);
         }
 
         /// <summary>
@@ -364,10 +364,22 @@ namespace BansheeEngine
         /// <param name="up">Axis that determines the upward direction of the object.</param>
         public void SetLookRotation(Vector3 forward, Vector3 up)
         {
-            Quaternion forwardRot = FromToRotation(Vector3.ZAxis, forward);
-            Quaternion upRot = FromToRotation(Vector3.YAxis, up);
+            Vector3 forwardNrm = Vector3.Normalize(forward);
+            Vector3 upNrm = Vector3.Normalize(up);
 
-            this = forwardRot * upRot;
+            if (MathEx.ApproxEquals(Vector3.Dot(forwardNrm, upNrm), 1.0f))
+            {
+                SetLookRotation(forwardNrm);
+                return;
+            }
+
+            Vector3 x = Vector3.Cross(forwardNrm, upNrm);
+            Vector3 y = Vector3.Cross(x, forwardNrm);
+
+            x.Normalize();
+            y.Normalize();
+
+            this = Quaternion.FromAxes(x, y, -forwardNrm);
         }
 
         /// <summary>
@@ -382,7 +394,7 @@ namespace BansheeEngine
         ///                            the two quaternions.</param>
         /// <returns>Interpolated quaternion representing a rotation between <paramref name="from"/> and 
         /// <paramref name="to"/>.</returns>
-        public static Quaternion Slerp(Quaternion from, Quaternion to, float t, bool shortestPath = false)
+        public static Quaternion Slerp(Quaternion from, Quaternion to, float t, bool shortestPath = true)
         {
             float cos = from.w*to.w + from.x*to.x + from.y*to.y + from.z*from.z;
             Quaternion quat;
@@ -468,10 +480,33 @@ namespace BansheeEngine
         }
 
         /// <summary>
-        /// Converts the quaternion rotation into euler angle (pitch/yaw/roll) rotation.
+        /// Converts a quaternion into an orthonormal set of axes.
         /// </summary>
-        /// <returns>Rotation as euler angles, in degrees.</returns>
-        public Vector3 ToEuler()
+        /// <param name="xAxis">Output normalized x axis.</param>
+        /// <param name="yAxis">Output normalized y axis.</param>
+        /// <param name="zAxis">Output normalized z axis.</param>
+        public void ToAxes(ref Vector3 xAxis, ref Vector3 yAxis, ref Vector3 zAxis)
+        {
+            Matrix3 matRot = ToRotationMatrix();
+
+            xAxis.x = matRot[0, 0];
+		    xAxis.y = matRot[1, 0];
+		    xAxis.z = matRot[2, 0];
+
+		    yAxis.x = matRot[0, 1];
+		    yAxis.y = matRot[1, 1];
+		    yAxis.z = matRot[2, 1];
+
+		    zAxis.x = matRot[0, 2];
+		    zAxis.y = matRot[1, 2];
+		    zAxis.z = matRot[2, 2];
+	    }
+
+    /// <summary>
+    /// Converts the quaternion rotation into euler angle (pitch/yaw/roll) rotation.
+    /// </summary>
+    /// <returns>Rotation as euler angles, in degrees.</returns>
+    public Vector3 ToEuler()
         {
             Matrix3 matRot = ToRotationMatrix();
             return matRot.ToEulerAngles();
@@ -661,6 +696,32 @@ namespace BansheeEngine
             return quat;
         }
 
+        /// <summary>
+        /// Initializes the quaternion from orthonormal set of axes. 
+        /// </summary>
+        /// <param name="xAxis">Normalized x axis.</param>
+        /// <param name="yAxis">Normalized y axis.</param>
+        /// <param name="zAxis">Normalized z axis.</param>
+        /// <returns>Quaternion that represents a rotation from base axes to the specified set of axes.</returns>
+        public static Quaternion FromAxes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
+        {
+            Matrix3 mat;
+
+            mat.m00 = xAxis.x;
+            mat.m10 = xAxis.y;
+            mat.m20 = xAxis.z;
+
+            mat.m01 = yAxis.x;
+            mat.m11 = yAxis.y;
+            mat.m21 = yAxis.z;
+
+            mat.m02 = zAxis.x;
+            mat.m12 = zAxis.y;
+            mat.m22 = zAxis.z;
+
+            return FromRotationMatrix(mat);
+        }
+
         /// <summary>
         /// Creates a quaternion from the provided euler angle (pitch/yaw/roll) rotation.
         /// </summary>

+ 1 - 1
MBansheeEngine/SceneObject.cs

@@ -356,7 +356,7 @@ namespace BansheeEngine
         /// <summary>
         /// Orients the object so it is looking at the provided location.
         /// </summary>
-        /// <param name="position">Position in local space where to look at.</param>
+        /// <param name="position">Position in world space where to look at.</param>
         /// <param name="up">Determines the object's Y axis orientation.</param>
         public void LookAt(Vector3 position, Vector3 up)
         {

+ 9 - 11
README.md

@@ -2,13 +2,17 @@
 
 A modern open-source game development toolkit. It aims to provide simple yet powerful environment for creating games and other graphical applications. A wide range of features are available, ranging from a math and utility library, to DirectX 11 and OpenGL render systems all the way to asset processing, fully featured editor and C# scripting.
 
-Banshee aims to become a standard in game development by providing rock solid implementations of known and widely used techniques while also being flexible enough to implement constantly changing cutting edge techniques. Built from ground up to be a modern library using modern design principles - everything is modular, layered and decoupled as much as possible, making it easier to modify, replace or add functionality. Platform specific functionality is kept at a minimum making porting as easy as possible. Additionally every non-trivial method, class and field is documented.
+For game developers Banshee aims to provide a complete set of tools and APIs needed to make a game from start to finish. It provides a highly intuitive and customizable editor that is easy to use by artists, designers and programmers alike, supporting development stages from asset management, scene building, scripting to game publishing. C# scripting makes your development easier by giving you access to the entire .NET library, along with fast iterations times while being safe and easy to write. Editor is fully extensible with the help of specially developed scripting API so you may customize it for exact needs of your project.
 
-On top of the powerful and flexible C++ core lies a fully featured editor and a C# scripting system. C# scripting makes your development easier by giving you access to the entire .NET library, along with fast iterations times while being safe and easy to write. Editor is fully extensible with the help of C# scripting so you may customize it for exact needs of your project.
+For engine developers it aims to provide a high quality foundation to build and improve upon. Banshee runs on a powerful multi-threaded C++14 core created to take advantage of all resources modern hardware can offer. Built from ground up using modern design principles, everything is modular, layered and decoupled as much as possible, making it easier to modify, replace or add functionality. Platform specific functionality is kept at a minimum making porting as easy as possible. Additionally every non-trivial method, class and field is documented. All this combined makes Banshee a great platform to build upon, for developing higher level systems like editor tools, core enhancements like new rendering techniques and platform ports, all the way to creating fully specialized toolsets that fit your team's needs exactly.
+
+## Development state
+
+Project is currently in active development. Current version is considered a preview version. Bugs are to be expected, optimization is not complete and some major features are yet to be added (see below for a list).
 
 ## Download/Install
 
-Downloading pre-compiled binaries is the easiest way to check out Banshee if you don't want to go through the hassle of compiling it yourself. But be aware that this a development version and you should expect bugs and missing features.
+Downloading pre-compiled binaries is the easiest way to check out Banshee if you don't want to go through the hassle of compiling it yourself. 
 
 [Download binaries (Windows x64)] (http://bearishsun.thalassa.feralhosting.com/Banshee_Win_x64_v0.2.rar)
 
@@ -114,20 +118,14 @@ Aside from Banshee source code you will also need various third party dependenci
     * Utility library
       * Math, file system, events, thread pool, task scheduler, logging, memory allocators and more
     
-## Features (upcoming)
+## Features (coming in near future)
  * DirectX 12/Vulkan support
  * Physically based renderer
  * Physics system integration
  * Audio system integration
- * Video system integration
- * Networking system integration
  * Animation
  * Mac & Linux support
 
-## Development state
-
-Project is currently in active development. Current version is considered a preview version. Bugs are to be expected and new features will be added as development progresses.
-
 ## Getting started
 
 Banshee is a multi-layered engine that aims to be flexible enough to handle various needs. Therefore this section is split into two sub-sections, first one aimed for game developers (high-level C# programmers, artists, designers) and engine developers (low level C++ programmers).
@@ -194,6 +192,6 @@ Banshee is offered completely free for personal or commercial use under the Gene
 
 # Author
 
-Banshee is developed by Marko Pintera. I've been a professional game developer for the last five years working on various mid-sized titles. My interests lie in engine and graphics development which I spend most of my free time on.
+Banshee was created and is developed by Marko Pintera.
 
 Contact me at [e-mail] (http://scr.im/39d1) or add me on [LinkedIn] (https://goo.gl/t6pPPs).