فهرست منبع

Fixed transparency for handle/gizmos rendering
Added controls for scene camera up/down, panning and zooming (orthographic)
Hooked up scene axis input to rotate scene camera and change its type
Added a way to retrieve editor window bounds and check if it's tab is active
WIP scene grid rendering for ortographic views
Fixed scroll axis input so it reports relative position and not absolute
Fixed scroll axis input so relative position is properly reset

BearishSun 10 سال پیش
والد
کامیت
3effe98d8b

+ 339 - 333
BansheeCore/Source/BsInput.cpp

@@ -1,334 +1,340 @@
-#include "BsInput.h"
-#include "BsTime.h"
-#include "BsMath.h"
-#include "BsRect2I.h"
-#include "BsDebug.h"
-#include "BsRenderWindowManager.h"
-
-using namespace std::placeholders;
-
-namespace BansheeEngine
-{
-	const int Input::HISTORY_BUFFER_SIZE = 10; // Size of buffer used for input smoothing
-	const float Input::WEIGHT_MODIFIER = 0.5f;
-
-	Input::DeviceData::DeviceData()
-	{
-		for (int i = 0; i < BC_Count; i++)
-			keyStates[i] = ButtonState::Off;
-	}
-
-	Input::Input()
-		:mLastPositionSet(false), mPointerDoubleClicked(false)
-	{ 
-		mOSInputHandler = bs_shared_ptr_new<OSInputHandler>();
-
-		mOSInputHandler->onCharInput.connect(std::bind(&Input::charInput, this, _1));
-		mOSInputHandler->onCursorMoved.connect(std::bind(&Input::cursorMoved, this, _1));
-		mOSInputHandler->onCursorPressed.connect(std::bind(&Input::cursorPressed, this, _1));
-		mOSInputHandler->onCursorReleased.connect(std::bind(&Input::cursorReleased, this, _1));
-		mOSInputHandler->onDoubleClick.connect(std::bind(&Input::cursorDoubleClick, this, _1));
-		mOSInputHandler->onInputCommand.connect(std::bind(&Input::inputCommandEntered, this, _1));
-
-		RenderWindowManager::instance().onFocusGained.connect(std::bind(&Input::inputWindowChanged, this, _1));
-
-		for (int i = 0; i < 3; i++)
-			mPointerButtonStates[i] = ButtonState::Off;
-	}
-
-	Input::~Input()
-	{ }
-
-	void Input::_registerRawInputHandler(std::shared_ptr<RawInputHandler> inputHandler)
-	{
-		if(mRawInputHandler != inputHandler)
-		{
-			mRawInputHandler = inputHandler;
-
-			if(mRawInputHandler != nullptr)
-			{
-				mRawInputHandler->onButtonDown.connect(std::bind(&Input::buttonDown, this, _1, _2, _3));
-				mRawInputHandler->onButtonUp.connect(std::bind(&Input::buttonUp, this, _1, _2, _3));
-
-				mRawInputHandler->onAxisMoved.connect(std::bind(&Input::axisMoved, this, _1, _2, _3));
-			}
-		}
-	}
-
-	void Input::_update()
-	{
-		// Toggle states only remain active for a single frame before they are transitioned
-		// into permanent state
-
-		for (auto& deviceData : mDevices)
-		{
-			for (UINT32 i = 0; i < BC_Count; i++)
-			{
-				if (deviceData.keyStates[i] == ButtonState::ToggledOff || deviceData.keyStates[i] == ButtonState::ToggledOnOff)
-					deviceData.keyStates[i] = ButtonState::Off;
-				else if (deviceData.keyStates[i] == ButtonState::ToggledOn)
-					deviceData.keyStates[i] = ButtonState::On;
-			}
-		}
-
-		for (UINT32 i = 0; i < 3; i++)
-		{
-			if (mPointerButtonStates[i] == ButtonState::ToggledOff || mPointerButtonStates[i] == ButtonState::ToggledOnOff)
-				mPointerButtonStates[i] = ButtonState::Off;
-			else if (mPointerButtonStates[i] == ButtonState::ToggledOn)
-				mPointerButtonStates[i] = ButtonState::On;
-		}
-
-		mPointerDelta = Vector2I::ZERO; // Reset delta in case we don't receive any mouse input this frame
-		mPointerDoubleClicked = false;
-
-		if(mRawInputHandler == nullptr)
-		{
-			LOGERR("Raw input handler not initialized!");
-			return;
-		}
-		else
-			mRawInputHandler->_update();
-
-		if(mOSInputHandler == nullptr)
-		{
-			LOGERR("OS input handler not initialized!");
-			return;
-		}
-		else
-			mOSInputHandler->_update();
-	}
-
-	void Input::_triggerCallbacks()
-	{
-		for (auto& event : mQueuedEvents)
-		{
-			switch (event.type)
-			{
-			case EventType::ButtonDown:
-				onButtonDown(mButtonDownEvents[event.idx]);
-				break;
-			case EventType::ButtonUp:
-				onButtonUp(mButtonUpEvents[event.idx]);
-				break;
-			case EventType::PointerDown:
-				onPointerPressed(mPointerPressedEvents[event.idx]);
-				break;
-			case EventType::PointerUp:
-				onPointerReleased(mPointerReleasedEvents[event.idx]);
-				break;
-			case EventType::PointerDoubleClick:
-				onPointerDoubleClick(mPointerDoubleClickEvents[event.idx]);
-				break;
-			case EventType::PointerMoved:
-				onPointerMoved(mPointerMovedEvents[event.idx]);
-				break;
-			case EventType::TextInput:
-				onCharInput(mTextInputEvents[event.idx]);
-				break;
-			case EventType::Command:
-				onInputCommand(mCommandEvents[event.idx]);
-				break;
-			}
-		}
-
-		mQueuedEvents.clear();
-		mButtonDownEvents.clear();
-		mButtonUpEvents.clear();
-		mPointerPressedEvents.clear();
-		mPointerReleasedEvents.clear();
-		mPointerDoubleClickEvents.clear();
-		mPointerMovedEvents.clear();
-		mTextInputEvents.clear();
-		mCommandEvents.clear();
-	}
-
-	void Input::inputWindowChanged(RenderWindow& win)
-	{
-		if(mRawInputHandler != nullptr)
-			mRawInputHandler->_inputWindowChanged(win);
-
-		if(mOSInputHandler != nullptr)
-			mOSInputHandler->_inputWindowChanged(win);
-	}
-
-	void Input::buttonDown(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
-	{
-		while (deviceIdx >= (UINT32)mDevices.size())
-			mDevices.push_back(DeviceData());
-
-		mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOn;
-
-		ButtonEvent btnEvent;
-		btnEvent.buttonCode = code;
-		btnEvent.timestamp = timestamp;
-		btnEvent.deviceIdx = deviceIdx;
-
-		mQueuedEvents.push_back(QueuedEvent(EventType::ButtonDown, (UINT32)mButtonDownEvents.size()));
-		mButtonDownEvents.push_back(btnEvent);
-	}
-
-	void Input::buttonUp(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
-	{
-		while (deviceIdx >= (UINT32)mDevices.size())
-			mDevices.push_back(DeviceData());
-
-		if (mDevices[deviceIdx].keyStates[code & 0x0000FFFF] == ButtonState::ToggledOn)
-			mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOnOff;
-		else
-			mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOff;
-
-		ButtonEvent btnEvent;
-		btnEvent.buttonCode = code;
-		btnEvent.timestamp = timestamp;
-		btnEvent.deviceIdx = deviceIdx;
-
-		mQueuedEvents.push_back(QueuedEvent(EventType::ButtonUp, (UINT32)mButtonUpEvents.size()));
-		mButtonUpEvents.push_back(btnEvent);
-	}
-
-	void Input::axisMoved(UINT32 deviceIdx, const RawAxisState& state, UINT32 axis)
-	{
-		while (deviceIdx >= (UINT32)mDevices.size())
-			mDevices.push_back(DeviceData());
-
-		Vector<RawAxisState>& axes = mDevices[deviceIdx].axes;
-		while (axis >= (UINT32)axes.size())
-			axes.push_back(RawAxisState());
-
-		mDevices[deviceIdx].axes[axis] = state;
-	}
-
-	void Input::cursorMoved(const PointerEvent& event)
-	{
-		mQueuedEvents.push_back(QueuedEvent(EventType::PointerMoved, (UINT32)mPointerMovedEvents.size()));
-		mPointerMovedEvents.push_back(event);
-
-		if (mLastPositionSet)
-			mPointerDelta = event.screenPos - mPointerPosition;
-
-		mPointerPosition = event.screenPos;
-		mLastPositionSet = true;
-	}
-
-	void Input::cursorPressed(const PointerEvent& event)
-	{
-		mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOn;
-
-		mQueuedEvents.push_back(QueuedEvent(EventType::PointerDown, (UINT32)mPointerPressedEvents.size()));
-		mPointerPressedEvents.push_back(event);
-	}
-
-	void Input::cursorReleased(const PointerEvent& event)
-	{
-		if (mPointerButtonStates[(UINT32)event.button] == ButtonState::ToggledOn)
-			mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOnOff;
-		else
-			mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOff;
-
-		mQueuedEvents.push_back(QueuedEvent(EventType::PointerUp, (UINT32)mPointerReleasedEvents.size()));
-		mPointerReleasedEvents.push_back(event);
-	}
-
-	void Input::cursorDoubleClick(const PointerEvent& event)
-	{
-		mPointerDoubleClicked = true;
-
-		mQueuedEvents.push_back(QueuedEvent(EventType::PointerDoubleClick, (UINT32)mPointerDoubleClickEvents.size()));
-		mPointerDoubleClickEvents.push_back(event);
-	}
-
-	void Input::inputCommandEntered(InputCommandType commandType)
-	{
-		mQueuedEvents.push_back(QueuedEvent(EventType::Command, (UINT32)mCommandEvents.size()));
-		mCommandEvents.push_back(commandType);
-	}
-
-	void Input::charInput(UINT32 chr)
-	{
-		TextInputEvent textInputEvent;
-		textInputEvent.textChar = chr;
-
-		mQueuedEvents.push_back(QueuedEvent(EventType::TextInput, (UINT32)mTextInputEvents.size()));
-		mTextInputEvents.push_back(textInputEvent);
-	}
-
-	float Input::getAxisValue(UINT32 type, UINT32 deviceIdx) const
-	{
-		if (deviceIdx >= (UINT32)mDevices.size())
-			return 0.0f;
-
-		const Vector<RawAxisState>& axes = mDevices[deviceIdx].axes;
-		if (type >= (UINT32)axes.size())
-			return 0.0f;
-
-		return axes[type].abs;
-	}
-
-	bool Input::isButtonHeld(ButtonCode button, UINT32 deviceIdx) const
-	{
-		if (deviceIdx >= (UINT32)mDevices.size())
-			return false;
-
-		return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::On || 
-			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn ||
-			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
-	}
-
-	bool Input::isButtonUp(ButtonCode button, UINT32 deviceIdx) const
-	{
-		if (deviceIdx >= (UINT32)mDevices.size())
-			return false;
-
-		return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOff ||
-			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
-	}
-
-	bool Input::isButtonDown(ButtonCode button, UINT32 deviceIdx) const
-	{
-		if (deviceIdx >= (UINT32)mDevices.size())
-			return false;
-
-		return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn ||
-			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
-	}
-
-	bool Input::isPointerButtonHeld(PointerEventButton pointerButton) const
-	{
-		return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::On ||
-			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn ||
-			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
-	}
-
-	bool Input::isPointerButtonUp(PointerEventButton pointerButton) const
-	{
-		return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOff ||
-			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
-	}
-
-	bool Input::isPointerButtonDown(PointerEventButton pointerButton) const
-	{
-		return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn ||
-			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
-	}
-
-	bool Input::isPointerDoubleClicked() const
-	{
-		return mPointerDoubleClicked;
-	}
-
-	Vector2I Input::getPointerPosition() const
-	{
-		return mPointerPosition;
-	}
-
-	void Input::setMouseSmoothing(bool enable)
-	{
-		mRawInputHandler->setMouseSmoothing(enable);
-	}
-
-	Input& gInput()
-	{
-		return Input::instance();
-	}
+#include "BsInput.h"
+#include "BsTime.h"
+#include "BsMath.h"
+#include "BsRect2I.h"
+#include "BsDebug.h"
+#include "BsRenderWindowManager.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	const int Input::HISTORY_BUFFER_SIZE = 10; // Size of buffer used for input smoothing
+	const float Input::WEIGHT_MODIFIER = 0.5f;
+
+	Input::DeviceData::DeviceData()
+	{
+		for (int i = 0; i < BC_Count; i++)
+			keyStates[i] = ButtonState::Off;
+	}
+
+	Input::Input()
+		:mLastPositionSet(false), mPointerDoubleClicked(false)
+	{ 
+		mOSInputHandler = bs_shared_ptr_new<OSInputHandler>();
+
+		mOSInputHandler->onCharInput.connect(std::bind(&Input::charInput, this, _1));
+		mOSInputHandler->onCursorMoved.connect(std::bind(&Input::cursorMoved, this, _1));
+		mOSInputHandler->onCursorPressed.connect(std::bind(&Input::cursorPressed, this, _1));
+		mOSInputHandler->onCursorReleased.connect(std::bind(&Input::cursorReleased, this, _1));
+		mOSInputHandler->onDoubleClick.connect(std::bind(&Input::cursorDoubleClick, this, _1));
+		mOSInputHandler->onInputCommand.connect(std::bind(&Input::inputCommandEntered, this, _1));
+
+		RenderWindowManager::instance().onFocusGained.connect(std::bind(&Input::inputWindowChanged, this, _1));
+
+		for (int i = 0; i < 3; i++)
+			mPointerButtonStates[i] = ButtonState::Off;
+	}
+
+	Input::~Input()
+	{ }
+
+	void Input::_registerRawInputHandler(std::shared_ptr<RawInputHandler> inputHandler)
+	{
+		if(mRawInputHandler != inputHandler)
+		{
+			mRawInputHandler = inputHandler;
+
+			if(mRawInputHandler != nullptr)
+			{
+				mRawInputHandler->onButtonDown.connect(std::bind(&Input::buttonDown, this, _1, _2, _3));
+				mRawInputHandler->onButtonUp.connect(std::bind(&Input::buttonUp, this, _1, _2, _3));
+
+				mRawInputHandler->onAxisMoved.connect(std::bind(&Input::axisMoved, this, _1, _2, _3));
+			}
+		}
+	}
+
+	void Input::_update()
+	{
+		// Toggle states only remain active for a single frame before they are transitioned
+		// into permanent state
+
+		for (auto& deviceData : mDevices)
+		{
+			for (UINT32 i = 0; i < BC_Count; i++)
+			{
+				if (deviceData.keyStates[i] == ButtonState::ToggledOff || deviceData.keyStates[i] == ButtonState::ToggledOnOff)
+					deviceData.keyStates[i] = ButtonState::Off;
+				else if (deviceData.keyStates[i] == ButtonState::ToggledOn)
+					deviceData.keyStates[i] = ButtonState::On;
+			}
+
+			UINT32 numAxes = deviceData.axes.size();
+			for (UINT32 i = 0; i < numAxes; i++)
+			{
+				deviceData.axes[i].rel = 0.0f;
+			}
+		}
+
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			if (mPointerButtonStates[i] == ButtonState::ToggledOff || mPointerButtonStates[i] == ButtonState::ToggledOnOff)
+				mPointerButtonStates[i] = ButtonState::Off;
+			else if (mPointerButtonStates[i] == ButtonState::ToggledOn)
+				mPointerButtonStates[i] = ButtonState::On;
+		}
+
+		mPointerDelta = Vector2I::ZERO; // Reset delta in case we don't receive any mouse input this frame
+		mPointerDoubleClicked = false;
+
+		if(mRawInputHandler == nullptr)
+		{
+			LOGERR("Raw input handler not initialized!");
+			return;
+		}
+		else
+			mRawInputHandler->_update();
+
+		if(mOSInputHandler == nullptr)
+		{
+			LOGERR("OS input handler not initialized!");
+			return;
+		}
+		else
+			mOSInputHandler->_update();
+	}
+
+	void Input::_triggerCallbacks()
+	{
+		for (auto& event : mQueuedEvents)
+		{
+			switch (event.type)
+			{
+			case EventType::ButtonDown:
+				onButtonDown(mButtonDownEvents[event.idx]);
+				break;
+			case EventType::ButtonUp:
+				onButtonUp(mButtonUpEvents[event.idx]);
+				break;
+			case EventType::PointerDown:
+				onPointerPressed(mPointerPressedEvents[event.idx]);
+				break;
+			case EventType::PointerUp:
+				onPointerReleased(mPointerReleasedEvents[event.idx]);
+				break;
+			case EventType::PointerDoubleClick:
+				onPointerDoubleClick(mPointerDoubleClickEvents[event.idx]);
+				break;
+			case EventType::PointerMoved:
+				onPointerMoved(mPointerMovedEvents[event.idx]);
+				break;
+			case EventType::TextInput:
+				onCharInput(mTextInputEvents[event.idx]);
+				break;
+			case EventType::Command:
+				onInputCommand(mCommandEvents[event.idx]);
+				break;
+			}
+		}
+
+		mQueuedEvents.clear();
+		mButtonDownEvents.clear();
+		mButtonUpEvents.clear();
+		mPointerPressedEvents.clear();
+		mPointerReleasedEvents.clear();
+		mPointerDoubleClickEvents.clear();
+		mPointerMovedEvents.clear();
+		mTextInputEvents.clear();
+		mCommandEvents.clear();
+	}
+
+	void Input::inputWindowChanged(RenderWindow& win)
+	{
+		if(mRawInputHandler != nullptr)
+			mRawInputHandler->_inputWindowChanged(win);
+
+		if(mOSInputHandler != nullptr)
+			mOSInputHandler->_inputWindowChanged(win);
+	}
+
+	void Input::buttonDown(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
+	{
+		while (deviceIdx >= (UINT32)mDevices.size())
+			mDevices.push_back(DeviceData());
+
+		mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOn;
+
+		ButtonEvent btnEvent;
+		btnEvent.buttonCode = code;
+		btnEvent.timestamp = timestamp;
+		btnEvent.deviceIdx = deviceIdx;
+
+		mQueuedEvents.push_back(QueuedEvent(EventType::ButtonDown, (UINT32)mButtonDownEvents.size()));
+		mButtonDownEvents.push_back(btnEvent);
+	}
+
+	void Input::buttonUp(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
+	{
+		while (deviceIdx >= (UINT32)mDevices.size())
+			mDevices.push_back(DeviceData());
+
+		if (mDevices[deviceIdx].keyStates[code & 0x0000FFFF] == ButtonState::ToggledOn)
+			mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOnOff;
+		else
+			mDevices[deviceIdx].keyStates[code & 0x0000FFFF] = ButtonState::ToggledOff;
+
+		ButtonEvent btnEvent;
+		btnEvent.buttonCode = code;
+		btnEvent.timestamp = timestamp;
+		btnEvent.deviceIdx = deviceIdx;
+
+		mQueuedEvents.push_back(QueuedEvent(EventType::ButtonUp, (UINT32)mButtonUpEvents.size()));
+		mButtonUpEvents.push_back(btnEvent);
+	}
+
+	void Input::axisMoved(UINT32 deviceIdx, const RawAxisState& state, UINT32 axis)
+	{
+		while (deviceIdx >= (UINT32)mDevices.size())
+			mDevices.push_back(DeviceData());
+
+		Vector<RawAxisState>& axes = mDevices[deviceIdx].axes;
+		while (axis >= (UINT32)axes.size())
+			axes.push_back(RawAxisState());
+
+		mDevices[deviceIdx].axes[axis] = state;
+	}
+
+	void Input::cursorMoved(const PointerEvent& event)
+	{
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerMoved, (UINT32)mPointerMovedEvents.size()));
+		mPointerMovedEvents.push_back(event);
+
+		if (mLastPositionSet)
+			mPointerDelta = event.screenPos - mPointerPosition;
+
+		mPointerPosition = event.screenPos;
+		mLastPositionSet = true;
+	}
+
+	void Input::cursorPressed(const PointerEvent& event)
+	{
+		mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOn;
+
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerDown, (UINT32)mPointerPressedEvents.size()));
+		mPointerPressedEvents.push_back(event);
+	}
+
+	void Input::cursorReleased(const PointerEvent& event)
+	{
+		if (mPointerButtonStates[(UINT32)event.button] == ButtonState::ToggledOn)
+			mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOnOff;
+		else
+			mPointerButtonStates[(UINT32)event.button] = ButtonState::ToggledOff;
+
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerUp, (UINT32)mPointerReleasedEvents.size()));
+		mPointerReleasedEvents.push_back(event);
+	}
+
+	void Input::cursorDoubleClick(const PointerEvent& event)
+	{
+		mPointerDoubleClicked = true;
+
+		mQueuedEvents.push_back(QueuedEvent(EventType::PointerDoubleClick, (UINT32)mPointerDoubleClickEvents.size()));
+		mPointerDoubleClickEvents.push_back(event);
+	}
+
+	void Input::inputCommandEntered(InputCommandType commandType)
+	{
+		mQueuedEvents.push_back(QueuedEvent(EventType::Command, (UINT32)mCommandEvents.size()));
+		mCommandEvents.push_back(commandType);
+	}
+
+	void Input::charInput(UINT32 chr)
+	{
+		TextInputEvent textInputEvent;
+		textInputEvent.textChar = chr;
+
+		mQueuedEvents.push_back(QueuedEvent(EventType::TextInput, (UINT32)mTextInputEvents.size()));
+		mTextInputEvents.push_back(textInputEvent);
+	}
+
+	float Input::getAxisValue(UINT32 type, UINT32 deviceIdx) const
+	{
+		if (deviceIdx >= (UINT32)mDevices.size())
+			return 0.0f;
+
+		const Vector<RawAxisState>& axes = mDevices[deviceIdx].axes;
+		if (type >= (UINT32)axes.size())
+			return 0.0f;
+
+		return axes[type].rel;
+	}
+
+	bool Input::isButtonHeld(ButtonCode button, UINT32 deviceIdx) const
+	{
+		if (deviceIdx >= (UINT32)mDevices.size())
+			return false;
+
+		return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::On || 
+			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn ||
+			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
+	}
+
+	bool Input::isButtonUp(ButtonCode button, UINT32 deviceIdx) const
+	{
+		if (deviceIdx >= (UINT32)mDevices.size())
+			return false;
+
+		return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOff ||
+			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
+	}
+
+	bool Input::isButtonDown(ButtonCode button, UINT32 deviceIdx) const
+	{
+		if (deviceIdx >= (UINT32)mDevices.size())
+			return false;
+
+		return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn ||
+			mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
+	}
+
+	bool Input::isPointerButtonHeld(PointerEventButton pointerButton) const
+	{
+		return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::On ||
+			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn ||
+			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
+	}
+
+	bool Input::isPointerButtonUp(PointerEventButton pointerButton) const
+	{
+		return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOff ||
+			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
+	}
+
+	bool Input::isPointerButtonDown(PointerEventButton pointerButton) const
+	{
+		return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn ||
+			mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
+	}
+
+	bool Input::isPointerDoubleClicked() const
+	{
+		return mPointerDoubleClicked;
+	}
+
+	Vector2I Input::getPointerPosition() const
+	{
+		return mPointerPosition;
+	}
+
+	void Input::setMouseSmoothing(bool enable)
+	{
+		mRawInputHandler->setMouseSmoothing(enable);
+	}
+
+	Input& gInput()
+	{
+		return Input::instance();
+	}
 }

+ 7 - 0
BansheeEditor/Include/BsEditorWidget.h

@@ -71,6 +71,12 @@ namespace BansheeEngine
 		 */
 		bool hasFocus() const { return mHasFocus; }
 
+		/** 
+		 * Checks is the widget the currently active widget in its container. This means the widget's tab is active or
+		 * the widget is the only one in its container. 
+		 */
+		bool isActive() const { return mIsActive; }
+
 		/**
 		 * @brief	Gets the parent editor window this widget is docked in.
 		 */
@@ -199,6 +205,7 @@ namespace BansheeEngine
 		UINT32 mDefaultWidth, mDefaultHeight;
 		GUIPanel* mContent;
 		bool mHasFocus;
+		bool mIsActive;
 	};
 
 	/**

+ 39 - 50
BansheeEditor/Include/BsSceneGrid.h

@@ -9,69 +9,60 @@ namespace BansheeEngine
 {
 	class SceneGridCore;
 
-	/**
-	 * @brief	Handles rendering of the grid in the scene view.
-	 */
+	/** Determines how is the scene grid drawn. */
+	enum class GridMode
+	{
+		Perspective, /**< Grid is drawn in XZ plane, at Y = 0. */
+		OrthoX, /**< Grid is drawn in YZ plane, always visible along positive X. */
+		OrthoY, /**< Grid is drawn in XZ plane, always visible along positive Y. */
+		OrthoZ, /**< Grid is drawn in XY plane, always visible along positive Z. */
+		OrthoNegX, /**< Grid is drawn in YZ plane, always visible along negative X. */
+		OrthoNegY, /**< Grid is drawn in XZ plane, always visible along negative Y. */
+		OrthoNegZ /**< Grid is drawn in XY plane, always visible along negative Z. */
+	};
+
+	/**	Handles rendering of the grid in the scene view. */
 	class BS_ED_EXPORT SceneGrid
 	{
 	public:
 		SceneGrid(const CameraPtr& camera);
 		~SceneGrid();
 
-		/**
-		 * @brief	Sets the grid origin in world coordinates.
-		 */
-		void setOrigin(const Vector3& origin);
-
-		/**
-		 * @brief	Sets the total width/height of the grid in XZ plane.
-		 */
+		/**	Sets the total width/height of the grid in XZ plane. */
 		void setSize(UINT32 size);
 		
-		/**
-		 * @brief	Sets the spacing between grid lines.
-		 */
+		/**	Sets the spacing between grid lines. */
 		void setSpacing(float spacing);
 
-		/**
-		 * @brief	Changes the active editor settings. Grid properties
-		 *			will be updated internally when editor settings change.
-		 */
+		/** Determines in what position and orientation is the grid drawn. */
+		void setMode(GridMode mode);
+
+		/** Changes the active editor settings. Grid properties will be updated internally when editor settings change. */
 		void setSettings(const EditorSettingsPtr& settings);
 
-		/**
-		 * @brief	Called once per frame.
-		 *
-		 * @note	Internal method.
-		 */
-		void update();
+		/** Called once per frame. */
+		void _update();
 	private:
-		/**
-		 * @brief	Updates internal grid parameters from the attached settings object.
-		 */
+		/** Updates internal grid parameters from the attached settings object. */
 		void updateFromEditorSettings();
 
-		/**
-		 * @brief	Rebuilds the scene grid mesh. Call this whenever grid parameters change.
-		 */
+		/**	Rebuilds the scene grid mesh. Call this whenever grid parameters change. */
 		void updateGridMesh();
 
 		/**
-		 * @brief	Initializes the core thread portion of the scene grid renderer.
+		 * Initializes the core thread portion of the scene grid renderer.
 		 *
-		 * @param	material	Material used for drawing the grid.
-		 * @param	camera		Camera to render the scene grid to.
+		 * @param[in]	material	Material used for drawing the grid.
+		 * @param[in]	camera		Camera to render the scene grid to.
 		 */
 		void initializeCore(const SPtr<CameraCore>& camera, const SPtr<MaterialCore>& material);
 
-		/**
-		 * @brief	Destroys the core thread portion of the draw manager.
-		 */
+		/** Destroys the core thread portion of the draw manager. */
 		void destroyCore(SceneGridCore* core);
 
-		Vector3 mOrigin;
 		float mSpacing = 1.0f;
 		UINT32 mSize = 256;
+		GridMode mMode = GridMode::Perspective;
 		bool mCoreDirty;
 
 		EditorSettingsPtr mSettings;
@@ -82,9 +73,7 @@ namespace BansheeEngine
 		std::atomic<SceneGridCore*> mCore;
 	};
 
-	/**
-	 * @brief	Handles scene grid rendering on the core thread.
-	 */
+	/** Handles scene grid rendering on the core thread. */
 	class SceneGridCore
 	{
 	public:
@@ -95,30 +84,30 @@ namespace BansheeEngine
 		friend class SceneGrid;
 
 		/**
-		 * @brief	Initializes the object. Must be called right after construction and before any use.
+		 * Initializes the object. Must be called right after construction and before any use.
 		 *
-		 * @param	material	Material used for drawing the grid.
-		 * @param	camera		Camera to render the scene grid to.
+		 * @param[in]	material	Material used for drawing the grid.
+		 * @param[in]	camera		Camera to render the scene grid to.
 		 */
 		void initialize(const SPtr<CameraCore>& camera, const SPtr<MaterialCore>& material);
 
 		/**
-		 * @brief	Updates the grid mesh to render.
+		 * Updates the grid mesh to render.
 		 * 			
-		 * @param	mesh		Grid mesh to render.
-		 * @param	spacing		Spacing between the grid lines.
+		 * @param[in]	mesh		Grid mesh to render.
+		 * @param[in]	spacing		Spacing between the grid lines.
+		 * @param[in]	fadeGrid	Determines should the grid fade out at larger distances.
 		 */
-		void updateData(const SPtr<MeshCore>& mesh, float spacing);
+		void updateData(const SPtr<MeshCore>& mesh, float spacing, bool fadeGrid);
 
-		/**
-		 * @brief	Callback triggered by the renderer, actually draws the grid mesh.
-		 */
+		/**	Callback triggered by the renderer, actually draws the grid mesh. */
 		void render();
 
 		SPtr<CameraCore> mCamera;
 		SPtr<MeshCore> mGridMesh;
 		SPtr<MaterialCore> mGridMaterial;
 		float mSpacing = 1.0f;
+		bool mFadeGrid = true;
 
 		MaterialParamMat4Core mViewProjParam;
 		MaterialParamVec4Core mWorldCameraPosParam;

+ 3 - 1
BansheeEditor/Source/BsEditorWidget.cpp

@@ -16,7 +16,7 @@ namespace BansheeEngine
 	EditorWidgetBase::EditorWidgetBase(const HString& displayName, const String& name, UINT32 defaultWidth,
 		UINT32 defaultHeight, EditorWidgetContainer& parentContainer)
 		:mDisplayName(displayName), mName(name), mParent(nullptr), mContent(nullptr), mX(0), mY(0), mWidth(0), 
-		mHeight(0), mHasFocus(false), mDefaultWidth(defaultWidth), mDefaultHeight(defaultHeight)
+		mHeight(0), mHasFocus(false), mDefaultWidth(defaultWidth), mDefaultHeight(defaultHeight), mIsActive(true)
 	{
 		parentContainer.add(*this);
 	}
@@ -162,11 +162,13 @@ namespace BansheeEngine
 	void EditorWidgetBase::_disable()
 	{
 		mContent->setVisible(false);
+		mIsActive = false;
 	}
 
 	void EditorWidgetBase::_enable()
 	{
 		mContent->setVisible(true);
+		mIsActive = true;
 	}
 
 	CGUIWidget& EditorWidgetBase::getParentWidget() const

+ 232 - 180
BansheeEditor/Source/BsSceneGrid.cpp

@@ -1,181 +1,233 @@
-#include "BsSceneGrid.h"
-#include "BsMath.h"
-#include "BsShapeMeshes3D.h"
-#include "BsVertexDataDesc.h"
-#include "BsMaterial.h"
-#include "BsMesh.h"
-#include "BsBuiltinEditorResources.h"
-#include "BsCCamera.h"
-#include "BsRect3.h"
-#include "BsCoreThread.h"
-#include "BsEditorSettings.h"
-#include "BsRendererManager.h"
-#include "BsRenderer.h"
-#include "BsRendererUtility.h"
-
-namespace BansheeEngine
-{
-	const Color SceneGridCore::GRID_LINE_COLOR = Color(0.5f, 0.5f, 0.5f);
-	const float SceneGridCore::LINE_WIDTH = 0.025f;
-	const float SceneGridCore::LINE_BORDER_WIDTH = 0.00075f;
-	const float SceneGridCore::FADE_OUT_START = 5.0f;
-	const float SceneGridCore::FADE_OUT_END = 40.0f;
-
-	SceneGrid::SceneGrid(const CameraPtr& camera)
-		:mCoreDirty(true), mCore(nullptr)
-	{
-		mVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
-		mVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
-		mVertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL);
-
-		mCore.store(bs_new<SceneGridCore>(), std::memory_order_release);
-
-		HMaterial gridMaterial = BuiltinEditorResources::instance().createSceneGridMaterial();
-		SPtr<MaterialCore> materialCore = gridMaterial->getCore();
-		gCoreAccessor().queueCommand(std::bind(&SceneGrid::initializeCore, this, camera->getCore(), materialCore));
-
-		updateGridMesh();
-	}
-
-	SceneGrid::~SceneGrid()
-	{
-		gCoreAccessor().queueCommand(std::bind(&SceneGrid::destroyCore, this, mCore.load(std::memory_order_relaxed)));
-	}
-
-	void SceneGrid::initializeCore(const SPtr<CameraCore>& camera, const SPtr<MaterialCore>& material)
-	{
-		THROW_IF_NOT_CORE_THREAD;
-
-		mCore.load()->initialize(camera, material);
-	}
-
-	void SceneGrid::destroyCore(SceneGridCore* core)
-	{
-		THROW_IF_NOT_CORE_THREAD;
-
-		bs_delete(core);
-	}
-
-	void SceneGrid::setOrigin(const Vector3& origin)
-	{
-		if (mOrigin != origin)
-		{
-			mOrigin = origin;
-			updateGridMesh();
-		}
-	}
-
-	void SceneGrid::setSize(UINT32 size)
-	{
-		if (mSize != size)
-		{
-			mSize = size;
-			updateGridMesh();
-		}
-	}
-
-	void SceneGrid::setSpacing(float spacing)
-	{
-		if (mSpacing != spacing)
-		{
-			mSpacing = spacing;
-			mCoreDirty = true;
-		}
-	}
-
-	void SceneGrid::setSettings(const EditorSettingsPtr& settings)
-	{
-		mSettings = settings;
-		updateFromEditorSettings();
-	}
-
-	void SceneGrid::update()
-	{
-		if (mSettings != nullptr && mSettingsHash != mSettings->getHash())
-			updateFromEditorSettings();
-
-		if (mCoreDirty)
-		{
-			SceneGridCore* core = mCore.load(std::memory_order_relaxed);
-			gCoreAccessor().queueCommand(std::bind(&SceneGridCore::updateData, core, mGridMesh->getCore(), mSpacing));
-
-			mCoreDirty = false;
-		}
-	}
-
-	void SceneGrid::updateFromEditorSettings()
-	{
-		setSize(mSettings->getGridSize());
-		setSpacing(mSettings->getGridSpacing());
-
-		mSettingsHash = mSettings->getHash();
-	}
-
-	void SceneGrid::updateGridMesh()
-	{
-		std::array<Vector3, 2> axes;
-		axes[0] = Vector3::UNIT_X;
-		axes[1] = Vector3::UNIT_Z;
-
-		std::array<float, 2> extents;
-		extents[0] = mSize * 0.5f;
-		extents[1] = mSize * 0.5f;
-
-		Rect3 quad(mOrigin, axes, extents);
-		MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(8, 12, mVertexDesc);
-
-		ShapeMeshes3D::solidQuad(quad, meshData, 0, 0);
-		mGridMesh = Mesh::create(meshData);
-		mCoreDirty = true;
-	}
-
-	SceneGridCore::~SceneGridCore()
-	{
-		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
-		activeRenderer->_unregisterRenderCallback(mCamera.get(), 5);
-	}
-
-	void SceneGridCore::initialize(const SPtr<CameraCore>& camera, const SPtr<MaterialCore>& material)
-	{
-		mCamera = camera;
-		mGridMaterial = material;
-
-		mViewProjParam = mGridMaterial->getParamMat4("matViewProj");
-		mWorldCameraPosParam = mGridMaterial->getParamVec4("worldCameraPos");
-		mGridColorParam = mGridMaterial->getParamColor("gridColor");
-		mGridSpacingParam = mGridMaterial->getParamFloat("gridSpacing");
-		mGridBorderWidthParam = mGridMaterial->getParamFloat("gridBorderWidth");
-		mGridFadeOutStartParam = mGridMaterial->getParamFloat("gridFadeOutStart");
-		mGridFadeOutEndParam = mGridMaterial->getParamFloat("gridFadeOutEnd");
-
-		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
-		activeRenderer->_registerRenderCallback(camera.get(), 5, std::bind(&SceneGridCore::render, this));			
-	}
-
-	void SceneGridCore::updateData(const SPtr<MeshCore>& mesh, float spacing)
-	{
-		mGridMesh = mesh;
-		mSpacing = spacing;
-	}
-
-	void SceneGridCore::render()
-	{
-		THROW_IF_NOT_CORE_THREAD;
-
-		Matrix4 projMatrix = mCamera->getProjectionMatrixRS();
-		Matrix4 viewMatrix = mCamera->getViewMatrix();
-
-		Matrix4 viewProjMatrix = projMatrix * viewMatrix;
-		mViewProjParam.set(viewProjMatrix);
-
-		mWorldCameraPosParam.set(Vector4(mCamera->getPosition(), 1.0f));
-		mGridColorParam.set(GRID_LINE_COLOR);
-		mGridSpacingParam.set(mSpacing);
-		mGridBorderWidthParam.set(LINE_BORDER_WIDTH);
-		mGridFadeOutStartParam.set(FADE_OUT_START);
-		mGridFadeOutEndParam.set(FADE_OUT_END);
-
-		gRendererUtility().setPass(mGridMaterial, 0);
-		gRendererUtility().draw(mGridMesh, mGridMesh->getProperties().getSubMesh(0));
-	}
+#include "BsSceneGrid.h"
+#include "BsMath.h"
+#include "BsShapeMeshes3D.h"
+#include "BsVertexDataDesc.h"
+#include "BsMaterial.h"
+#include "BsMesh.h"
+#include "BsBuiltinEditorResources.h"
+#include "BsCCamera.h"
+#include "BsRect3.h"
+#include "BsCoreThread.h"
+#include "BsEditorSettings.h"
+#include "BsRendererManager.h"
+#include "BsRenderer.h"
+#include "BsRendererUtility.h"
+
+namespace BansheeEngine
+{
+	const Color SceneGridCore::GRID_LINE_COLOR = Color(0.5f, 0.5f, 0.5f);
+	const float SceneGridCore::LINE_WIDTH = 0.025f;
+	const float SceneGridCore::LINE_BORDER_WIDTH = 0.00075f;
+	const float SceneGridCore::FADE_OUT_START = 5.0f;
+	const float SceneGridCore::FADE_OUT_END = 40.0f;
+
+	SceneGrid::SceneGrid(const CameraPtr& camera)
+		:mCoreDirty(true), mCore(nullptr)
+	{
+		mVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
+		mVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
+		mVertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL);
+
+		mCore.store(bs_new<SceneGridCore>(), std::memory_order_release);
+
+		HMaterial gridMaterial = BuiltinEditorResources::instance().createSceneGridMaterial();
+		SPtr<MaterialCore> materialCore = gridMaterial->getCore();
+		gCoreAccessor().queueCommand(std::bind(&SceneGrid::initializeCore, this, camera->getCore(), materialCore));
+
+		updateGridMesh();
+	}
+
+	SceneGrid::~SceneGrid()
+	{
+		gCoreAccessor().queueCommand(std::bind(&SceneGrid::destroyCore, this, mCore.load(std::memory_order_relaxed)));
+	}
+
+	void SceneGrid::initializeCore(const SPtr<CameraCore>& camera, const SPtr<MaterialCore>& material)
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		mCore.load()->initialize(camera, material);
+	}
+
+	void SceneGrid::destroyCore(SceneGridCore* core)
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		bs_delete(core);
+	}
+
+	void SceneGrid::setSize(UINT32 size)
+	{
+		if (mSize != size)
+		{
+			mSize = size;
+			updateGridMesh();
+		}
+	}
+
+	void SceneGrid::setSpacing(float spacing)
+	{
+		if (mSpacing != spacing)
+		{
+			mSpacing = spacing;
+			mCoreDirty = true;
+		}
+	}
+
+	void SceneGrid::setMode(GridMode mode)
+	{
+		if(mMode != mode)
+		{
+			mMode = mode;
+			updateGridMesh();
+		}
+	}
+
+	void SceneGrid::setSettings(const EditorSettingsPtr& settings)
+	{
+		mSettings = settings;
+		updateFromEditorSettings();
+	}
+
+	void SceneGrid::_update()
+	{
+		if (mSettings != nullptr && mSettingsHash != mSettings->getHash())
+			updateFromEditorSettings();
+
+		if (mCoreDirty)
+		{
+			SceneGridCore* core = mCore.load(std::memory_order_relaxed);
+			gCoreAccessor().queueCommand(
+				std::bind(&SceneGridCore::updateData, core, mGridMesh->getCore(), mSpacing, mMode == GridMode::Perspective));
+
+			mCoreDirty = false;
+		}
+	}
+
+	void SceneGrid::updateFromEditorSettings()
+	{
+		setSize(mSettings->getGridSize());
+		setSpacing(mSettings->getGridSpacing());
+
+		mSettingsHash = mSettings->getHash();
+	}
+
+	void SceneGrid::updateGridMesh()
+	{
+		std::array<Vector3, 2> axes;
+		Vector3 origin;
+
+		switch(mMode)
+		{
+		case GridMode::Perspective:
+			axes[0] = Vector3::UNIT_X;
+			axes[1] = Vector3::UNIT_Z;
+			origin = Vector3::ZERO;
+			break;
+		case GridMode::OrthoX:
+			axes[0] = Vector3::UNIT_Y;
+			axes[1] = Vector3::UNIT_Z;
+			origin = Vector3(500.0f, 0.0f, 0.0f);
+			break;
+		case GridMode::OrthoY:
+			axes[0] = Vector3::UNIT_X;
+			axes[1] = Vector3::UNIT_Z;
+			origin = Vector3(0.0f, 500.0f, 0.0f);
+			break;
+		case GridMode::OrthoZ:
+			axes[0] = Vector3::UNIT_X;
+			axes[1] = Vector3::UNIT_Y;
+			origin = Vector3(0.0f, 0.0f, 500.0f);
+			break;
+		case GridMode::OrthoNegX:
+			axes[0] = Vector3::UNIT_Y;
+			axes[1] = Vector3::UNIT_Z;
+			origin = Vector3(-500.0f, 0.0f, 0.0f);
+			break;
+		case GridMode::OrthoNegY:
+			axes[0] = Vector3::UNIT_X;
+			axes[1] = Vector3::UNIT_Z;
+			origin = Vector3(0.0f, -500.0f, 0.0f);
+			break;
+		case GridMode::OrthoNegZ:
+			axes[0] = Vector3::UNIT_X;
+			axes[1] = Vector3::UNIT_Y;
+			origin = Vector3(0.0f, 0.0f, -500.0f);
+			break;
+		}
+
+		axes[0] = Vector3::UNIT_X;
+		axes[1] = Vector3::UNIT_Z;
+
+		std::array<float, 2> extents;
+		extents[0] = mSize * 0.5f;
+		extents[1] = mSize * 0.5f;
+
+		Rect3 quad(origin, axes, extents);
+		MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(8, 12, mVertexDesc);
+
+		ShapeMeshes3D::solidQuad(quad, meshData, 0, 0);
+		mGridMesh = Mesh::create(meshData);
+		mCoreDirty = true;
+	}
+
+	SceneGridCore::~SceneGridCore()
+	{
+		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
+		activeRenderer->_unregisterRenderCallback(mCamera.get(), 5);
+	}
+
+	void SceneGridCore::initialize(const SPtr<CameraCore>& camera, const SPtr<MaterialCore>& material)
+	{
+		mCamera = camera;
+		mGridMaterial = material;
+
+		mViewProjParam = mGridMaterial->getParamMat4("matViewProj");
+		mWorldCameraPosParam = mGridMaterial->getParamVec4("worldCameraPos");
+		mGridColorParam = mGridMaterial->getParamColor("gridColor");
+		mGridSpacingParam = mGridMaterial->getParamFloat("gridSpacing");
+		mGridBorderWidthParam = mGridMaterial->getParamFloat("gridBorderWidth");
+		mGridFadeOutStartParam = mGridMaterial->getParamFloat("gridFadeOutStart");
+		mGridFadeOutEndParam = mGridMaterial->getParamFloat("gridFadeOutEnd");
+
+		CoreRendererPtr activeRenderer = RendererManager::instance().getActive();
+		activeRenderer->_registerRenderCallback(camera.get(), 5, std::bind(&SceneGridCore::render, this));			
+	}
+
+	void SceneGridCore::updateData(const SPtr<MeshCore>& mesh, float spacing, bool fadeGrid)
+	{
+		mGridMesh = mesh;
+		mSpacing = spacing;
+		mFadeGrid = fadeGrid;
+	}
+
+	void SceneGridCore::render()
+	{
+		THROW_IF_NOT_CORE_THREAD;
+
+		Matrix4 projMatrix = mCamera->getProjectionMatrixRS();
+		Matrix4 viewMatrix = mCamera->getViewMatrix();
+
+		Matrix4 viewProjMatrix = projMatrix * viewMatrix;
+		mViewProjParam.set(viewProjMatrix);
+
+		mWorldCameraPosParam.set(Vector4(mCamera->getPosition(), 1.0f));
+		mGridColorParam.set(GRID_LINE_COLOR);
+		mGridSpacingParam.set(mSpacing);
+		mGridBorderWidthParam.set(LINE_BORDER_WIDTH);
+
+		if (mFadeGrid)
+		{
+			mGridFadeOutStartParam.set(FADE_OUT_START);
+			mGridFadeOutEndParam.set(FADE_OUT_END);
+		}
+		else
+		{
+			mGridFadeOutStartParam.set(1000.0f);
+			mGridFadeOutEndParam.set(1500.0f);
+		}
+
+		gRendererUtility().setPass(mGridMaterial, 0);
+		gRendererUtility().draw(mGridMesh, mGridMesh->getProperties().getSubMesh(0));
+	}
 }

+ 4 - 0
MBansheeEditor/EditorApplication.cs

@@ -236,14 +236,18 @@ namespace BansheeEditor
             inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.S);
             inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.A);
             inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.D);
+            inputConfig.RegisterButton(SceneCamera.MoveUpBinding, ButtonCode.E);
+            inputConfig.RegisterButton(SceneCamera.MoveDownBinding, ButtonCode.Q);
             inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.Up);
             inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.Down);
             inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.Left);
             inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.Right);
             inputConfig.RegisterButton(SceneCamera.FastMoveBinding, ButtonCode.LeftShift);
             inputConfig.RegisterButton(SceneCamera.RotateBinding, ButtonCode.MouseRight);
+            inputConfig.RegisterButton(SceneCamera.PanBinding, ButtonCode.MouseMiddle);
             inputConfig.RegisterAxis(SceneCamera.HorizontalAxisBinding, InputAxis.MouseX);
             inputConfig.RegisterAxis(SceneCamera.VerticalAxisBinding, InputAxis.MouseY);
+            inputConfig.RegisterAxis(SceneCamera.ScrollAxisBinding, InputAxis.MouseZ);
 
             inputConfig.RegisterButton(SceneWindow.ToggleProfilerOverlayBinding, ButtonCode.P, ButtonModifier.CtrlAlt);
             inputConfig.RegisterButton(SceneWindow.ViewToolBinding, ButtonCode.Q);

+ 25 - 0
MBansheeEditor/EditorWindow.cs

@@ -27,11 +27,30 @@ namespace BansheeEditor
             get { return Internal_GetHeight(mCachedPtr); }
         }
 
+        /// <summary>
+        /// Screen space bounds of the window.
+        /// </summary>
+        public Rect2I Bounds
+        {
+            get
+            {
+                Rect2I bounds;
+                Internal_GetBounds(mCachedPtr, out bounds);
+                return bounds;
+            }
+        }
+
         /// <summary>
         /// Determines whether the editor window currently has keyboard focus (has been clicked on most recently).
         /// </summary>
         public bool HasFocus { get { return Internal_HasFocus(mCachedPtr); } }
 
+        /// <summary>
+        /// Checks if the window's tab is currently active. If the window is floating or not sharing space with any other
+        /// windows (just a single tab), it is always considered active.
+        /// </summary>
+        public bool Active { get { return Internal_IsActive(mCachedPtr); } }
+
         protected GUIPanel GUI;
 
         /// <summary>
@@ -121,6 +140,12 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_HasFocus(IntPtr nativeInstance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsActive(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetBounds(IntPtr nativeInstance, out Rect2I bounds);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_ScreenToWindowPos(IntPtr nativeInstance, ref Vector2I position, out Vector2I windowPos);
 

+ 21 - 2
MBansheeEditor/Scene/SceneAxesGUI.cs

@@ -11,9 +11,19 @@ namespace BansheeEditor
         private Camera camera;
         private SceneHandles sceneHandles;
 
+        private GUIPanel panel;
         private GUIRenderTexture renderTextureGUI;
+        private GUILabel labelGUI;
         private Rect2I bounds;
 
+        /// <summary>
+        /// Projection type to display on the GUI.
+        /// </summary>
+        public ProjectionType ProjectionType
+        {
+            set { labelGUI.SetContent(value.ToString()); }
+        }
+
         /// <summary>
         /// Creates a new scene axes GUI.
         /// </summary>
@@ -21,7 +31,8 @@ namespace BansheeEditor
         /// <param name="panel">Panel onto which to place the GUI element.</param>
         /// <param name="width">Width of the GUI element.</param>
         /// <param name="height">Height of the GUI element.</param>
-        public SceneAxesGUI(EditorWindow window, GUIPanel panel, int width, int height)
+        /// <param name="projType">Projection type to display on the GUI.</param>
+        public SceneAxesGUI(SceneWindow window, GUIPanel panel, int width, int height, ProjectionType projType)
         {
             renderTexture = new RenderTexture2D(PixelFormat.R8G8B8A8, width, height);
             renderTexture.Priority = 1;
@@ -44,12 +55,18 @@ namespace BansheeEditor
             camera.OrthoHeight = 2.0f;
 
             renderTextureGUI = new GUIRenderTexture(renderTexture, true);
-            panel.AddElement(renderTextureGUI);
+
+            GUILayoutY layout = panel.AddLayoutY();
+            layout.AddElement(renderTextureGUI);
 
             Rect2I bounds = new Rect2I(0, 0, width, height);
             sceneHandles = new SceneHandles(window, camera);
             renderTextureGUI.Bounds = bounds;
 
+            labelGUI = new GUILabel(projType.ToString(), EditorStyles.LabelCentered);
+            layout.AddElement(labelGUI);
+
+            this.panel = panel;
             this.bounds = bounds;
         }
 
@@ -92,6 +109,7 @@ namespace BansheeEditor
         {
             pointerPos.x -= bounds.x;
             pointerPos.y -= bounds.y;
+
             sceneHandles.UpdateInput(pointerPos, Input.PointerDelta);
         }
 
@@ -113,6 +131,7 @@ namespace BansheeEditor
             bounds.x = x;
             bounds.y = y;
             renderTextureGUI.Bounds = bounds;
+            panel.SetPosition(x, y);
         }
 
         /// <summary>

+ 12 - 4
MBansheeEditor/Scene/SceneAxesHandle.cs

@@ -219,18 +219,26 @@ namespace BansheeEditor
                 HandleDrawing.Color = Color.White;
 
             HandleDrawing.DrawCube(Vector3.Zero, new Vector3(BOX_EXTENT, BOX_EXTENT, BOX_EXTENT));
-
-            // 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)
+            SceneWindow sceneWindow = EditorWindow.GetWindow<SceneWindow>();
+            if (sceneWindow != null)
+                sceneWindow.LookAlong(axis);
         }
 
         private void ToggleProjectionType()
         {
-            // TODO - Switch between ortographic and perspective
+            SceneWindow sceneWindow = EditorWindow.GetWindow<SceneWindow>();
+            if (sceneWindow != null)
+            {
+                if (sceneWindow.ProjectionType == ProjectionType.Orthographic)
+                    sceneWindow.ProjectionType = ProjectionType.Perspective;
+                else
+                    sceneWindow.ProjectionType = ProjectionType.Orthographic;
+            }
+                
         }
     }
 }

+ 211 - 88
MBansheeEditor/Scene/SceneCamera.cs

@@ -8,30 +8,43 @@ namespace BansheeEditor
     [RunInEditor]
     internal sealed class SceneCamera : Component
     {
+        #region Constants
         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 MoveUpBinding = "SceneUp";
+        public const string MoveDownBinding = "SceneDown";
+        public const string FastMoveBinding = "SceneFastMove";
+        public const string PanBinding = "ScenePan";
+        public const string RotateBinding = "SceneRotate";
 	    public const string HorizontalAxisBinding = "SceneHorizontal";
 	    public const string VerticalAxisBinding = "SceneVertical";
+        public const string ScrollAxisBinding = "SceneScroll";
 
-	    private const float StartSpeed = 4.0f;
+        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 PanSpeed = 3.0f;
+        private const float ScrollSpeed = 3.0f;
 	    private const float RotationalSpeed = 360.0f; // Degrees/second
         private readonly Degree FieldOfView = 90.0f;
+        #endregion
 
+        #region Fields
         private VirtualButton moveForwardBtn;
         private VirtualButton moveLeftBtn;
         private VirtualButton moveRightBtn;
         private VirtualButton moveBackwardBtn;
+        private VirtualButton moveUpBtn;
+        private VirtualButton moveDownBtn;
         private VirtualButton fastMoveBtn;
         private VirtualButton activeBtn;
+        private VirtualButton panBtn;
         private VirtualAxis horizontalAxis;
         private VirtualAxis verticalAxis;
+        private VirtualAxis scrollAxis;
 
         private float currentSpeed;
         private Degree yaw;
@@ -45,7 +58,75 @@ namespace BansheeEditor
         private float frustumWidth = 50.0f;
         private float lerp;
         private bool isAnimating;
+        #endregion
 
+        #region Public properties
+        /// <summary>
+        /// Type of projection used by camera for rendering the scene.
+        /// </summary>
+        public ProjectionType ProjectionType
+        {
+            get { return camera.ProjectionType; }
+            set
+            {
+                if (camera.ProjectionType != value)
+                {
+                    CameraState state = new CameraState();
+                    state.Position = camera.SceneObject.Position;
+                    state.Rotation = camera.SceneObject.Rotation;
+                    state.Ortographic = value == ProjectionType.Orthographic;
+                    state.FrustumWidth = frustumWidth;
+
+                    SetState(state);
+                }
+            }
+        }
+        #endregion
+
+        #region Public methods
+        /// <summary>
+        /// Enables or disables camera controls.
+        /// </summary>
+        /// <param name="enable">True to enable controls, false to disable.</param>
+        public void EnableInput(bool enable)
+        {
+            inputEnabled = enable;
+        }
+
+        /// <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>
+        /// Orients the camera so it looks along the provided axis.
+        /// </summary>
+        public void LookAlong(Vector3 axis)
+        {
+            Vector3 up = Vector3.YAxis;
+            if (MathEx.Abs(Vector3.Dot(axis, up)) > 0.9f)
+                up = Vector3.ZAxis;
+
+            CameraState state = new CameraState();
+            state.Position = camera.SceneObject.Position;
+            state.Rotation = Quaternion.LookRotation(axis, up);
+            state.Ortographic = camera.ProjectionType == ProjectionType.Orthographic;
+            state.FrustumWidth = frustumWidth;
+
+            SetState(state);
+        }
+
+        #endregion
+
+        #region Private methods
         private void OnReset()
         {
             camera = SceneObject.GetComponent<Camera>();
@@ -54,113 +135,154 @@ namespace BansheeEditor
             moveLeftBtn = new VirtualButton(MoveLeftBinding);
             moveRightBtn = new VirtualButton(MoveRightBinding);
             moveBackwardBtn = new VirtualButton(MoveBackBinding);
+            moveUpBtn = new VirtualButton(MoveUpBinding);
+            moveDownBtn = new VirtualButton(MoveDownBinding);
             fastMoveBtn = new VirtualButton(FastMoveBinding);
             activeBtn = new VirtualButton(RotateBinding);
+            panBtn = new VirtualButton(PanBinding);
             horizontalAxis = new VirtualAxis(HorizontalAxisBinding);
             verticalAxis = new VirtualAxis(VerticalAxisBinding);
+            scrollAxis = new VirtualAxis(ScrollAxisBinding);
         }
 
         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();
+            bool isOrtographic = camera.ProjectionType == ProjectionType.Orthographic;
 
-                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
+            if (inputEnabled)
+            {
+                bool goingForward = VirtualInput.IsButtonHeld(moveForwardBtn);
+                bool goingBack = VirtualInput.IsButtonHeld(moveBackwardBtn);
+                bool goingLeft = VirtualInput.IsButtonHeld(moveLeftBtn);
+                bool goingRight = VirtualInput.IsButtonHeld(moveRightBtn);
+                bool goingUp = VirtualInput.IsButtonHeld(moveUpBtn);
+                bool goingDown = VirtualInput.IsButtonHeld(moveDownBtn);
+                bool fastMove = VirtualInput.IsButtonHeld(fastMoveBtn);
+                bool camActive = VirtualInput.IsButtonHeld(activeBtn);
+                bool isPanning = VirtualInput.IsButtonHeld(panBtn);
+
+                bool hideCursor = camActive || isPanning;
+                if (hideCursor != lastButtonState)
                 {
-                    currentSpeed = 0.0f;
+                    if (hideCursor)
+                    {
+                        Cursor.Hide();
+
+                        Rect2I clipRect;
+                        clipRect.x = Input.PointerPosition.x - 2;
+                        clipRect.y = Input.PointerPosition.y - 2;
+                        clipRect.width = 4;
+                        clipRect.height = 4;
+
+                        Cursor.ClipToRect(clipRect);
+                    }
+                    else
+                    {
+                        Cursor.Show();
+                        Cursor.ClipDisable();
+                    }
+
+                    lastButtonState = hideCursor;
                 }
 
-                const float tooSmall = 0.0001f;
-                if (currentSpeed > tooSmall)
+                float frameDelta = Time.FrameDelta;
+                if (camActive)
                 {
-                    Vector3 velocity = direction * currentSpeed;
-                    SceneObject.Move(velocity * frameDelta);
+                    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;
+
+                    // Handle movement using movement keys
+                    Vector3 direction = Vector3.Zero;
+
+                    if (!isOrtographic)
+                    {
+                        if (goingForward) direction += SceneObject.Forward;
+                        if (goingBack) direction -= SceneObject.Forward;
+                    }
+
+                    if (goingRight) direction += SceneObject.Right;
+                    if (goingLeft) direction -= SceneObject.Right;
+                    if (goingUp) direction += SceneObject.Up;
+                    if (goingDown) direction -= SceneObject.Up;
+
+                    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;
+                // Pan
+                if (isPanning)
+                {
+                    float horzValue = VirtualInput.GetAxisValue(horizontalAxis);
+                    float vertValue = VirtualInput.GetAxisValue(verticalAxis);
 
-            inputEnabled = enable;
+                    Vector3 direction = new Vector3(horzValue, -vertValue, 0.0f);
+                    direction = camera.SceneObject.Rotation.Rotate(direction);
 
-            if (!inputEnabled)
+                    SceneObject.Move(direction*PanSpeed*frameDelta);
+                }
+            }
+            else
             {
-                if (VirtualInput.IsButtonHeld(activeBtn))
-                    Cursor.Show();
+                Cursor.Show();
+                Cursor.ClipDisable();
             }
-        }
 
-        /// <summary>
-        /// Focuses the camera on the currently selected object(s).
-        /// </summary>
-        public void FrameSelected()
-        {
-            SceneObject[] selectedObjects = Selection.SceneObjects;
-            if (selectedObjects.Length > 0)
+            SceneWindow sceneWindow = EditorWindow.GetWindow<SceneWindow>();
+            if (sceneWindow.Active)
             {
-                AABox box = EditorUtility.CalculateBounds(Selection.SceneObjects);
-                FrameBounds(box);
+                Rect2I bounds = sceneWindow.Bounds;
+
+                // Move using scroll wheel
+                if (bounds.Contains(Input.PointerPosition))
+                {
+                    float scrollAmount = VirtualInput.GetAxisValue(scrollAxis);
+                    if (!isOrtographic)
+                    {
+                        SceneObject.Move(SceneObject.Forward*scrollAmount*ScrollSpeed);
+                    }
+                    else
+                    {
+                        float orthoHeight = MathEx.Max(1.0f, camera.OrthoHeight - scrollAmount);
+                        camera.OrthoHeight = orthoHeight;
+                    }
+                }
             }
+
+            UpdateAnim();
         }
 
         /// <summary>
@@ -385,5 +507,6 @@ namespace BansheeEditor
                 interpolated.FrustumWidth = start.FrustumWidth * (1.0f - t) + target.FrustumWidth * t;
             }
         };
+        #endregion 
     }
 }

+ 27 - 0
MBansheeEditor/Scene/SceneGrid.cs

@@ -4,6 +4,20 @@ using BansheeEngine;
 
 namespace BansheeEditor
 {
+    /// <summary>
+    /// Determines how is grid drawn.
+    /// </summary>
+    internal enum GridMode // Note: Must match C++ enum GridMode
+    {
+        Perspective,
+        OrthoX,
+        OrthoY,
+        OrthoZ,
+        OrthoNegX,
+        OrthoNegY,
+        OrthoNegZ
+    }
+
     /// <summary>
     /// Handles rendering of the scene grid for the specified camera. Grid properties are controlled through 
     /// <see cref="EditorSettings"/>.
@@ -27,10 +41,23 @@ namespace BansheeEditor
             Internal_Draw(mCachedPtr);
         }
 
+        /// <summary>
+        /// Changes how is the grid drawn.
+        /// </summary>
+        /// <param name="mode">Determines orientation and position of the grid so it suits the camera in the provided mode.
+        /// </param>
+        internal void SetMode(GridMode mode)
+        {
+            Internal_SetMode(mCachedPtr, mode);
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Create(SceneGrid managedInstance, IntPtr camera);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Draw(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMode(IntPtr thisPtr, GridMode mode);
     }
 }

+ 58 - 6
MBansheeEditor/Scene/SceneWindow.cs

@@ -1,9 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using BansheeEngine;
 
 namespace BansheeEditor
@@ -89,6 +86,15 @@ namespace BansheeEditor
             get { return camera; }
         }
 
+        /// <summary>
+        /// Determines scene camera's projection type.
+        /// </summary>
+        internal ProjectionType ProjectionType
+        {
+            get { return cameraController.ProjectionType; }
+            set { cameraController.ProjectionType = value; sceneAxesGUI.ProjectionType = value; }
+        }
+
         /// <summary>
         /// Constructs a new scene window.
         /// </summary>
@@ -252,7 +258,7 @@ namespace BansheeEditor
             rtPanel = mainPanel.AddPanel();
 
             GUIPanel sceneAxesPanel = mainPanel.AddPanel(-1);
-            sceneAxesGUI = new SceneAxesGUI(this, sceneAxesPanel, HandleAxesGUISize, HandleAxesGUISize);
+            sceneAxesGUI = new SceneAxesGUI(this, sceneAxesPanel, HandleAxesGUISize, HandleAxesGUISize, ProjectionType.Perspective);
 
             toggleProfilerOverlayKey = new VirtualButton(ToggleProfilerOverlayBinding);
             viewToolKey = new VirtualButton(ViewToolBinding);
@@ -278,6 +284,52 @@ namespace BansheeEditor
             sceneAxesGUI.Destroy();
             sceneAxesGUI = null;
         }
+
+        /// <summary>
+        /// Orients the camera so it looks along the provided axis.
+        /// </summary>
+        /// <param name="axis">Axis to look along.</param>
+        internal void LookAlong(Vector3 axis)
+        {
+            axis.Normalize();
+
+            cameraController.LookAlong(axis);
+            UpdateGridMode();
+        }
+
+        private void UpdateGridMode()
+        {
+            Vector3 forward = camera.SceneObject.Forward;
+
+            if (camera.ProjectionType == ProjectionType.Perspective)
+                sceneGrid.SetMode(GridMode.Perspective);
+            else
+            {
+                float dotX = Vector3.Dot(forward, Vector3.XAxis);
+                if (dotX >= 0.95f)
+                    sceneGrid.SetMode(GridMode.OrthoX);
+                else if (dotX <= -0.95f)
+                    sceneGrid.SetMode(GridMode.OrthoNegX);
+                else
+                {
+                    float dotY = Vector3.Dot(forward, Vector3.YAxis);
+                    if (dotY >= 0.95f)
+                        sceneGrid.SetMode(GridMode.OrthoY);
+                    else if (dotY <= -0.95f)
+                        sceneGrid.SetMode(GridMode.OrthoNegY);
+                    else
+                    {
+                        float dotZ = Vector3.Dot(forward, Vector3.ZAxis);
+                        if (dotZ >= 0.95f)
+                            sceneGrid.SetMode(GridMode.OrthoZ);
+                        else if (dotZ <= -0.95f)
+                            sceneGrid.SetMode(GridMode.OrthoNegZ);
+                        else
+                            sceneGrid.SetMode(GridMode.Perspective);
+                    }
+                }
+            }
+        }
         
         /// <summary>
         /// Converts screen coordinates into coordinates relative to the scene view render texture.
@@ -517,10 +569,10 @@ namespace BansheeEditor
 
             sceneSelection.Draw();
 
+            UpdateGridMode();
+
             if (VirtualInput.IsButtonDown(frameKey))
-            {
                 cameraController.FrameSelected();
-            }
         }
 
         /// <inheritdoc/>

+ 110 - 110
RenderBeast/Include/BsRenderTargets.h

@@ -1,111 +1,111 @@
-#pragma once
-
-#include "BsRenderBeastPrerequisites.h"
-#include "BsPixelUtil.h"
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Allocates and handles all the required render targets
-	 *			for rendering a scene from a specific viewport.
-	 *
-	 * @note	Core thread only.
-	 */
-	class BS_BSRND_EXPORT RenderTargets
-	{
-	public:
-		/**
-		 * @brief	Creates a new set of render targets. This will not actually allocate 
-		 *			the internal render targets - this happens the first time you call ::bind.
-		 *
-		 * @param	viewport		Viewport that the render targets will be used for. Determines size of the
-		 *							render targets, and the output color render target.
-		 * @param	hdr				Should the render targets support high dynamic range rendering.
-		 * @param	numSamples		Number of samples to use if multisampling is active. Provide 0 or 1 if
-		 *							multisampled targets are not needed.
-		 */
-		static SPtr<RenderTargets> create(const SPtr<ViewportCore>& viewport, bool hdr, UINT32 numSamples);
-
-		/**
-		 * @brief	Allocates the textures required for rendering. Allocations are pooled so this is generally a fast 
-		 * 			operation unless the size or other render target options changed. This must be called before binding
-		 * 			render targets.
-		 */
-		void allocate();
-
-		/**
-		 * @brief	Deallocates textures by returning them to the pool. This should be done when the caller is done using 
-		 * 			the render targets, so that other systems might re-use them. This will not release any memory unless
-		 * 			all render targets pointing to those textures go out of scope.
-		 */
-		void release();
-
-		/**
-		 * @brief	Binds the GBuffer render target for rendering.
-		 */
-		void bindGBuffer();
-
-		/**
-		 * @brief	Binds the scene color render target for rendering.
-		 */
-		void bindSceneColor();
-
-		/**
-		 * @brief	Resolves the GBuffer scene color into the output scene color buffer.
-		 */
-		void resolve();
-
-		/**
-		 * @brief	Returns the first color texture of the gbuffer as a bindable texture.
-		 */
-		SPtr<TextureCore> getTextureA() const;
-
-		/**
-		* @brief	Returns the second color texture of the gbuffer as a bindable texture.
-		*/
-		SPtr<TextureCore> getTextureB() const;
-
-		/**
-		 * @brief	Returns the depth texture of the gbuffer as a bindable texture.
-		 */
-		SPtr<TextureCore> getTextureDepth() const;
-
-		/**
-		 * @brief	Checks if the targets support HDR rendering.
-		 */
-		bool getHDR() const { return mHDR; }
-
-		/**
-		 * @brief	Returns the number of samples per pixel supported by the targets.
-		 */
-		UINT32 getNumSamples() const { return mNumSamples; }
-
-	private:
-		RenderTargets(const SPtr<ViewportCore>& viewport, bool hdr, UINT32 numSamples);
-
-		/**
-		 * @brief	Returns the width of gbuffer textures, in pixels.
-		 */
-		UINT32 getWidth() const;
-
-		/**
-		 * @brief	Returns the height of gbuffer textures, in pixels.
-		 */
-		UINT32 getHeight() const;
-
-		SPtr<ViewportCore> mViewport;
-
-		SPtr<PooledRenderTexture> mSceneColorTex;
-		SPtr<PooledRenderTexture> mAlbedoTex;
-		SPtr<PooledRenderTexture> mNormalTex;
-		SPtr<PooledRenderTexture> mDepthTex;
-
-		SPtr<MultiRenderTextureCore> mGBufferRT;
-		SPtr<RenderTextureCore> mSceneColorRT;
-
-		PixelFormat mDiffuseFormat;
-		PixelFormat mNormalFormat;
-		UINT32 mNumSamples;
-		bool mHDR;
-	};
+#pragma once
+
+#include "BsRenderBeastPrerequisites.h"
+#include "BsPixelUtil.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Allocates and handles all the required render targets
+	 *			for rendering a scene from a specific viewport.
+	 *
+	 * @note	Core thread only.
+	 */
+	class BS_BSRND_EXPORT RenderTargets
+	{
+	public:
+		/**
+		 * @brief	Creates a new set of render targets. This will not actually allocate 
+		 *			the internal render targets - this happens the first time you call ::bind.
+		 *
+		 * @param	viewport		Viewport that the render targets will be used for. Determines size of the
+		 *							render targets, and the output color render target.
+		 * @param	hdr				Should the render targets support high dynamic range rendering.
+		 * @param	numSamples		Number of samples to use if multisampling is active. Provide 0 or 1 if
+		 *							multisampled targets are not needed.
+		 */
+		static SPtr<RenderTargets> create(const SPtr<ViewportCore>& viewport, bool hdr, UINT32 numSamples);
+
+		/**
+		 * @brief	Allocates the textures required for rendering. Allocations are pooled so this is generally a fast 
+		 * 			operation unless the size or other render target options changed. This must be called before binding
+		 * 			render targets.
+		 */
+		void allocate();
+
+		/**
+		 * @brief	Deallocates textures by returning them to the pool. This should be done when the caller is done using 
+		 * 			the render targets, so that other systems might re-use them. This will not release any memory unless
+		 * 			all render targets pointing to those textures go out of scope.
+		 */
+		void release();
+
+		/**
+		 * @brief	Binds the GBuffer render target for rendering.
+		 */
+		void bindGBuffer();
+
+		/**
+		 * @brief	Binds the scene color render target for rendering.
+		 */
+		void bindSceneColor(bool readOnlyDepthStencil);
+
+		/**
+		 * @brief	Resolves the GBuffer scene color into the output scene color buffer.
+		 */
+		void resolve();
+
+		/**
+		 * @brief	Returns the first color texture of the gbuffer as a bindable texture.
+		 */
+		SPtr<TextureCore> getTextureA() const;
+
+		/**
+		* @brief	Returns the second color texture of the gbuffer as a bindable texture.
+		*/
+		SPtr<TextureCore> getTextureB() const;
+
+		/**
+		 * @brief	Returns the depth texture of the gbuffer as a bindable texture.
+		 */
+		SPtr<TextureCore> getTextureDepth() const;
+
+		/**
+		 * @brief	Checks if the targets support HDR rendering.
+		 */
+		bool getHDR() const { return mHDR; }
+
+		/**
+		 * @brief	Returns the number of samples per pixel supported by the targets.
+		 */
+		UINT32 getNumSamples() const { return mNumSamples; }
+
+	private:
+		RenderTargets(const SPtr<ViewportCore>& viewport, bool hdr, UINT32 numSamples);
+
+		/**
+		 * @brief	Returns the width of gbuffer textures, in pixels.
+		 */
+		UINT32 getWidth() const;
+
+		/**
+		 * @brief	Returns the height of gbuffer textures, in pixels.
+		 */
+		UINT32 getHeight() const;
+
+		SPtr<ViewportCore> mViewport;
+
+		SPtr<PooledRenderTexture> mSceneColorTex;
+		SPtr<PooledRenderTexture> mAlbedoTex;
+		SPtr<PooledRenderTexture> mNormalTex;
+		SPtr<PooledRenderTexture> mDepthTex;
+
+		SPtr<MultiRenderTextureCore> mGBufferRT;
+		SPtr<RenderTextureCore> mSceneColorRT;
+
+		PixelFormat mDiffuseFormat;
+		PixelFormat mNormalFormat;
+		UINT32 mNumSamples;
+		bool mHDR;
+	};
 }

+ 3 - 1
RenderBeast/Source/BsRenderBeast.cpp

@@ -589,7 +589,7 @@ namespace BansheeEngine
 				gRendererUtility().draw(iter->renderElem->mesh, iter->renderElem->subMesh);
 			}
 
-			camData.target->bindSceneColor();
+			camData.target->bindSceneColor(true);
 
 			// Render light pass
 			SPtr<GpuParamBlockBufferCore> perCameraBuffer = mStaticHandler->getPerCameraParams().getBuffer();
@@ -654,6 +654,8 @@ namespace BansheeEngine
 				SPtr<MeshCore> mesh = light.internal->getMesh();
 				gRendererUtility().draw(mesh, mesh->getProperties().getSubMesh(0));
 			}
+
+			camData.target->bindSceneColor(false);
 		}
 		else
 		{

+ 2 - 2
RenderBeast/Source/BsRenderTargets.cpp

@@ -128,10 +128,10 @@ namespace BansheeEngine
 		RenderAPICore::instance().clearViewport(clearBuffers, Color::ZERO, 1.0f, 0);
 	}
 
-	void RenderTargets::bindSceneColor()
+	void RenderTargets::bindSceneColor(bool readOnlyDepthStencil)
 	{
 		RenderAPICore& rapi = RenderAPICore::instance();
-		rapi.setRenderTarget(mSceneColorRT, true);
+		rapi.setRenderTarget(mSceneColorRT, readOnlyDepthStencil);
 
 		Rect2 area(0.0f, 0.0f, 1.0f, 1.0f);
 		rapi.setViewport(area);

+ 2 - 0
SBansheeEditor/Include/BsScriptEditorWindow.h

@@ -132,10 +132,12 @@ namespace BansheeEngine
 		static MonoObject* internal_getInstance(MonoString* ns, MonoString* typeName);
 
 		static bool internal_hasFocus(ScriptEditorWindow* thisPtr);
+		static bool internal_isActive(ScriptEditorWindow* thisPtr);
 		static void internal_screenToWindowPos(ScriptEditorWindow* thisPtr, Vector2I* screenPos, Vector2I* windowPos);
 		static void internal_windowToScreenPos(ScriptEditorWindow* thisPtr, Vector2I* windowPos, Vector2I* screenPos);
 		static UINT32 internal_getWidth(ScriptEditorWindow* thisPtr);
 		static UINT32 internal_getHeight(ScriptEditorWindow* thisPtr);
+		static void internal_getBounds(ScriptEditorWindow* thisPtr, Rect2I* bounds);
 	};
 
 	/**

+ 1 - 0
SBansheeEditor/Include/BsScriptSceneGrid.h

@@ -22,5 +22,6 @@ namespace BansheeEngine
 		/************************************************************************/
 		static void internal_Create(MonoObject* managedInstance, ScriptCamera* camera);
 		static void internal_Draw(ScriptSceneGrid* thisPtr);
+		static void internal_SetMode(ScriptSceneGrid* thisPtr, UINT32 mode);
 	};
 }

+ 27 - 0
SBansheeEditor/Source/BsScriptEditorWindow.cpp

@@ -49,7 +49,9 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetInstance", &ScriptEditorWindow::internal_getInstance);
 		metaData.scriptClass->addInternalCall("Internal_GetWidth", &ScriptEditorWindow::internal_getWidth);
 		metaData.scriptClass->addInternalCall("Internal_GetHeight", &ScriptEditorWindow::internal_getHeight);
+		metaData.scriptClass->addInternalCall("Internal_GetBounds", &ScriptEditorWindow::internal_getBounds);
 		metaData.scriptClass->addInternalCall("Internal_HasFocus", &ScriptEditorWindow::internal_hasFocus);
+		metaData.scriptClass->addInternalCall("Internal_IsActive", &ScriptEditorWindow::internal_isActive);
 		metaData.scriptClass->addInternalCall("Internal_ScreenToWindowPos", &ScriptEditorWindow::internal_screenToWindowPos);
 		metaData.scriptClass->addInternalCall("Internal_WindowToScreenPos", &ScriptEditorWindow::internal_windowToScreenPos);
 
@@ -174,6 +176,14 @@ namespace BansheeEngine
 			return false;
 	}
 
+	bool ScriptEditorWindow::internal_isActive(ScriptEditorWindow* thisPtr)
+	{
+		if (!thisPtr->isDestroyed())
+			return thisPtr->getEditorWidget()->isActive();
+		else
+			return false;
+	}
+
 	void ScriptEditorWindow::internal_screenToWindowPos(ScriptEditorWindow* thisPtr, Vector2I* screenPos, Vector2I* windowPos)
 	{
 		if (!thisPtr->isDestroyed())
@@ -206,6 +216,23 @@ namespace BansheeEngine
 			return 0;
 	}
 
+	void ScriptEditorWindow::internal_getBounds(ScriptEditorWindow* thisPtr, Rect2I* bounds)
+	{
+		if (!thisPtr->isDestroyed())
+		{
+			EditorWidgetBase* widget = thisPtr->getEditorWidget();
+			*bounds = thisPtr->getEditorWidget()->getBounds();
+			
+			Vector2I widgetPos(bounds->x, bounds->y);
+			Vector2I screenPos = widget->widgetToScreenPos(widgetPos);
+
+			bounds->x = screenPos.x;
+			bounds->y = screenPos.y;
+		}
+		else
+			*bounds = Rect2I();
+	}
+
 	void ScriptEditorWindow::onWidgetResized(UINT32 width, UINT32 height)
 	{
 		if (isDestroyed() || !mEditorWidget->isInitialized() || mManagedInstance == nullptr)

+ 7 - 1
SBansheeEditor/Source/BsScriptSceneGrid.cpp

@@ -21,6 +21,7 @@ namespace BansheeEngine
 	{
 		metaData.scriptClass->addInternalCall("Internal_Create", &ScriptSceneGrid::internal_Create);
 		metaData.scriptClass->addInternalCall("Internal_Draw", &ScriptSceneGrid::internal_Draw);
+		metaData.scriptClass->addInternalCall("Internal_SetMode", &ScriptSceneGrid::internal_SetMode);
 	}
 
 	void ScriptSceneGrid::internal_Create(MonoObject* managedInstance, ScriptCamera* camera)
@@ -30,6 +31,11 @@ namespace BansheeEngine
 
 	void ScriptSceneGrid::internal_Draw(ScriptSceneGrid* thisPtr)
 	{
-		thisPtr->mSceneGrid->update();
+		thisPtr->mSceneGrid->_update();
+	}
+
+	void ScriptSceneGrid::internal_SetMode(ScriptSceneGrid* thisPtr, UINT32 mode)
+	{
+		thisPtr->mSceneGrid->setMode((GridMode)mode);
 	}
 }