Procházet zdrojové kódy

Scene axes handle WIP
Modified keys shift/ctrl/alt are now properly reported by virtual input as button presses

BearishSun před 10 roky
rodič
revize
c580127a4b

+ 80 - 71
BansheeEditor/Source/BsEditorUtility.cpp

@@ -1,72 +1,81 @@
-#include "BsEditorUtility.h"
-#include "BsSceneObject.h"
-#include "BsCRenderable.h"
-
-namespace BansheeEngine
-{
-	AABox EditorUtility::calculateBounds(const HSceneObject& object)
-	{
-		Vector<HSceneObject> objects = { object };
-
-		return calculateBounds(objects);
-	}
-
-	AABox EditorUtility::calculateBounds(const Vector<HSceneObject>& objects)
-	{
-		if (objects.size() == 0)
-			return AABox(Vector3::ZERO, Vector3::ZERO);
-
-		AABox bounds = AABox(Vector3::INF, -Vector3::INF);
-		bool gotOneMesh = false;
-
-		for (auto& object : objects)
-		{
-			AABox meshBounds;
-			if (calculateMeshBounds(object, meshBounds))
-			{
-				bounds.merge(meshBounds);
-
-				gotOneMesh = true;
-			}
-		}
-
-		if (!gotOneMesh)
-		{
-			for (auto& object : objects)
-				bounds.merge(object->getWorldPosition());
-		}
-
-		return bounds;
-	}
-
-	bool EditorUtility::calculateMeshBounds(const HSceneObject& object, AABox& bounds)
-	{
-		bounds = AABox(Vector3::ZERO, Vector3::ZERO);
-		if (object.isDestroyed())
-			return false;
-
-		bool foundOne = false;
-		const Vector<HComponent>& components = object->getComponents();
-		for (auto& component : components)
-		{
-			Bounds curBounds;
-			if (component->calculateBounds(curBounds))
-			{
-				if (!foundOne)
-				{
-					bounds = curBounds.getBox();
-					foundOne = true;
-				}
-				else
-					bounds.merge(curBounds.getBox());
-			}
-			else
-			{
-				if (!foundOne)
-					bounds = curBounds.getBox();
-			}
-		}
-
-		return foundOne;
-	}
+#include "BsEditorUtility.h"
+#include "BsSceneObject.h"
+#include "BsCRenderable.h"
+
+namespace BansheeEngine
+{
+	AABox EditorUtility::calculateBounds(const HSceneObject& object)
+	{
+		Vector<HSceneObject> objects = { object };
+
+		return calculateBounds(objects);
+	}
+
+	AABox EditorUtility::calculateBounds(const Vector<HSceneObject>& objects)
+	{
+		if (objects.size() == 0)
+			return AABox(Vector3::ZERO, Vector3::ZERO);
+
+		AABox bounds = AABox(Vector3::INF, -Vector3::INF);
+		bool gotOneMesh = false;
+
+		for (auto& object : objects)
+		{
+			AABox meshBounds;
+			if (calculateMeshBounds(object, meshBounds))
+			{
+				bounds.merge(meshBounds);
+
+				gotOneMesh = true;
+			}
+		}
+
+		if (!gotOneMesh)
+		{
+			for (auto& object : objects)
+			{
+				if (object.isDestroyed())
+					continue;
+
+				bounds.merge(object->getWorldPosition());
+				gotOneMesh = true;
+			}
+		}
+
+		if(gotOneMesh)
+			return bounds;
+
+		return AABox(Vector3::ZERO, Vector3::ZERO);
+	}
+
+	bool EditorUtility::calculateMeshBounds(const HSceneObject& object, AABox& bounds)
+	{
+		bounds = AABox(Vector3::ZERO, Vector3::ZERO);
+		if (object.isDestroyed())
+			return false;
+
+		bool foundOne = false;
+		const Vector<HComponent>& components = object->getComponents();
+		for (auto& component : components)
+		{
+			Bounds curBounds;
+			if (component->calculateBounds(curBounds))
+			{
+				if (!foundOne)
+				{
+					bounds = curBounds.getBox();
+					foundOne = true;
+				}
+				else
+					bounds.merge(curBounds.getBox());
+			}
+			else
+			{
+				if (!foundOne)
+					bounds = curBounds.getBox();
+			}
+		}
+
+		return foundOne;
+	}
 }

+ 1 - 2
BansheeEngine/Include/BsCamera.h

@@ -449,8 +449,7 @@ namespace BansheeEngine
         static const float INFINITE_FAR_PLANE_ADJUST; /**< Small constant used to reduce far plane projection to avoid inaccuracies. */
 
 	protected:
-		CameraBase(RenderTargetPtr target = nullptr,
-			float left = 0.0f, float top = 0.0f, float width = 1.0f, float height = 1.0f);
+		CameraBase();
 
 		/**
 		 * @brief	Calculate projection parameters that are used when constructing the projection matrix.

+ 5 - 5
BansheeEngine/Source/BsCamera.cpp

@@ -17,8 +17,8 @@ namespace BansheeEngine
 {
 	const float CameraBase::INFINITE_FAR_PLANE_ADJUST = 0.00001f;
 
-	CameraBase::CameraBase(RenderTargetPtr target, float left, float top, float width, float height)
-		:mProjType(PT_PERSPECTIVE), mHorzFOV(Radian(Math::PI / 4.0f)), mFarDist(1000.0f),
+	CameraBase::CameraBase()
+		:mProjType(PT_PERSPECTIVE), mHorzFOV(Degree(90.0f)), mFarDist(1000.0f),
 		mNearDist(0.05f), mAspect(1.33333333333333f), mOrthoHeight(1000), mRecalcFrustum(true), mRecalcFrustumPlanes(true),
 		mCustomViewMatrix(false), mCustomProjMatrix(false), mFrustumExtentsManuallySet(false), mPriority(0), 
 		mLayers(0xFFFFFFFFFFFFFFFF), mRecalcView(true), mCameraFlags(0)
@@ -168,9 +168,9 @@ namespace BansheeEngine
 			}
 			else if (mProjType == PT_PERSPECTIVE)
 			{
-				Radian thetaY(mHorzFOV * 0.5f);
-				float tanThetaY = Math::tan(thetaY);
-				float tanThetaX = tanThetaY * mAspect;
+				Radian thetaX(mHorzFOV * 0.5f);
+				float tanThetaX = Math::tan(thetaX);
+				float tanThetaY = tanThetaX / mAspect;
 
 				float half_w = tanThetaX * mNearDist;
 				float half_h = tanThetaY * mNearDist;

+ 8 - 7
BansheeEngine/Source/BsLight.cpp

@@ -180,7 +180,8 @@ namespace BansheeEngine
 			UINT32* indexData = meshData->getIndices32();
 			UINT8* positionData = meshData->getElementData(VES_POSITION);
 
-			ShapeMeshes3D::solidSphere(mBounds, positionData, nullptr, 0,
+			Sphere localSphere(Vector3::ZERO, mRange);
+			ShapeMeshes3D::solidSphere(localSphere, positionData, nullptr, 0,
 				vertexDesc->getVertexStride(), indexData, 0, 1);
 
 			mMesh = MeshCore::create(meshData);
@@ -213,11 +214,11 @@ namespace BansheeEngine
 			for (UINT32 sliceIdx = 0; sliceIdx < (LIGHT_CONE_NUM_SLICES - 1); sliceIdx++)
 			{
 				for (UINT32 sideIdx = 0; sideIdx < LIGHT_CONE_NUM_SIDES; sideIdx++)
-				{
-					indexData[curIdx++] = sliceIdx * LIGHT_CONE_NUM_SIDES + sideIdx;
+				{
+					indexData[curIdx++] = sliceIdx * LIGHT_CONE_NUM_SIDES + sideIdx;
 					indexData[curIdx++] = sliceIdx * LIGHT_CONE_NUM_SIDES + (sideIdx + 1) % LIGHT_CONE_NUM_SIDES;
 					indexData[curIdx++] = (sliceIdx + 1) * LIGHT_CONE_NUM_SIDES + sideIdx;
-
+
 					indexData[curIdx++] = sliceIdx * LIGHT_CONE_NUM_SIDES + (sideIdx + 1) % LIGHT_CONE_NUM_SIDES;
 					indexData[curIdx++] = (sliceIdx + 1) * LIGHT_CONE_NUM_SIDES + (sideIdx + 1) % LIGHT_CONE_NUM_SIDES;
 					indexData[curIdx++] = (sliceIdx + 1) * LIGHT_CONE_NUM_SIDES + sideIdx;
@@ -229,11 +230,11 @@ namespace BansheeEngine
 			for (UINT32 sliceIdx = 0; sliceIdx < (LIGHT_CONE_NUM_SLICES - 1); sliceIdx++)
 			{
 				for (UINT32 sideIdx = 0; sideIdx < LIGHT_CONE_NUM_SIDES; sideIdx++)
-				{
-					indexData[curIdx++] = coneOffset + sliceIdx * LIGHT_CONE_NUM_SIDES + sideIdx;
+				{
+					indexData[curIdx++] = coneOffset + sliceIdx * LIGHT_CONE_NUM_SIDES + sideIdx;
 					indexData[curIdx++] = coneOffset + sliceIdx * LIGHT_CONE_NUM_SIDES + (sideIdx + 1) % LIGHT_CONE_NUM_SIDES;
 					indexData[curIdx++] = coneOffset + (sliceIdx + 1) * LIGHT_CONE_NUM_SIDES + sideIdx;
-
+
 					indexData[curIdx++] = coneOffset + sliceIdx * LIGHT_CONE_NUM_SIDES + (sideIdx + 1) % LIGHT_CONE_NUM_SIDES;
 					indexData[curIdx++] = coneOffset + (sliceIdx + 1) * LIGHT_CONE_NUM_SIDES + (sideIdx + 1) % LIGHT_CONE_NUM_SIDES;
 					indexData[curIdx++] = coneOffset + (sliceIdx + 1) * LIGHT_CONE_NUM_SIDES + sideIdx;

+ 48 - 52
BansheeEngine/Source/BsVirtualInput.cpp

@@ -184,39 +184,37 @@ namespace BansheeEngine
 			mActiveModifiers |= (UINT32)ButtonModifier::Ctrl;
 		else if(event.buttonCode == BC_LMENU || event.buttonCode == BC_RMENU)
 			mActiveModifiers |= (UINT32)ButtonModifier::Alt;
-		else
-		{
-			tempButtons.clear();
-			tempBtnDescs.clear();
 
-			if (mInputConfiguration->_getButtons(event.buttonCode, mActiveModifiers, tempButtons, tempBtnDescs))
-			{
-				while (event.deviceIdx >= (UINT32)mDevices.size())
-					mDevices.push_back(DeviceData());
+		tempButtons.clear();
+		tempBtnDescs.clear();
 
-				Map<UINT32, ButtonData>& cachedStates = mDevices[event.deviceIdx].cachedStates;
+		if (mInputConfiguration->_getButtons(event.buttonCode, mActiveModifiers, tempButtons, tempBtnDescs))
+		{
+			while (event.deviceIdx >= (UINT32)mDevices.size())
+				mDevices.push_back(DeviceData());
 
-				UINT32 numButtons = (UINT32)tempButtons.size();
-				for (UINT32 i = 0; i < numButtons; i++)
-				{
-					const VirtualButton& btn = tempButtons[i];
-					const VIRTUAL_BUTTON_DESC& btnDesc = tempBtnDescs[i];
+			Map<UINT32, ButtonData>& cachedStates = mDevices[event.deviceIdx].cachedStates;
 
-					ButtonData& data = cachedStates[btn.buttonIdentifier];
+			UINT32 numButtons = (UINT32)tempButtons.size();
+			for (UINT32 i = 0; i < numButtons; i++)
+			{
+				const VirtualButton& btn = tempButtons[i];
+				const VIRTUAL_BUTTON_DESC& btnDesc = tempBtnDescs[i];
 
-					data.button = btn;
-					data.state = ButtonState::ToggledOn;
-					data.timestamp = event.timestamp;
-					data.updateFrameIdx = gTime().getFrameIdx();
-					data.allowRepeat = btnDesc.repeatable;
+				ButtonData& data = cachedStates[btn.buttonIdentifier];
 
-					VirtualButtonEvent virtualEvent;
-					virtualEvent.button = btn;
-					virtualEvent.state = ButtonState::On;
-					virtualEvent.deviceIdx = event.deviceIdx;
+				data.button = btn;
+				data.state = ButtonState::ToggledOn;
+				data.timestamp = event.timestamp;
+				data.updateFrameIdx = gTime().getFrameIdx();
+				data.allowRepeat = btnDesc.repeatable;
 
-					mEvents.push(virtualEvent);
-				}
+				VirtualButtonEvent virtualEvent;
+				virtualEvent.button = btn;
+				virtualEvent.state = ButtonState::On;
+				virtualEvent.deviceIdx = event.deviceIdx;
+
+				mEvents.push(virtualEvent);
 			}
 		}
 	}
@@ -229,39 +227,37 @@ namespace BansheeEngine
 			mActiveModifiers &= ~(UINT32)ButtonModifier::Ctrl;
 		else if(event.buttonCode == BC_LMENU || event.buttonCode == BC_RMENU)
 			mActiveModifiers &= ~(UINT32)ButtonModifier::Alt;
-		else
-		{
-			tempButtons.clear();
-			tempBtnDescs.clear();
 
-			if (mInputConfiguration->_getButtons(event.buttonCode, mActiveModifiers, tempButtons, tempBtnDescs))
-			{
-				while (event.deviceIdx >= (UINT32)mDevices.size())
-					mDevices.push_back(DeviceData());
+		tempButtons.clear();
+		tempBtnDescs.clear();
 
-				Map<UINT32, ButtonData>& cachedStates = mDevices[event.deviceIdx].cachedStates;
+		if (mInputConfiguration->_getButtons(event.buttonCode, mActiveModifiers, tempButtons, tempBtnDescs))
+		{
+			while (event.deviceIdx >= (UINT32)mDevices.size())
+				mDevices.push_back(DeviceData());
 
-				UINT32 numButtons = (UINT32)tempButtons.size();
-				for (UINT32 i = 0; i < numButtons; i++)
-				{
-					const VirtualButton& btn = tempButtons[i];
-					const VIRTUAL_BUTTON_DESC& btnDesc = tempBtnDescs[i];
+			Map<UINT32, ButtonData>& cachedStates = mDevices[event.deviceIdx].cachedStates;
 
-					ButtonData& data = cachedStates[btn.buttonIdentifier];
+			UINT32 numButtons = (UINT32)tempButtons.size();
+			for (UINT32 i = 0; i < numButtons; i++)
+			{
+				const VirtualButton& btn = tempButtons[i];
+				const VIRTUAL_BUTTON_DESC& btnDesc = tempBtnDescs[i];
 
-					data.button = btn;
-					data.state = ButtonState::ToggledOff;
-					data.timestamp = event.timestamp;
-					data.updateFrameIdx = gTime().getFrameIdx();
-					data.allowRepeat = btnDesc.repeatable;
+				ButtonData& data = cachedStates[btn.buttonIdentifier];
 
-					VirtualButtonEvent virtualEvent;
-					virtualEvent.button = btn;
-					virtualEvent.state = ButtonState::Off;
-					virtualEvent.deviceIdx = event.deviceIdx;
+				data.button = btn;
+				data.state = ButtonState::ToggledOff;
+				data.timestamp = event.timestamp;
+				data.updateFrameIdx = gTime().getFrameIdx();
+				data.allowRepeat = btnDesc.repeatable;
 
-					mEvents.push(virtualEvent);
-				}
+				VirtualButtonEvent virtualEvent;
+				virtualEvent.button = btn;
+				virtualEvent.state = ButtonState::Off;
+				virtualEvent.deviceIdx = event.deviceIdx;
+
+				mEvents.push(virtualEvent);
 			}
 		}
 	}

+ 1 - 0
MBansheeEditor/MBansheeEditor.csproj

@@ -131,6 +131,7 @@
     <Compile Include="ProjectSettings.cs" />
     <Compile Include="Library\LibraryWindow.cs" />
     <Compile Include="ProjectWindow.cs" />
+    <Compile Include="Scene\SceneAxesHandle.cs" />
     <Compile Include="Scene\SceneCamera.cs" />
     <Compile Include="Scene\SceneViewHandler.cs" />
     <Compile Include="Scene\SceneWindow.cs" />

+ 29 - 25
MBansheeEditor/Scene/CustomHandle.cs

@@ -1,25 +1,29 @@
-using System;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Attribute that can be added to types deriving from <see cref="BansheeEditor.Handle"/>. That handle implementation
-    /// will then be used whenever a handle for the type specified in this attribute needs to be displayed.
-    /// </summary>
-    [AttributeUsage(AttributeTargets.Class)]
-    public sealed class CustomHandle : Attribute
-    {
-        private Type type;
-
-        /// <summary>
-        /// Creates a new custom handle attribute.
-        /// </summary>
-        /// <param name="type">Type deriving from <see cref="Component"/> for which the custom handle will be displayed.
-        ///                    </param>
-        public CustomHandle(Type type)
-        {
-            this.type = type;
-        }
-    }
-}
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Attribute that can be added to types deriving from <see cref="BansheeEditor.Handle"/>. That handle implementation
+    /// will then be used whenever a handle for the type specified in this attribute needs to be displayed.
+    /// 
+    /// Implementation must contain a constructor accepting the object for the type the handle is shown for, otherwise
+    /// it will not be recognized by the system. If handle is not shown for any specific type, the constructor should not
+    /// accept any parameters.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class)]
+    public sealed class CustomHandle : Attribute
+    {
+        private Type type;
+
+        /// <summary>
+        /// Creates a new custom handle attribute.
+        /// </summary>
+        /// <param name="type">Type deriving from <see cref="Component"/> for which the custom handle will be displayed.
+        ///                    Type can be null in which case the handle is always displayed.</param>
+        public CustomHandle(Type type)
+        {
+            this.type = type;
+        }
+    }
+}

+ 170 - 0
MBansheeEditor/Scene/SceneAxesHandle.cs

@@ -0,0 +1,170 @@
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Draws axes that display the orientation of the scene camera. Also allows you to change camera orientation along
+    /// one of the axes, or change projection modes.
+    /// </summary>
+    [CustomHandle(null)]
+    public class SceneAxesHandle : Handle
+    {
+        private const float CONE_HEIGHT = 0.25f;
+        private const float CONE_RADIUS = 0.175f;
+
+        private HandleSliderLine xAxis;
+        private HandleSliderLine yAxis;
+        private HandleSliderLine zAxis;
+        private HandleSliderPlane projTypePlane;
+
+        private bool[] clickStates = new bool[4];
+
+        private Vector3 position = Vector3.Zero;
+        private Quaternion rotation = Quaternion.Identity;
+
+        /// <summary>
+        /// Creates a new scene axes handle.
+        /// </summary>
+        public SceneAxesHandle()
+        {
+            xAxis = new HandleSliderLine(this, Vector3.XAxis, 1.0f);
+            yAxis = new HandleSliderLine(this, Vector3.YAxis, 1.0f);
+            zAxis = new HandleSliderLine(this, Vector3.ZAxis, 1.0f);
+
+            projTypePlane = new HandleSliderPlane(this, Vector3.XAxis, Vector3.YAxis, 0.4f);
+        }
+
+        /// <inheritdoc/>
+        protected internal override void PreInput()
+        {
+            Camera cam = EditorApplication.SceneViewCamera;
+            if (cam == null)
+                return;
+
+            float distFromCamera = 500.0f;
+            float x = cam.GetFrustumWidth(distFromCamera) * 0.5f;
+            float y = x / cam.AspectRatio;
+
+            Vector3 localPosition = new Vector3(0, 0, -distFromCamera);
+            float appoxHandleSize = EditorSettings.DefaultHandleSize * distFromCamera;
+            localPosition.x = x - appoxHandleSize * 1.2f;
+            localPosition.y = y - appoxHandleSize * 1.2f;
+
+            position = cam.SceneObject.WorldTransform.MultiplyAffine(localPosition);
+            rotation = Quaternion.Identity;
+
+            xAxis.Position = position;
+            yAxis.Position = position;
+            zAxis.Position = position;
+
+            xAxis.Rotation = rotation;
+            yAxis.Rotation = rotation;
+            zAxis.Rotation = rotation;
+
+            float handleSize = Handles.GetHandleSize(EditorApplication.SceneViewCamera, position);
+            Vector3 freeAxisOffset = (Vector3.XAxis * -0.2f + Vector3.YAxis * -0.2f) * handleSize;
+            projTypePlane.Rotation = EditorApplication.SceneViewCamera.SceneObject.Rotation;
+            projTypePlane.Position = position + projTypePlane.Rotation.Rotate(freeAxisOffset);
+        }
+
+        /// <inheritdoc/>
+        protected internal override void PostInput()
+        {
+            var axes = new []
+            {
+                new Tuple<HandleSlider, Action>(xAxis, () => RotateCameraTo(Vector3.XAxis)),
+                new Tuple<HandleSlider, Action>(yAxis, () => RotateCameraTo(Vector3.YAxis)),
+                new Tuple<HandleSlider, Action>(zAxis, () => RotateCameraTo(Vector3.ZAxis)),
+                new Tuple<HandleSlider, Action>(projTypePlane, ToggleProjectionType)
+            };
+
+            for (int i = 0; i < axes.Length; i++)
+            {
+                if (axes[i].Item1.State == HandleSlider.StateType.Active)
+                {
+                    if (!clickStates[i])
+                    {
+                        axes[i].Item2();
+                        clickStates[i] = true;
+                    }
+                }
+                else
+                {
+                    clickStates[i] = false;
+                }
+            }
+        }
+
+        /// <inheritdoc/>
+        protected internal override void Draw()
+        {
+            HandleDrawing.Transform = Matrix4.TRS(position, rotation, Vector3.One);
+            Vector3 cameraForward = EditorApplication.SceneViewCamera.SceneObject.Forward;
+            float handleSize = Handles.GetHandleSize(EditorApplication.SceneViewCamera, position);
+
+            // Draw 1D arrows
+            Color xColor = Color.Red;
+            if (xAxis.State == HandleSlider.StateType.Active)
+                xColor = Color.White;
+            else if (xAxis.State == HandleSlider.StateType.Hover)
+                xColor = Color.BansheeOrange;
+
+            xColor.a = MathEx.Lerp(1.0f, 0.0f, MathEx.Abs(Vector3.Dot(cameraForward, Vector3.XAxis)), 0.8f, 1.0f);
+            HandleDrawing.Color = xColor;
+
+            Vector3 xConeStart = Vector3.XAxis * (1.0f - CONE_HEIGHT);
+            HandleDrawing.DrawLine(Vector3.Zero, xConeStart, handleSize);
+            HandleDrawing.DrawCone(xConeStart, Vector3.XAxis, CONE_HEIGHT, CONE_RADIUS, handleSize);
+
+            Color yColor = Color.Green;
+            if (yAxis.State == HandleSlider.StateType.Active)
+                yColor = Color.White;
+            else if (yAxis.State == HandleSlider.StateType.Hover)
+                yColor = Color.BansheeOrange;
+
+            yColor.a = MathEx.Lerp(1.0f, 0.0f, MathEx.Abs(Vector3.Dot(cameraForward, Vector3.YAxis)), 0.8f, 1.0f);
+            HandleDrawing.Color = yColor;
+
+            Vector3 yConeStart = Vector3.YAxis * (1.0f - CONE_HEIGHT);
+            HandleDrawing.DrawLine(Vector3.Zero, yConeStart, handleSize);
+            HandleDrawing.DrawCone(yConeStart, Vector3.YAxis, CONE_HEIGHT, CONE_RADIUS, handleSize);
+
+            Color zColor = Color.Blue;
+            if (zAxis.State == HandleSlider.StateType.Active)
+                zColor = Color.White;
+            else if (zAxis.State == HandleSlider.StateType.Hover)
+                zColor = Color.BansheeOrange;
+
+            zColor.a = MathEx.Lerp(1.0f, 0.0f, MathEx.Abs(Vector3.Dot(cameraForward, Vector3.ZAxis)), 0.8f, 1.0f);
+            HandleDrawing.Color = zColor;
+
+            Vector3 zConeStart = Vector3.ZAxis * (1.0f - CONE_HEIGHT);
+            HandleDrawing.DrawLine(Vector3.Zero, zConeStart, handleSize);
+            HandleDrawing.DrawCone(zConeStart, Vector3.ZAxis, CONE_HEIGHT, CONE_RADIUS, handleSize);
+
+            // Draw projection type handle
+            if (projTypePlane.State == HandleSlider.StateType.Active)
+                HandleDrawing.Color = Color.White;
+            else if (projTypePlane.State == HandleSlider.StateType.Hover)
+                HandleDrawing.Color = Color.BansheeOrange;
+            else
+                HandleDrawing.Color = Color.White;
+
+            HandleDrawing.DrawCube(Vector3.Zero, new Vector3(0.2f, 0.2f, 0.2f), handleSize);
+
+            // TODO - Hide the axis pointing straight to/from the camera
+            // TODO - Add a text notifying the user whether ortho/proj is active
+        }
+
+        private void RotateCameraTo(Vector3 axis)
+        {
+            // TODO - Rotate to the provided axis. If already looking at that axis, rotate in the opposite direction (-axis)
+        }
+
+        private void ToggleProjectionType()
+        {
+            // TODO - Switch between ortographic and perspective
+        }
+    }
+}

+ 2 - 2
MBansheeEditor/Scene/SceneCamera.cs

@@ -167,7 +167,7 @@ namespace BansheeEditor
         /// </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)
+        private void FrameBounds(AABox bounds, float padding = 0.0f)
         {
             // TODO - Use AABox bounds directly instead of a sphere to be more accurate
             float worldWidth = bounds.Size.Length;
@@ -185,7 +185,7 @@ namespace BansheeEditor
 
             // 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;
+                frustumWidth = worldHeight * camera.AspectRatio * paddingScale;
             else // Otherwise width
                 frustumWidth = worldWidth * paddingScale;
 

+ 0 - 1
MBansheeEditor/Scene/SceneWindow.cs

@@ -415,7 +415,6 @@ namespace BansheeEditor
                             bool ctrlHeld = Input.IsButtonHeld(ButtonCode.LeftControl) ||
                                             Input.IsButtonHeld(ButtonCode.RightControl);
 
-                            Debug.Log("PICK");
                             sceneViewHandler.PickObject(scenePos, ctrlHeld);
                         }
                     }

+ 425 - 412
MBansheeEngine/Camera.cs

@@ -1,412 +1,425 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Text;
-
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Camera component that determines how is world geometry projected onto a 2D surface. You may position and orient it 
-    /// in space, set options like aspect ratio and field or view and it outputs view and projection matrices required for 
-    /// rendering.
-    /// </summary>
-    [RunInEditor]
-    public sealed class Camera : Component
-    {
-        private NativeCamera native;
-
-        [SerializeField]
-        internal SerializableData serializableData = new SerializableData();
-
-        /// <summary>
-        /// Returns the non-component version of Camera that is wrapped by this component. 
-        /// </summary>
-        internal NativeCamera Native
-        {
-            get { return native; }
-        }
-
-        /// <summary>
-        /// Ratio between viewport width and height (width / height).
-        /// </summary>
-        public float AspectRatio
-        {
-            get { return native.aspectRatio; }
-            set { native.aspectRatio = value; serializableData.aspectRatio = value; }
-        }
-
-        /// <summary>
-        /// Distance from the frustum origin to the near clipping plane. Anything closer than the near clipping plane will 
-        /// not be rendered. Decreasing this value decreases depth buffer precision.
-        /// </summary>
-        public float NearClipPlane
-        {
-            get { return native.nearClipPlane; }
-            set { native.nearClipPlane = value; serializableData.nearClipPlane = value; }
-        }
-
-        /// <summary>
-        /// Distance from the frustum origin to the far clipping plane. Anything farther than the far clipping plane will 
-        /// not be rendered. Increasing this value decreases depth buffer precision.
-        /// </summary>
-        public float FarClipPlane
-        {
-            get { return native.farClipPlane; }
-            set { native.farClipPlane = value; serializableData.farClipPlane = value; }
-        }
-
-        /// <summary>
-        /// Horizontal field of view. This determines how wide the camera viewing angle is along the horizontal axis. 
-        /// Vertical FOV is calculated from the horizontal FOV and the aspect ratio.
-        /// </summary>
-        public Degree FieldOfView
-        {
-            get { return native.fieldOfView; }
-            set { native.fieldOfView = value; serializableData.fieldOfView = value; }
-        }
-
-        /// <summary>
-        /// Returns the area of the render target that the camera renders to, in normalized coordinates. 
-        /// </summary>
-        public Rect2 ViewportRect
-        {
-            get { return native.viewportRect; }
-            set { native.viewportRect = value; serializableData.viewportRect = value; }
-        }
-
-        /// <summary>
-        /// Projection type that controls how is 3D geometry projected onto a 2D plane.
-        /// </summary>
-        public ProjectionType ProjectionType
-        {
-            get { return native.projectionType; }
-            set { native.projectionType = value; serializableData.projectionType = value; }
-        }
-
-        /// <summary>
-        /// Ortographic height that controls the size of the displayed objects. This value is only relevant when projection
-        /// type is set to orthographic. Setting this value will automatically recalculate ortographic width depending on
-        /// the aspect ratio.
-        /// </summary>
-        public float OrthoHeight
-        {
-            get { return native.orthoHeight; }
-            set { native.orthoHeight = value; serializableData.orthoHeight = value; }
-        }
-
-        /// <summary>
-        /// Returns the ortographic width that controls the size of the displayed object. To change this value modify
-        /// <see cref="OrthoHeight"/> or <see cref="AspectRatio"/>.
-        /// </summary>
-        public float OrthoWidth
-        {
-            get { return native.orthoWidth; }
-        }
-
-        /// <summary>
-        /// Color that will be used for clearing the camera's viewport before rendering. Only relevant if color clear is
-        /// enabled.
-        /// </summary>
-        public Color ClearColor
-        {
-            get { return native.clearColor; }
-            set { native.clearColor = value; serializableData.clearColor = value; }
-        }
-
-        /// <summary>
-        /// Value that will be used for clearing the camera's depth buffer before rendering. Only relevant if depth clear
-        /// is enabled.
-        /// </summary>
-        public float ClearDepth
-        {
-            get { return native.clearDepth; }
-            set { native.clearDepth = value; serializableData.clearDepth = value; }
-        }
-
-        /// <summary>
-        /// Value that will be used for clearing the camera's stencil buffer before rendering. Only relevant if stencil
-        /// clear is enabled.
-        /// </summary>
-        public UInt16 ClearStencil
-        {
-            get { return native.clearStencil; }
-            set { native.clearStencil = value; serializableData.clearStencil = value; }
-        }
-
-        /// <summary>
-        /// Flags that control which portions of the camera viewport, if any, are cleared before rendering.
-        /// </summary>
-        public ClearFlags ClearFlags
-        {
-            get { return native.clearFlags; }
-            set { native.clearFlags = value; serializableData.clearFlags = value; }
-        }
-
-        /// <summary>
-        /// Determines in which orders are the cameras rendered. This only applies to cameras rendering to the same render 
-        /// target. Higher value means the camera will be processed sooner.
-        /// </summary>
-        public int Priority
-        {
-            get { return native.priority; }
-            set { native.priority = value; serializableData.priority = value; }
-        }
-
-        /// <summary>
-        /// Sets layer bitfield that is used when determining which object should the camera render. Renderable objects
-        /// have their own layer flags that can be set depending on which camera you want to render them in.
-        /// </summary>
-        public UInt64 Layers
-        {
-            get { return native.layers; }
-            set { native.layers = value; serializableData.layers = value; }
-        }
-
-        /// <summary>
-        /// Returns the standard projection matrix that determines how are 3D points projected to two dimensions. The layout
-        /// of this matrix depends on currently used render system.
-        /// </summary>
-        public Matrix4 ProjMatrix
-        {
-            get { return native.projMatrix; }
-        }
-
-        /// <summary>
-        /// Returns the inverse of the standard projection matrix that determines how are 3D points projected to two 
-        /// dimensions. The layout of this matrix depends on currently used render system.
-        /// </summary>
-        public Matrix4 ProjMatrixInverse
-        {
-            get { return native.projMatrixInv; }
-        }
-
-        /// <summary>
-        /// Returns the view matrix that controls camera position and orientation.
-        /// </summary>
-        public Matrix4 ViewMatrix
-        {
-            get { return native.viewMatrix; }
-        }
-
-        /// <summary>
-        /// Returns the inverse of the view matrix that controls camera position and orientation.
-        /// </summary>
-        public Matrix4 ViewMatrixInverse
-        {
-            get { return native.viewMatrixInv; }
-        }
-
-        /// <summary>
-        /// Returns the width of the camera's viewport, in pixels. Only valid if render target is currently set.
-        /// </summary>
-        public int WidthPixels
-        {
-            get { return native.widthPixels; }
-        }
-
-        /// <summary>
-        /// Returns the height of the camera's viewport, in pixels. Only valid if render target is currently set.
-        /// </summary>
-        public int HeightPixels
-        {
-            get { return native.heightPixels; }
-        }
-
-        /// <summary>
-        /// Returns the render target that the camera will output all rendered pixels to.
-        /// </summary>
-        public RenderTarget Target
-        {
-            get { return native.target; }
-            set { native.target = value; }
-        }
-
-        /// <summary>
-        /// Determines if this is the main application camera. Main camera controls the final render surface that is 
-        /// displayed to the user.
-        /// </summary>
-        public bool Main
-        {
-            get { return native.main; }
-            set { native.main = value; serializableData.main = value; }
-        }
-
-        /// <summary>
-        /// Converts a point in world space to screen coordinates.
-        /// </summary>
-        /// <param name="value">3D point in world space.</param>
-        /// <returns>2D point on the render target attached to the camera, in pixels.</returns>
-        public Vector2I WorldToScreen(Vector3 value) { return native.WorldToScreen(value); }
-
-        /// <summary>
-        /// Converts a point in world space to clip space coordinates.
-        /// </summary>
-        /// <param name="value">3D point in world space.</param>
-        /// <returns>2D point in normalized coordinates ([0, 1] range), relative to the camera's viewport.</returns>
-        public Vector2 WorldToClip(Vector3 value) { return native.WorldToClip(value); }
-
-        /// <summary>
-        /// Converts a point in world space to view space coordinates.
-        /// </summary>
-        /// <param name="value">3D point in world space.</param>
-        /// <returns>3D point relative to the camera's coordinate system.</returns>
-        public Vector3 WorldToView(Vector3 value) { return native.WorldToView(value); }
-
-        /// <summary>
-        /// Converts a point in screen space to a point in world space.
-        /// </summary>
-        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
-        /// <param name="depth">Depth at which place the world point at. The depth is applied to the vector going from 
-        ///                     camera origin to the point on the near plane.</param>
-        /// <returns>3D point in world space.</returns>
-        public Vector3 ScreenToWorld(Vector2I value, float depth = 0.5f) { return native.ScreenToWorld(value, depth); }
-
-        /// <summary>
-        /// Converts a point in screen space to a point in view space.
-        /// </summary>
-        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
-        /// <param name="depth">Depth at which place the view point at. The depth is applied to the vector going from 
-        ///                     camera origin to the point on the near plane.</param>
-        /// <returns>3D point in view space.</returns>
-        public Vector3 ScreenToView(Vector2I value, float depth = 0.5f) { return native.ScreenToView(value, depth); }
-
-        /// <summary>
-        /// Converts a point in screen space to a point in normalized clip space.
-        /// </summary>
-        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
-        /// <returns>2D point in normalized cliped space ([0, 1] range), relative to the camera's viewport.</returns>
-        public Vector2 ScreenToClip(Vector2I value) { return native.ScreenToClip(value); }
-
-        /// <summary>
-        /// Converts a point relative to camera's coordinate system (view space) into a point in world space.
-        /// </summary>
-        /// <param name="value">3D point in view space.</param>
-        /// <returns>3D point in world space.</returns>
-        public Vector3 ViewToWorld(Vector3 value) { return native.ViewToWorld(value); }
-
-        /// <summary>
-        /// Converts a point relative to camera's coordinate system (view space) to screen coordinates.
-        /// </summary>
-        /// <param name="value">3D point in view space.</param>
-        /// <returns>2D point on the render target attached to the camera, in pixels.</returns>
-        public Vector2I ViewToScreen(Vector3 value) { return native.ViewToScreen(value); }
-
-        /// <summary>
-        /// Converts a point relative to camera's coordinate system (view space) to normalized clip space.
-        /// </summary>
-        /// <param name="value">3D point in view space.</param>
-        /// <returns>2D point in normalized cliped space ([0, 1] range), relative to the camera's viewport.</returns>
-        public Vector2 ViewToClip(Vector3 value) { return native.ViewToClip(value); }
-
-        /// <summary>
-        /// Converts a point relative to camera's viewport in normalized clip space ([0, 1] range) into a point in 
-        /// world space.
-        /// </summary>
-        /// <param name="value">2D point in normalized clip space.</param>
-        /// <param name="depth">Depth at which place the world point at. The depth is applied to the vector going from
-        ///                     camera origin to the point on the near plane.</param>
-        /// <returns>3D point in world space.</returns>
-        public Vector3 ClipToWorld(Vector2 value, float depth = 0.5f) { return native.ClipToWorld(value, depth); }
-
-        /// <summary>
-        /// Converts a point relative to camera's viewport in normalized clip space ([0, 1] range) into a point in 
-        /// view space.
-        /// </summary>
-        /// <param name="value">2D point in normalized clip space.</param>
-        /// <param name="depth">Depth at which place the world point at. The depth is applied to the vector going from
-        ///                     camera origin to the point on the near plane.</param>
-        /// <returns>3D point in view space.</returns>
-        public Vector3 ClipToView(Vector2 value, float depth = 0.5f) { return native.ClipToView(value, depth); }
-
-        /// <summary>
-        /// Converts a point relative to camera's viewport in normalized clip space ([0, 1] range) to screen space.
-        /// </summary>
-        /// <param name="value">2D point in normalized clip space.</param>
-        /// <returns>2D point on the render target attached to the camera, in pixels.</returns>
-        public Vector2I ClipToScreen(Vector2 value) { return native.ClipToScreen(value); }
-
-        /// <summary>
-        /// Converts a point in screen space in a ray in world space.
-        /// </summary>
-        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
-        /// <returns>A ray in world space with it's origin the selected point at the near frustum plane, pointing in the 
-        ///          direction going from camera's origin towards a point on the near frustum plane.</returns>
-        public Ray ScreenToWorldRay(Vector2I value) { return native.ScreenToWorldRay(value); }
-
-        /// <summary>
-        /// Projects a point in view space to a point in clip space. Similar to <see cref="ViewToClip"/> but preserves
-        /// the depth component.
-        /// </summary>
-        /// <param name="value">3D point in view space.</param>
-        /// <returns>3D point in normalized cliped space ([0, 1] range), relative to the camera's viewport. Z value
-        ///          range depends on active render API.</returns>
-        public Vector3 ProjectPoint(Vector3 value) { return native.ProjectPoint(value); }
-
-        /// <summary>
-        /// Un-rpojects a point in clip space to a point in view space.
-        /// </summary>
-        /// <param name="value">3D point in normalized cliped space ([0, 1] range), relative to the camera's viewport. 
-        ///                     Z value range depends on active render API.</param>
-        /// <returns>3D point in view space.</returns>
-        public Vector3 UnprojectPoint(Vector3 value) { return native.UnprojectPoint(value); }
-
-        private void OnReset()
-        {
-            if (native != null)
-                native.OnDestroy();
-
-            native = new NativeCamera(SceneObject);
-
-            // Restore saved values after reset
-            native.aspectRatio = serializableData.aspectRatio;
-            native.nearClipPlane = serializableData.nearClipPlane;
-            native.farClipPlane = serializableData.farClipPlane;
-            native.fieldOfView = serializableData.fieldOfView;
-            native.viewportRect = serializableData.viewportRect;
-            native.projectionType = serializableData.projectionType;
-            native.orthoHeight = serializableData.orthoHeight;
-            native.clearColor = serializableData.clearColor;
-            native.clearDepth = serializableData.clearDepth;
-            native.clearStencil = serializableData.clearStencil;
-            native.clearFlags = serializableData.clearFlags;
-            native.priority = serializableData.priority;
-            native.layers = serializableData.layers;
-            native.main = serializableData.main;
-
-            // TODO - Make RenderTexture a resource so I can save/restore it?
-        }
-
-        private void OnUpdate()
-        {
-            native.UpdateView(SceneObject);
-        }
-
-        private void OnDestroy()
-        {
-            native.OnDestroy();
-        }
-
-        /// <summary>
-        /// Holds all data the camera component needs to persist through serialization.
-        /// </summary>
-        [SerializeObject]
-        internal class SerializableData
-        {
-            public float aspectRatio = 1.333f;
-            public float nearClipPlane = 1.0f;
-            public float farClipPlane = 1000.0f;
-            public Degree fieldOfView = new Degree(60);
-            public Rect2 viewportRect = new Rect2(0, 0, 1, 1);
-            public ProjectionType projectionType = ProjectionType.Perspective;
-            public float orthoHeight;
-            public Color clearColor = new Color(143.0f / 255.0f, 111.0f / 255.0f, 0);
-            public float clearDepth = 1.0f;
-            public UInt16 clearStencil;
-            public ClearFlags clearFlags = ClearFlags.Color | ClearFlags.Depth | ClearFlags.Stencil;
-            public int priority;
-            public UInt64 layers = 0xFFFFFFFFFFFFFFFF;
-            public bool main;
-        }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Camera component that determines how is world geometry projected onto a 2D surface. You may position and orient it 
+    /// in space, set options like aspect ratio and field or view and it outputs view and projection matrices required for 
+    /// rendering.
+    /// </summary>
+    [RunInEditor]
+    public sealed class Camera : Component
+    {
+        private NativeCamera native;
+
+        [SerializeField]
+        internal SerializableData serializableData = new SerializableData();
+
+        /// <summary>
+        /// Returns the non-component version of Camera that is wrapped by this component. 
+        /// </summary>
+        internal NativeCamera Native
+        {
+            get { return native; }
+        }
+
+        /// <summary>
+        /// Ratio between viewport width and height (width / height).
+        /// </summary>
+        public float AspectRatio
+        {
+            get { return native.aspectRatio; }
+            set { native.aspectRatio = value; serializableData.aspectRatio = value; }
+        }
+
+        /// <summary>
+        /// Distance from the frustum origin to the near clipping plane. Anything closer than the near clipping plane will 
+        /// not be rendered. Decreasing this value decreases depth buffer precision.
+        /// </summary>
+        public float NearClipPlane
+        {
+            get { return native.nearClipPlane; }
+            set { native.nearClipPlane = value; serializableData.nearClipPlane = value; }
+        }
+
+        /// <summary>
+        /// Distance from the frustum origin to the far clipping plane. Anything farther than the far clipping plane will 
+        /// not be rendered. Increasing this value decreases depth buffer precision.
+        /// </summary>
+        public float FarClipPlane
+        {
+            get { return native.farClipPlane; }
+            set { native.farClipPlane = value; serializableData.farClipPlane = value; }
+        }
+
+        /// <summary>
+        /// Horizontal field of view. This determines how wide the camera viewing angle is along the horizontal axis. 
+        /// Vertical FOV is calculated from the horizontal FOV and the aspect ratio.
+        /// </summary>
+        public Degree FieldOfView
+        {
+            get { return native.fieldOfView; }
+            set { native.fieldOfView = value; serializableData.fieldOfView = value; }
+        }
+
+        /// <summary>
+        /// Returns the area of the render target that the camera renders to, in normalized coordinates. 
+        /// </summary>
+        public Rect2 ViewportRect
+        {
+            get { return native.viewportRect; }
+            set { native.viewportRect = value; serializableData.viewportRect = value; }
+        }
+
+        /// <summary>
+        /// Projection type that controls how is 3D geometry projected onto a 2D plane.
+        /// </summary>
+        public ProjectionType ProjectionType
+        {
+            get { return native.projectionType; }
+            set { native.projectionType = value; serializableData.projectionType = value; }
+        }
+
+        /// <summary>
+        /// Ortographic height that controls the size of the displayed objects. This value is only relevant when projection
+        /// type is set to orthographic. Setting this value will automatically recalculate ortographic width depending on
+        /// the aspect ratio.
+        /// </summary>
+        public float OrthoHeight
+        {
+            get { return native.orthoHeight; }
+            set { native.orthoHeight = value; serializableData.orthoHeight = value; }
+        }
+
+        /// <summary>
+        /// Returns the ortographic width that controls the size of the displayed object. To change this value modify
+        /// <see cref="OrthoHeight"/> or <see cref="AspectRatio"/>.
+        /// </summary>
+        public float OrthoWidth
+        {
+            get { return native.orthoWidth; }
+        }
+
+        /// <summary>
+        /// Color that will be used for clearing the camera's viewport before rendering. Only relevant if color clear is
+        /// enabled.
+        /// </summary>
+        public Color ClearColor
+        {
+            get { return native.clearColor; }
+            set { native.clearColor = value; serializableData.clearColor = value; }
+        }
+
+        /// <summary>
+        /// Value that will be used for clearing the camera's depth buffer before rendering. Only relevant if depth clear
+        /// is enabled.
+        /// </summary>
+        public float ClearDepth
+        {
+            get { return native.clearDepth; }
+            set { native.clearDepth = value; serializableData.clearDepth = value; }
+        }
+
+        /// <summary>
+        /// Value that will be used for clearing the camera's stencil buffer before rendering. Only relevant if stencil
+        /// clear is enabled.
+        /// </summary>
+        public UInt16 ClearStencil
+        {
+            get { return native.clearStencil; }
+            set { native.clearStencil = value; serializableData.clearStencil = value; }
+        }
+
+        /// <summary>
+        /// Flags that control which portions of the camera viewport, if any, are cleared before rendering.
+        /// </summary>
+        public ClearFlags ClearFlags
+        {
+            get { return native.clearFlags; }
+            set { native.clearFlags = value; serializableData.clearFlags = value; }
+        }
+
+        /// <summary>
+        /// Determines in which orders are the cameras rendered. This only applies to cameras rendering to the same render 
+        /// target. Higher value means the camera will be processed sooner.
+        /// </summary>
+        public int Priority
+        {
+            get { return native.priority; }
+            set { native.priority = value; serializableData.priority = value; }
+        }
+
+        /// <summary>
+        /// Sets layer bitfield that is used when determining which object should the camera render. Renderable objects
+        /// have their own layer flags that can be set depending on which camera you want to render them in.
+        /// </summary>
+        public UInt64 Layers
+        {
+            get { return native.layers; }
+            set { native.layers = value; serializableData.layers = value; }
+        }
+
+        /// <summary>
+        /// Returns the standard projection matrix that determines how are 3D points projected to two dimensions. The layout
+        /// of this matrix depends on currently used render system.
+        /// </summary>
+        public Matrix4 ProjMatrix
+        {
+            get { return native.projMatrix; }
+        }
+
+        /// <summary>
+        /// Returns the inverse of the standard projection matrix that determines how are 3D points projected to two 
+        /// dimensions. The layout of this matrix depends on currently used render system.
+        /// </summary>
+        public Matrix4 ProjMatrixInverse
+        {
+            get { return native.projMatrixInv; }
+        }
+
+        /// <summary>
+        /// Returns the view matrix that controls camera position and orientation.
+        /// </summary>
+        public Matrix4 ViewMatrix
+        {
+            get { return native.viewMatrix; }
+        }
+
+        /// <summary>
+        /// Returns the inverse of the view matrix that controls camera position and orientation.
+        /// </summary>
+        public Matrix4 ViewMatrixInverse
+        {
+            get { return native.viewMatrixInv; }
+        }
+
+        /// <summary>
+        /// Returns the width of the camera's viewport, in pixels. Only valid if render target is currently set.
+        /// </summary>
+        public int WidthPixels
+        {
+            get { return native.widthPixels; }
+        }
+
+        /// <summary>
+        /// Returns the height of the camera's viewport, in pixels. Only valid if render target is currently set.
+        /// </summary>
+        public int HeightPixels
+        {
+            get { return native.heightPixels; }
+        }
+
+        /// <summary>
+        /// Returns the render target that the camera will output all rendered pixels to.
+        /// </summary>
+        public RenderTarget Target
+        {
+            get { return native.target; }
+            set { native.target = value; }
+        }
+
+        /// <summary>
+        /// Determines if this is the main application camera. Main camera controls the final render surface that is 
+        /// displayed to the user.
+        /// </summary>
+        public bool Main
+        {
+            get { return native.main; }
+            set { native.main = value; serializableData.main = value; }
+        }
+
+        /// <summary>
+        /// Converts a point in world space to screen coordinates.
+        /// </summary>
+        /// <param name="value">3D point in world space.</param>
+        /// <returns>2D point on the render target attached to the camera, in pixels.</returns>
+        public Vector2I WorldToScreen(Vector3 value) { return native.WorldToScreen(value); }
+
+        /// <summary>
+        /// Converts a point in world space to clip space coordinates.
+        /// </summary>
+        /// <param name="value">3D point in world space.</param>
+        /// <returns>2D point in normalized coordinates ([0, 1] range), relative to the camera's viewport.</returns>
+        public Vector2 WorldToClip(Vector3 value) { return native.WorldToClip(value); }
+
+        /// <summary>
+        /// Converts a point in world space to view space coordinates.
+        /// </summary>
+        /// <param name="value">3D point in world space.</param>
+        /// <returns>3D point relative to the camera's coordinate system.</returns>
+        public Vector3 WorldToView(Vector3 value) { return native.WorldToView(value); }
+
+        /// <summary>
+        /// Converts a point in screen space to a point in world space.
+        /// </summary>
+        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
+        /// <param name="depth">Depth at which place the world point at. The depth is applied to the vector going from 
+        ///                     camera origin to the point on the near plane.</param>
+        /// <returns>3D point in world space.</returns>
+        public Vector3 ScreenToWorld(Vector2I value, float depth = 0.5f) { return native.ScreenToWorld(value, depth); }
+
+        /// <summary>
+        /// Converts a point in screen space to a point in view space.
+        /// </summary>
+        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
+        /// <param name="depth">Depth at which place the view point at. The depth is applied to the vector going from 
+        ///                     camera origin to the point on the near plane.</param>
+        /// <returns>3D point in view space.</returns>
+        public Vector3 ScreenToView(Vector2I value, float depth = 0.5f) { return native.ScreenToView(value, depth); }
+
+        /// <summary>
+        /// Converts a point in screen space to a point in normalized clip space.
+        /// </summary>
+        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
+        /// <returns>2D point in normalized cliped space ([0, 1] range), relative to the camera's viewport.</returns>
+        public Vector2 ScreenToClip(Vector2I value) { return native.ScreenToClip(value); }
+
+        /// <summary>
+        /// Converts a point relative to camera's coordinate system (view space) into a point in world space.
+        /// </summary>
+        /// <param name="value">3D point in view space.</param>
+        /// <returns>3D point in world space.</returns>
+        public Vector3 ViewToWorld(Vector3 value) { return native.ViewToWorld(value); }
+
+        /// <summary>
+        /// Converts a point relative to camera's coordinate system (view space) to screen coordinates.
+        /// </summary>
+        /// <param name="value">3D point in view space.</param>
+        /// <returns>2D point on the render target attached to the camera, in pixels.</returns>
+        public Vector2I ViewToScreen(Vector3 value) { return native.ViewToScreen(value); }
+
+        /// <summary>
+        /// Converts a point relative to camera's coordinate system (view space) to normalized clip space.
+        /// </summary>
+        /// <param name="value">3D point in view space.</param>
+        /// <returns>2D point in normalized cliped space ([0, 1] range), relative to the camera's viewport.</returns>
+        public Vector2 ViewToClip(Vector3 value) { return native.ViewToClip(value); }
+
+        /// <summary>
+        /// Converts a point relative to camera's viewport in normalized clip space ([0, 1] range) into a point in 
+        /// world space.
+        /// </summary>
+        /// <param name="value">2D point in normalized clip space.</param>
+        /// <param name="depth">Depth at which place the world point at. The depth is applied to the vector going from
+        ///                     camera origin to the point on the near plane.</param>
+        /// <returns>3D point in world space.</returns>
+        public Vector3 ClipToWorld(Vector2 value, float depth = 0.5f) { return native.ClipToWorld(value, depth); }
+
+        /// <summary>
+        /// Converts a point relative to camera's viewport in normalized clip space ([0, 1] range) into a point in 
+        /// view space.
+        /// </summary>
+        /// <param name="value">2D point in normalized clip space.</param>
+        /// <param name="depth">Depth at which place the world point at. The depth is applied to the vector going from
+        ///                     camera origin to the point on the near plane.</param>
+        /// <returns>3D point in view space.</returns>
+        public Vector3 ClipToView(Vector2 value, float depth = 0.5f) { return native.ClipToView(value, depth); }
+
+        /// <summary>
+        /// Converts a point relative to camera's viewport in normalized clip space ([0, 1] range) to screen space.
+        /// </summary>
+        /// <param name="value">2D point in normalized clip space.</param>
+        /// <returns>2D point on the render target attached to the camera, in pixels.</returns>
+        public Vector2I ClipToScreen(Vector2 value) { return native.ClipToScreen(value); }
+
+        /// <summary>
+        /// Converts a point in screen space in a ray in world space.
+        /// </summary>
+        /// <param name="value">2D point on the render target attached to the camera, in pixels.</param>
+        /// <returns>A ray in world space with it's origin the selected point at the near frustum plane, pointing in the 
+        ///          direction going from camera's origin towards a point on the near frustum plane.</returns>
+        public Ray ScreenToWorldRay(Vector2I value) { return native.ScreenToWorldRay(value); }
+
+        /// <summary>
+        /// Projects a point in view space to a point in clip space. Similar to <see cref="ViewToClip"/> but preserves
+        /// the depth component.
+        /// </summary>
+        /// <param name="value">3D point in view space.</param>
+        /// <returns>3D point in normalized cliped space ([0, 1] range), relative to the camera's viewport. Z value
+        ///          range depends on active render API.</returns>
+        public Vector3 ProjectPoint(Vector3 value) { return native.ProjectPoint(value); }
+
+        /// <summary>
+        /// Un-rpojects a point in clip space to a point in view space.
+        /// </summary>
+        /// <param name="value">3D point in normalized cliped space ([0, 1] range), relative to the camera's viewport. 
+        ///                     Z value range depends on active render API.</param>
+        /// <returns>3D point in view space.</returns>
+        public Vector3 UnprojectPoint(Vector3 value) { return native.UnprojectPoint(value); }
+
+        /// <summary>
+        /// Returns the width of the camera's frustum at the specified distance from the camera.
+        /// </summary>
+        /// <param name="distance">Distance along the axis the camera is looking at, in world units.</param>
+        /// <returns>Frustum width, in world units. To find frustum height divide this by camera's aspect ratio. </returns>
+        public float GetFrustumWidth(float distance)
+        {
+            if (ProjectionType == ProjectionType.Perspective)
+                return distance * 2.0f * MathEx.Tan(FieldOfView * 0.5f);
+            else
+                return distance * 0.5f;
+        }
+
+        private void OnReset()
+        {
+            if (native != null)
+                native.OnDestroy();
+
+            native = new NativeCamera(SceneObject);
+
+            // Restore saved values after reset
+            native.aspectRatio = serializableData.aspectRatio;
+            native.nearClipPlane = serializableData.nearClipPlane;
+            native.farClipPlane = serializableData.farClipPlane;
+            native.fieldOfView = serializableData.fieldOfView;
+            native.viewportRect = serializableData.viewportRect;
+            native.projectionType = serializableData.projectionType;
+            native.orthoHeight = serializableData.orthoHeight;
+            native.clearColor = serializableData.clearColor;
+            native.clearDepth = serializableData.clearDepth;
+            native.clearStencil = serializableData.clearStencil;
+            native.clearFlags = serializableData.clearFlags;
+            native.priority = serializableData.priority;
+            native.layers = serializableData.layers;
+            native.main = serializableData.main;
+
+            // TODO - Make RenderTexture a resource so I can save/restore it?
+        }
+
+        private void OnUpdate()
+        {
+            native.UpdateView(SceneObject);
+        }
+
+        private void OnDestroy()
+        {
+            native.OnDestroy();
+        }
+
+        /// <summary>
+        /// Holds all data the camera component needs to persist through serialization.
+        /// </summary>
+        [SerializeObject]
+        internal class SerializableData
+        {
+            public float aspectRatio = 1.333f;
+            public float nearClipPlane = 1.0f;
+            public float farClipPlane = 1000.0f;
+            public Degree fieldOfView = new Degree(90);
+            public Rect2 viewportRect = new Rect2(0, 0, 1, 1);
+            public ProjectionType projectionType = ProjectionType.Perspective;
+            public float orthoHeight;
+            public Color clearColor = new Color(83.0f / 255.0f, 83.0f / 255.0f, 83.0f / 255.0f);
+            public float clearDepth = 1.0f;
+            public UInt16 clearStencil;
+            public ClearFlags clearFlags = ClearFlags.Color | ClearFlags.Depth | ClearFlags.Stencil;
+            public int priority;
+            public UInt64 layers = 0xFFFFFFFFFFFFFFFF;
+            public bool main;
+        }
+    }
+}

+ 16 - 0
MBansheeEngine/Math/MathEx.cs

@@ -542,6 +542,22 @@ namespace BansheeEngine
             return value;
         }
 
+        /// <summary>
+        /// Linearly interpolates between two values.
+        /// </summary>
+        /// <param name="a">Starting value to interpolate from.</param>
+        /// <param name="b">Ending value to interpolate towards.</param>
+        /// <param name="t">Interpolation factor in specified range.</param>
+        /// <param name="tmin">Minimum value for the range of <see cref="t"/></param>
+        /// <param name="tmax">Maximum value for the range of <see cref="t"/></param>
+        /// <returns>Interpolated value.</returns>
+        public static float Lerp(float a, float b, float t, float tmin = 0.0f, float tmax = 1.0f)
+        {
+            t = Clamp01((t - tmin) / (tmax - tmin));
+
+            return a * (1.0f - t) + b * t;
+        }
+
         /// <summary>
         /// Wraps an angle in [0, 360) range. Values lower than zero, or higher or equal to 360
         /// will get wrapped around back into [0, 360) range.

+ 4 - 2
MBansheeEngine/Math/Matrix4.cs

@@ -451,7 +451,8 @@ namespace BansheeEngine
         }
 
         /// <summary>
-        /// Transform a 3D point by this matrix. Matrix must be affine.
+        /// Transform a 3D point by this matrix. Matrix must be affine. Affine multiplication offers better performance 
+        /// than the general case.
         /// </summary>
         /// <param name="p">Point to transform.</param>
         /// <returns>Point transformed by this matrix.</returns>
@@ -477,7 +478,8 @@ namespace BansheeEngine
         }
 
         /// <summary>
-        /// Transform a 4D vector by this matrix. Matrix must be affine.
+        /// Transform a 4D vector by this matrix. Matrix must be affine. Affine multiplication offers better performance 
+        /// than the general case.
         /// </summary>
         /// <param name="v">Vector to transform.</param>
         /// <returns>Vector transformed by this matrix.</returns>

+ 1 - 1
RenderBeast/Source/BsLightRendering.cpp

@@ -47,7 +47,7 @@ namespace BansheeEngine
 		mBuffer.gLightDirection.set(light->getRotation().zAxis());
 
 		Vector4 lightGeometry;
-		lightGeometry.x = LightCore::LIGHT_CONE_NUM_SIDES;
+		lightGeometry.x = light->getType() == LightType::Spot ? LightCore::LIGHT_CONE_NUM_SIDES : 0;
 		lightGeometry.y = LightCore::LIGHT_CONE_NUM_SLICES;
 		lightGeometry.z = light->getBounds().getRadius();
 

+ 145 - 143
SBansheeEditor/Include/BsScriptHandleManager.h

@@ -1,144 +1,146 @@
-#pragma once
-
-#include "BsScriptEditorPrerequisites.h"
-#include "BsHandleManager.h"
-#include <mono/jit/jit.h>
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Renders, updates and manipulates handles declared in managed code.
-	 *			Managed code handles have a [CustomHandle] attribute and must implement
-	 *			BansheeEditor.Handle.
-	 */
-	class BS_SCR_BED_EXPORT ScriptHandleManager : public HandleManager
-	{
-		/**
-		 * @brief	Contains data about a manage type that draws and handles
-		 *			interaction with a custom handle.
-		 */
-		struct CustomHandleData
-		{
-			MonoClass* handleType;
-			MonoClass* componentType;
-			MonoMethod* ctor;
-		};
-
-		/**
-		 * @brief	Data about an active instance of a managed custom handle object.
-		 *			Active handle means its scene object is currently selected and the
-		 *			handle is displayed and can be interacted with.
-		 */
-		struct ActiveCustomHandleData
-		{
-			MonoObject* object;
-			uint32_t gcHandle;
-		};
-
-		/**
-		 * @brief	Data about all active managed custom handle objects
-		 *			for a specific scene object. Active handle means its 
-		 *			scene object is currently selected and the handle is displayed 
-		 *			and can be interacted with.
-		 */
-		struct ActiveCustomHandles
-		{
-			HSceneObject selectedObject;
-			Vector<ActiveCustomHandleData> handles;
-		};
-
-	public:
-		ScriptHandleManager(ScriptAssemblyManager& scriptObjectManager);
-		~ScriptHandleManager();
-
-	protected:
-		/**
-		 * @copydoc	HandleManager::refreshHandles
-		 */
-		void refreshHandles() override;
-
-		/**
-		 * @copydoc	HandleManager::triggerHandles
-		 */
-		void triggerHandles() override;
-
-		/**
-		 * @copydoc	HandleManager::queueDrawCommands
-		 */
-		void queueDrawCommands() override;
-
-		/**   
-		 * @brief	 Clears references to all managed types and objects. Must be called before loadAssemblyData if 
-		 * 			 loadAssemblyData was called previously.
-		 */
-		void clearAssemblyData();
-
-		/**
-		 * @brief	Loads internal managed assembly types and finds all custom handle classes.
-		 *			Must be called after construction and after assembly reload.
-		 */
-		void loadAssemblyData();
-
-		/**
-		 * @brief	Checks is the provided type a valid custom handle class. Custom handles
-		 *			must have a [CustomHandle] attribute and must implement BansheeEditor.Handle.
-		 *
-		 * @param	type			Type to check.
-		 * @param	componentType	Component type for which the handle should be displayed for. Handle will not
-		 *							be displayed unless a component of this type is selected. Only valid if method returns true.
-		 * @param	ctor			Constructor method for the handle type. Only valid if method returns true.
-		 *
-		 * @return	True if the type is a valid custom handle type.
-		 */
-		bool isValidHandleType(MonoClass* type, MonoClass*& componentType, MonoMethod*& ctor);
-
-		/**
-		 * @brief	Triggers the PreInput method on the provided Handle object. Pre
-		 *			input happens before any handles are selected or moved and allows you
-		 *			to position the handles or prepare them in some other way.
-		 */
-		void callPreInput(MonoObject* instance);
-
-		/**
-		 * @brief	Triggers the PostInput method on the provided Handle object.
-		 *			Post input happens after we know in what way has the user interacted
-		 *			with the handles this frame.
-		 */
-		void callPostInput(MonoObject* instance);
-
-		/**
-		 * @brief	Triggers the Draw method on the provided Handle object. Draw allows
-		 *			you to draw the visual representation of the handles. Called after PostInput.
-		 */
-		void callDraw(MonoObject* instance);
-
-		/**
-		 * @brief	Triggers the Destroy method on the provided Handle object. Destroy
-		 *			is called when the handle is no longer being displayed.
-		 */
-		void callDestroy(MonoObject* instance);
-
-		ScriptAssemblyManager& mScriptObjectManager;
-		HEvent mDomainUnloadConn;
-		HEvent mDomainLoadConn;
-
-		Map<String, CustomHandleData> mHandles;
-
-		ActiveCustomHandles mActiveHandleData;
-
-		MonoObject* mDefaultHandleManager = nullptr;
-		uint32_t mDefaultHandleManagerGCHandle = 0;
-
-		MonoClass* mCustomHandleAttribute = nullptr;
-		MonoField* mTypeField = nullptr;
-		MonoClass* mHandleBaseClass = nullptr;
-		MonoClass* mDefaultHandleManagerClass = nullptr;
-
-		typedef void(__stdcall *DestroyThunkDef) (MonoObject*, MonoException**);
-
-		MonoMethod* mPreInputMethod;
-		MonoMethod* mPostInputMethod;
-		MonoMethod* mDrawMethod;
-		DestroyThunkDef mDestroyThunk;
-	};
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsHandleManager.h"
+#include <mono/jit/jit.h>
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Renders, updates and manipulates handles declared in managed code.
+	 *			Managed code handles have a [CustomHandle] attribute and must implement
+	 *			BansheeEditor.Handle.
+	 */
+	class BS_SCR_BED_EXPORT ScriptHandleManager : public HandleManager
+	{
+		/**
+		 * @brief	Contains data about a manage type that draws and handles
+		 *			interaction with a custom handle.
+		 */
+		struct CustomHandleData
+		{
+			MonoClass* handleType;
+			MonoClass* componentType;
+			MonoMethod* ctor;
+		};
+
+		/**
+		 * @brief	Data about an active instance of a managed custom handle object.
+		 *			Active handle means its scene object is currently selected and the
+		 *			handle is displayed and can be interacted with.
+		 */
+		struct ActiveCustomHandleData
+		{
+			MonoObject* object;
+			uint32_t gcHandle;
+		};
+
+		/**
+		 * @brief	Data about all active managed custom handle objects
+		 *			for a specific scene object. Active handle means its 
+		 *			scene object is currently selected and the handle is displayed 
+		 *			and can be interacted with.
+		 */
+		struct ActiveCustomHandles
+		{
+			HSceneObject selectedObject;
+			Vector<ActiveCustomHandleData> handles;
+		};
+
+	public:
+		ScriptHandleManager(ScriptAssemblyManager& scriptObjectManager);
+		~ScriptHandleManager();
+
+	protected:
+		/**
+		 * @copydoc	HandleManager::refreshHandles
+		 */
+		void refreshHandles() override;
+
+		/**
+		 * @copydoc	HandleManager::triggerHandles
+		 */
+		void triggerHandles() override;
+
+		/**
+		 * @copydoc	HandleManager::queueDrawCommands
+		 */
+		void queueDrawCommands() override;
+
+		/**   
+		 * @brief	 Clears references to all managed types and objects. Must be called before loadAssemblyData if 
+		 * 			 loadAssemblyData was called previously.
+		 */
+		void clearAssemblyData();
+
+		/**
+		 * @brief	Loads internal managed assembly types and finds all custom handle classes.
+		 *			Must be called after construction and after assembly reload.
+		 */
+		void loadAssemblyData();
+
+		/**
+		 * @brief	Checks is the provided type a valid custom handle class. Custom handles
+		 *			must have a [CustomHandle] attribute and must implement BansheeEditor.Handle.
+		 *
+		 * @param	type			Type to check.
+		 * @param	componentType	Component type for which the handle should be displayed for. Handle will not
+		 *							be displayed unless a component of this type is selected. Only valid if method returns true.
+		 * @param	ctor			Constructor method for the handle type. Only valid if method returns true.
+		 *
+		 * @return	True if the type is a valid custom handle type.
+		 */
+		bool isValidHandleType(MonoClass* type, MonoClass*& componentType, MonoMethod*& ctor) const;
+
+		/**
+		 * @brief	Triggers the PreInput method on the provided Handle object. Pre
+		 *			input happens before any handles are selected or moved and allows you
+		 *			to position the handles or prepare them in some other way.
+		 */
+		void callPreInput(MonoObject* instance);
+
+		/**
+		 * @brief	Triggers the PostInput method on the provided Handle object.
+		 *			Post input happens after we know in what way has the user interacted
+		 *			with the handles this frame.
+		 */
+		void callPostInput(MonoObject* instance);
+
+		/**
+		 * @brief	Triggers the Draw method on the provided Handle object. Draw allows
+		 *			you to draw the visual representation of the handles. Called after PostInput.
+		 */
+		void callDraw(MonoObject* instance);
+
+		/**
+		 * @brief	Triggers the Destroy method on the provided Handle object. Destroy
+		 *			is called when the handle is no longer being displayed.
+		 */
+		void callDestroy(MonoObject* instance);
+
+		ScriptAssemblyManager& mScriptObjectManager;
+		HEvent mDomainUnloadConn;
+		HEvent mDomainLoadConn;
+
+		Map<String, CustomHandleData> mHandles;
+
+		ActiveCustomHandles mActiveHandleData;
+		Vector<MonoClass*> mGlobalHandlesToCreate;
+		Vector<ActiveCustomHandleData> mActiveGlobalHandles;
+
+		MonoObject* mDefaultHandleManager = nullptr;
+		uint32_t mDefaultHandleManagerGCHandle = 0;
+
+		MonoClass* mCustomHandleAttribute = nullptr;
+		MonoField* mTypeField = nullptr;
+		MonoClass* mHandleBaseClass = nullptr;
+		MonoClass* mDefaultHandleManagerClass = nullptr;
+
+		typedef void(__stdcall *DestroyThunkDef) (MonoObject*, MonoException**);
+
+		MonoMethod* mPreInputMethod;
+		MonoMethod* mPostInputMethod;
+		MonoMethod* mDrawMethod;
+		DestroyThunkDef mDestroyThunk;
+	};
 }

+ 292 - 247
SBansheeEditor/Source/BsScriptHandleManager.cpp

@@ -1,248 +1,293 @@
-#include "BsScriptHandleManager.h"
-#include "BsScriptAssemblyManager.h"
-#include "BsMonoManager.h"
-#include "BsMonoAssembly.h"
-#include "BsMonoClass.h"
-#include "BsMonoField.h"
-#include "BsMonoMethod.h"
-#include "BsMonoUtil.h"
-#include "BsSelection.h"
-#include "BsSceneObject.h"
-#include "BsManagedComponent.h"
-#include "BsScriptObjectManager.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	ScriptHandleManager::ScriptHandleManager(ScriptAssemblyManager& scriptObjectManager)
-		:mScriptObjectManager(scriptObjectManager)
-	{
-		mDomainLoadConn = ScriptObjectManager::instance().onRefreshDomainLoaded.connect(std::bind(&ScriptHandleManager::loadAssemblyData, this));
-		mDomainUnloadConn = MonoManager::instance().onDomainUnload.connect(std::bind(&ScriptHandleManager::clearAssemblyData, this));
-		loadAssemblyData();
-	}
-
-	ScriptHandleManager::~ScriptHandleManager()
-	{
-		clearAssemblyData();
-
-		mDomainUnloadConn.disconnect();
-		mDomainLoadConn.disconnect();
-	}
-
-	void ScriptHandleManager::refreshHandles()
-	{
-		const Vector<HSceneObject>& selectedSOs = Selection::instance().getSceneObjects();
-
-		HSceneObject newSelectedObject;
-		if (selectedSOs.size() > 0)
-		{
-			// We only consider the first selected object for custom handles, multiple selection is not supported
-			newSelectedObject = selectedSOs[0]; 
-		}
-
-		if (newSelectedObject != mActiveHandleData.selectedObject)
-		{
-			for (auto& handle : mActiveHandleData.handles)
-			{
-				callDestroy(handle.object);
-				mono_gchandle_free(handle.gcHandle);
-			}
-
-			mActiveHandleData.selectedObject = newSelectedObject;
-			mActiveHandleData.handles.clear();
-
-			if (newSelectedObject)
-			{
-				const Vector<HComponent>& components = newSelectedObject->getComponents();
-
-				for (auto& component : components)
-				{
-					if (!rtti_is_of_type<ManagedComponent>(component.getInternalPtr()))
-						continue;
-
-					HManagedComponent mc = static_object_cast<ManagedComponent>(component);
-					const String& componentTypeName = mc->getManagedFullTypeName();
-
-					auto iterFind = mHandles.find(componentTypeName);
-					if (iterFind == mHandles.end())
-						continue;
-
-					CustomHandleData& handleData = iterFind->second;
-					MonoObject* newHandleInstance = handleData.handleType->createInstance(false);
-
-					void* params[1] = { mc->getManagedInstance() };
-					handleData.ctor->invoke(newHandleInstance, params);
-
-					mActiveHandleData.handles.push_back(ActiveCustomHandleData());
-					ActiveCustomHandleData& newHandleData = mActiveHandleData.handles.back();
-
-					newHandleData.object = newHandleInstance;
-					newHandleData.gcHandle = mono_gchandle_new(newHandleInstance, false);
-				}
-			}
-		}
-
-		if (mDefaultHandleManager == nullptr)
-		{
-			mDefaultHandleManager = mDefaultHandleManagerClass->createInstance(true);
-			mDefaultHandleManagerGCHandle = mono_gchandle_new(mDefaultHandleManager, false);
-		}
-
-		callPreInput(mDefaultHandleManager);
-
-		for (auto& handle : mActiveHandleData.handles)
-			callPreInput(handle.object);
-	}
-
-	void ScriptHandleManager::triggerHandles()
-	{
-		callPostInput(mDefaultHandleManager);
-
-		for (auto& handle : mActiveHandleData.handles)
-		{
-			callPostInput(handle.object);
-		}
-	}
-
-	void ScriptHandleManager::queueDrawCommands()
-	{
-		if (mDefaultHandleManager != nullptr)
-			callDraw(mDefaultHandleManager);
-
-		for (auto& handle : mActiveHandleData.handles)
-		{
-			callDraw(handle.object);
-		}
-	}
-
-	void ScriptHandleManager::clearAssemblyData()
-	{
-		for (auto& handle : mActiveHandleData.handles)
-		{
-			callDestroy(handle.object);
-			mono_gchandle_free(handle.gcHandle);
-		}
-
-		mActiveHandleData.selectedObject = HSceneObject();
-		mActiveHandleData.handles.clear();
-
-		if (mDefaultHandleManager != nullptr)
-		{
-			callDestroy(mDefaultHandleManager);
-			mono_gchandle_free(mDefaultHandleManagerGCHandle);
-		}
-
-		mDefaultHandleManager = nullptr;
-		mDefaultHandleManagerGCHandle = 0;
-	}
-
-	void ScriptHandleManager::loadAssemblyData()
-	{
-		MonoAssembly* editorAssembly = MonoManager::instance().getAssembly(EDITOR_ASSEMBLY);
-		mCustomHandleAttribute = editorAssembly->getClass("BansheeEditor", "CustomHandle");
-		if (mCustomHandleAttribute == nullptr)
-			BS_EXCEPT(InvalidStateException, "Cannot find CustomHandle managed class.");
-
-		mHandleBaseClass = editorAssembly->getClass("BansheeEditor", "Handle");
-		if (mHandleBaseClass == nullptr)
-			BS_EXCEPT(InvalidStateException, "Cannot find Handle managed class.");
-
-		mDefaultHandleManagerClass = editorAssembly->getClass("BansheeEditor", "DefaultHandleManager");
-		if (mDefaultHandleManagerClass == nullptr)
-			BS_EXCEPT(InvalidStateException, "Cannot find DefaultHandleManager managed class.");
-
-		mTypeField = mCustomHandleAttribute->getField("type");
-
-		mPreInputMethod = mHandleBaseClass->getMethod("PreInput", 0);
-		mPostInputMethod = mHandleBaseClass->getMethod("PostInput", 0);
-		mDrawMethod = mHandleBaseClass->getMethod("Draw", 0);
-		mDestroyThunk = (DestroyThunkDef)mHandleBaseClass->getMethod("Destroy", 0)->getThunk();
-
-		Vector<String> scriptAssemblyNames = mScriptObjectManager.getScriptAssemblies();
-		for (auto& assemblyName : scriptAssemblyNames)
-		{
-			MonoAssembly* assembly = MonoManager::instance().getAssembly(assemblyName);
-
-			// Find new custom handle types
-			const Vector<MonoClass*>& allClasses = assembly->getAllClasses();
-			for (auto curClass : allClasses)
-			{
-				MonoClass* componentType = nullptr;
-				MonoMethod* ctor = nullptr;
-
-				if (isValidHandleType(curClass, componentType, ctor))
-				{
-					String fullComponentName = componentType->getFullName();
-
-					CustomHandleData& newHandleData = mHandles[fullComponentName];
-
-					newHandleData.componentType = componentType;
-					newHandleData.handleType = curClass;
-					newHandleData.ctor = ctor;
-				}
-			}
-		}
-	}
-
-	bool ScriptHandleManager::isValidHandleType(MonoClass* type, MonoClass*& componentType, MonoMethod*& ctor)
-	{
-		componentType = nullptr;
-		ctor = nullptr;
-
-		if (!type->hasAttribute(mCustomHandleAttribute))
-			return false;
-
-		if (!type->isSubClassOf(mHandleBaseClass))
-			return false;
-
-		MonoObject* customHandleAttrib = type->getAttribute(mCustomHandleAttribute);
-		MonoReflectionType* attribReflType = nullptr;
-
-		mTypeField->getValue(customHandleAttrib, &attribReflType);
-		MonoType* attribType = mono_reflection_type_get_type(attribReflType);
-		::MonoClass* attribMonoClass = mono_class_from_mono_type(attribType);
-
-		MonoClass* attribClass = MonoManager::instance().findClass(attribMonoClass);
-		if (attribClass == nullptr)
-			return false;
-
-		MonoClass* componentClass = mScriptObjectManager.getComponentClass();
-		if (!attribClass->isSubClassOf(componentClass))
-			return false;
-		
-		MonoMethod* constructor = type->getMethod(".ctor", 1);
-		if (constructor == nullptr)
-			return false;
-
-		MonoClass* paramType = constructor->getParameterType(0);
-		if (paramType != attribClass)
-			return false;
-
-		componentType = paramType;
-		ctor = constructor;
-
-		return true;
-	}
-
-	void ScriptHandleManager::callPreInput(MonoObject* instance)
-	{
-		mPreInputMethod->invokeVirtual(instance, nullptr);
-	}
-
-	void ScriptHandleManager::callPostInput(MonoObject* instance)
-	{
-		mPostInputMethod->invokeVirtual(instance, nullptr);
-	}
-
-	void ScriptHandleManager::callDraw(MonoObject* instance)
-	{
-		mDrawMethod->invokeVirtual(instance, nullptr);
-	}
-
-	void ScriptHandleManager::callDestroy(MonoObject* instance)
-	{
-		MonoUtil::invokeThunk(mDestroyThunk, instance);
-	}
+#include "BsScriptHandleManager.h"
+#include "BsScriptAssemblyManager.h"
+#include "BsMonoManager.h"
+#include "BsMonoAssembly.h"
+#include "BsMonoClass.h"
+#include "BsMonoField.h"
+#include "BsMonoMethod.h"
+#include "BsMonoUtil.h"
+#include "BsSelection.h"
+#include "BsSceneObject.h"
+#include "BsManagedComponent.h"
+#include "BsScriptObjectManager.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ScriptHandleManager::ScriptHandleManager(ScriptAssemblyManager& scriptObjectManager)
+		:mScriptObjectManager(scriptObjectManager)
+	{
+		mDomainLoadConn = ScriptObjectManager::instance().onRefreshDomainLoaded.connect(std::bind(&ScriptHandleManager::loadAssemblyData, this));
+		mDomainUnloadConn = MonoManager::instance().onDomainUnload.connect(std::bind(&ScriptHandleManager::clearAssemblyData, this));
+		loadAssemblyData();
+	}
+
+	ScriptHandleManager::~ScriptHandleManager()
+	{
+		clearAssemblyData();
+
+		mDomainUnloadConn.disconnect();
+		mDomainLoadConn.disconnect();
+	}
+
+	void ScriptHandleManager::refreshHandles()
+	{
+		// Activate global handles
+		for(auto& handle : mGlobalHandlesToCreate)
+		{
+			MonoObject* newHandleInstance = handle->createInstance();
+
+			mActiveGlobalHandles.push_back(ActiveCustomHandleData());
+			ActiveCustomHandleData& newHandleData = mActiveGlobalHandles.back();
+
+			newHandleData.object = newHandleInstance;
+			newHandleData.gcHandle = mono_gchandle_new(newHandleInstance, false);
+		}
+
+		mGlobalHandlesToCreate.clear();
+
+		// Activate object-specific handles
+		const Vector<HSceneObject>& selectedSOs = Selection::instance().getSceneObjects();
+
+		HSceneObject newSelectedObject;
+		if (selectedSOs.size() > 0)
+		{
+			// We only consider the first selected object for custom handles, multiple selection is not supported
+			newSelectedObject = selectedSOs[0]; 
+		}
+
+		if (newSelectedObject != mActiveHandleData.selectedObject)
+		{
+			for (auto& handle : mActiveHandleData.handles)
+			{
+				callDestroy(handle.object);
+				mono_gchandle_free(handle.gcHandle);
+			}
+
+			mActiveHandleData.selectedObject = newSelectedObject;
+			mActiveHandleData.handles.clear();
+
+			if (newSelectedObject)
+			{
+				const Vector<HComponent>& components = newSelectedObject->getComponents();
+
+				for (auto& component : components)
+				{
+					if (!rtti_is_of_type<ManagedComponent>(component.getInternalPtr()))
+						continue;
+
+					HManagedComponent mc = static_object_cast<ManagedComponent>(component);
+					const String& componentTypeName = mc->getManagedFullTypeName();
+
+					auto iterFind = mHandles.find(componentTypeName);
+					if (iterFind == mHandles.end())
+						continue;
+
+					CustomHandleData& handleData = iterFind->second;
+					MonoObject* newHandleInstance = handleData.handleType->createInstance(false);
+
+					void* params[1] = { mc->getManagedInstance() };
+					handleData.ctor->invoke(newHandleInstance, params);
+
+					mActiveHandleData.handles.push_back(ActiveCustomHandleData());
+					ActiveCustomHandleData& newHandleData = mActiveHandleData.handles.back();
+
+					newHandleData.object = newHandleInstance;
+					newHandleData.gcHandle = mono_gchandle_new(newHandleInstance, false);
+				}
+			}
+		}
+
+		if (mDefaultHandleManager == nullptr)
+		{
+			mDefaultHandleManager = mDefaultHandleManagerClass->createInstance(true);
+			mDefaultHandleManagerGCHandle = mono_gchandle_new(mDefaultHandleManager, false);
+		}
+
+		callPreInput(mDefaultHandleManager);
+
+		for (auto& handle : mActiveGlobalHandles)
+			callPreInput(handle.object);
+
+		for (auto& handle : mActiveHandleData.handles)
+			callPreInput(handle.object);
+	}
+
+	void ScriptHandleManager::triggerHandles()
+	{
+		callPostInput(mDefaultHandleManager);
+
+		for (auto& handle : mActiveGlobalHandles)
+			callPostInput(handle.object);
+
+		for (auto& handle : mActiveHandleData.handles)
+			callPostInput(handle.object);
+	}
+
+	void ScriptHandleManager::queueDrawCommands()
+	{
+		if (mDefaultHandleManager != nullptr)
+			callDraw(mDefaultHandleManager);
+
+		for (auto& handle : mActiveGlobalHandles)
+			callDraw(handle.object);
+
+		for (auto& handle : mActiveHandleData.handles)
+			callDraw(handle.object);
+	}
+
+	void ScriptHandleManager::clearAssemblyData()
+	{
+		mGlobalHandlesToCreate.clear();
+
+		for (auto& handle : mActiveGlobalHandles)
+		{
+			callDestroy(handle.object);
+			mono_gchandle_free(handle.gcHandle);
+		}
+
+		mActiveGlobalHandles.clear();
+
+		for (auto& handle : mActiveHandleData.handles)
+		{
+			callDestroy(handle.object);
+			mono_gchandle_free(handle.gcHandle);
+		}
+
+		mActiveHandleData.selectedObject = HSceneObject();
+		mActiveHandleData.handles.clear();
+
+		if (mDefaultHandleManager != nullptr)
+		{
+			callDestroy(mDefaultHandleManager);
+			mono_gchandle_free(mDefaultHandleManagerGCHandle);
+		}
+
+		mDefaultHandleManager = nullptr;
+		mDefaultHandleManagerGCHandle = 0;
+	}
+
+	void ScriptHandleManager::loadAssemblyData()
+	{
+		MonoAssembly* editorAssembly = MonoManager::instance().getAssembly(EDITOR_ASSEMBLY);
+		mCustomHandleAttribute = editorAssembly->getClass("BansheeEditor", "CustomHandle");
+		if (mCustomHandleAttribute == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find CustomHandle managed class.");
+
+		mHandleBaseClass = editorAssembly->getClass("BansheeEditor", "Handle");
+		if (mHandleBaseClass == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find Handle managed class.");
+
+		mDefaultHandleManagerClass = editorAssembly->getClass("BansheeEditor", "DefaultHandleManager");
+		if (mDefaultHandleManagerClass == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find DefaultHandleManager managed class.");
+
+		mTypeField = mCustomHandleAttribute->getField("type");
+
+		mPreInputMethod = mHandleBaseClass->getMethod("PreInput", 0);
+		mPostInputMethod = mHandleBaseClass->getMethod("PostInput", 0);
+		mDrawMethod = mHandleBaseClass->getMethod("Draw", 0);
+		mDestroyThunk = (DestroyThunkDef)mHandleBaseClass->getMethod("Destroy", 0)->getThunk();
+
+		Vector<String> scriptAssemblyNames = mScriptObjectManager.getScriptAssemblies();
+		for (auto& assemblyName : scriptAssemblyNames)
+		{
+			MonoAssembly* assembly = MonoManager::instance().getAssembly(assemblyName);
+
+			// Find new custom handle types
+			const Vector<MonoClass*>& allClasses = assembly->getAllClasses();
+			for (auto curClass : allClasses)
+			{
+				MonoClass* componentType = nullptr;
+				MonoMethod* ctor = nullptr;
+
+				if (isValidHandleType(curClass, componentType, ctor))
+				{
+					if (componentType != nullptr)
+					{
+						String fullComponentName = componentType->getFullName();
+						CustomHandleData& newHandleData = mHandles[fullComponentName];
+
+						newHandleData.componentType = componentType;
+						newHandleData.handleType = curClass;
+						newHandleData.ctor = ctor;
+					}
+					else // Global handle
+					{
+						mGlobalHandlesToCreate.push_back(curClass);
+					}
+				}
+			}
+		}
+	}
+
+	bool ScriptHandleManager::isValidHandleType(MonoClass* type, MonoClass*& componentType, MonoMethod*& ctor) const
+	{
+		componentType = nullptr;
+		ctor = nullptr;
+
+		if (!type->hasAttribute(mCustomHandleAttribute))
+			return false;
+
+		if (!type->isSubClassOf(mHandleBaseClass))
+			return false;
+
+		MonoObject* customHandleAttrib = type->getAttribute(mCustomHandleAttribute);
+		MonoReflectionType* attribReflType = nullptr;
+
+		mTypeField->getValue(customHandleAttrib, &attribReflType);
+
+		// Handle shown only when specific component type is selected
+		if (attribReflType != nullptr)
+		{
+			MonoType* attribType = mono_reflection_type_get_type(attribReflType);
+			::MonoClass* attribMonoClass = mono_class_from_mono_type(attribType);
+
+			MonoClass* attribClass = MonoManager::instance().findClass(attribMonoClass);
+			if (attribClass != nullptr)
+				return false;
+
+			MonoClass* componentClass = mScriptObjectManager.getComponentClass();
+			if (!attribClass->isSubClassOf(componentClass))
+				return false;
+
+			MonoMethod* constructor = type->getMethod(".ctor", 1);
+			if (constructor == nullptr)
+				return false;
+
+			MonoClass* paramType = constructor->getParameterType(0);
+			if (paramType != attribClass)
+				return false;
+
+			componentType = paramType;
+			ctor = constructor;
+		}
+		else // Handle shown always
+		{
+			// Do nothing
+		}
+
+		return true;
+	}
+
+	void ScriptHandleManager::callPreInput(MonoObject* instance)
+	{
+		mPreInputMethod->invokeVirtual(instance, nullptr);
+	}
+
+	void ScriptHandleManager::callPostInput(MonoObject* instance)
+	{
+		mPostInputMethod->invokeVirtual(instance, nullptr);
+	}
+
+	void ScriptHandleManager::callDraw(MonoObject* instance)
+	{
+		mDrawMethod->invokeVirtual(instance, nullptr);
+	}
+
+	void ScriptHandleManager::callDestroy(MonoObject* instance)
+	{
+		MonoUtil::invokeThunk(mDestroyThunk, instance);
+	}
 }