Prechádzať zdrojové kódy

WIP virtual axes and input refactor

Marko Pintera 11 rokov pred
rodič
commit
2be9a3371e

+ 10 - 0
BansheeCore/Include/BsInput.h

@@ -102,6 +102,16 @@ namespace BansheeEngine
 		 */
 		 */
 		float getVerticalAxis() const;
 		float getVerticalAxis() const;
 
 
+		/**
+		 * @brief	Returns value of the specified input axis in range [-1.0, 1.0].
+		 *
+		 * @param	device		Device from which to query the axis.
+		 * @param	type		Type of axis to query.
+		 * @param	deviceIdx	Index of the device in case more than one is hooked up.
+		 * @param	smooth		Should the returned value be smoothed.
+		 */
+		float getAxisValue(AxisDevice device, AxisType type, UINT32 deviceIdx = 0, bool smooth = false);
+
 		/**
 		/**
 		 * @brief	Query if the provided button is currently being held (this frame or previous frames).
 		 * @brief	Query if the provided button is currently being held (this frame or previous frames).
 		 */
 		 */

+ 24 - 0
BansheeCore/Include/BsInputFwd.h

@@ -374,4 +374,28 @@ namespace BansheeEngine
 	private:
 	private:
 		mutable bool mIsUsed;
 		mutable bool mIsUsed;
 	};
 	};
+
+	/**
+	 * @brief	Types of input devices that can return analog axis data.
+	 */
+	enum class AxisDevice
+	{
+		Mouse,
+		Gamepad,
+		Count // Keep at end
+	};
+
+	/**
+	 * @brief	Common input axis types.
+	 */
+	enum class AxisType
+	{
+		MainX, /**< Mouse X, Gamepad left stick X */
+		MainY, /**< Mouse Y, Gamepad left stick Y */
+		RightStickX, /**< Gamepad right stick X */
+		RightStickY, /**< Gamepad right stick Y */
+		LeftTrigger, /**< Gamepad left trigger */
+		RightTrigger, /**< Gamepad right trigger */
+		Count // Keep at end
+	};
 }
 }

+ 0 - 4
BansheeCore/Source/BsCoreApplication.cpp

@@ -209,14 +209,10 @@ namespace BansheeEngine
 	{
 	{
 		gProfilerCPU().beginThread("Core");
 		gProfilerCPU().beginThread("Core");
 		ProfilerGPU::instance().beginFrame();
 		ProfilerGPU::instance().beginFrame();
-		ProfilerGPU::instance().beginSample("DBG1");
-		ProfilerGPU::instance().beginSample("DBG2");
 	}
 	}
 
 
 	void CoreApplication::endCoreProfiling()
 	void CoreApplication::endCoreProfiling()
 	{
 	{
-		ProfilerGPU::instance().endSample("DBG2");
-		ProfilerGPU::instance().endSample("DBG1");
 		ProfilerGPU::instance().endFrame();
 		ProfilerGPU::instance().endFrame();
 		ProfilerGPU::instance()._update();
 		ProfilerGPU::instance()._update();
 
 

+ 6 - 0
BansheeCore/Source/BsInput.cpp

@@ -197,6 +197,12 @@ namespace BansheeEngine
 		return mSmoothVerticalAxis;
 		return mSmoothVerticalAxis;
 	}
 	}
 
 
+	float Input::getAxisValue(AxisDevice device, AxisType type, UINT32 deviceIdx, bool smooth)
+	{
+		// TODO
+		return 0.0f;
+	}
+
 	bool Input::isButtonHeld(ButtonCode button) const
 	bool Input::isButtonHeld(ButtonCode button) const
 	{
 	{
 		return mKeyState[button & 0x0000FFFF] == ButtonState::On || mKeyState[button & 0x0000FFFF] == ButtonState::ToggledOn;
 		return mKeyState[button & 0x0000FFFF] == ButtonState::On || mKeyState[button & 0x0000FFFF] == ButtonState::ToggledOn;

+ 63 - 3
BansheeEngine/Include/BsInputConfiguration.h

@@ -27,6 +27,21 @@ namespace BansheeEngine
 		bool repeatable;
 		bool repeatable;
 	};
 	};
 
 
+	struct VIRTUAL_AXIS_DESC
+	{
+		VIRTUAL_AXIS_DESC();
+		VIRTUAL_AXIS_DESC(AxisType type, AxisDevice device, float deadZone = 0.0001f, UINT32 deviceIndex = 0, 
+			float sensitivity = 1.0f, bool invert = false, bool smooth = true);
+
+		float deadZone;
+		float sensitivity;
+		bool invert;
+		bool smooth;
+		AxisDevice device;
+		AxisType type;
+		UINT32 deviceIndex;
+	};
+
 	/**
 	/**
 	 * @brief	Identifier for a virtual button. 
 	 * @brief	Identifier for a virtual button. 
 	 * 			
 	 * 			
@@ -54,8 +69,38 @@ namespace BansheeEngine
 		static UINT32 NextButtonId;
 		static UINT32 NextButtonId;
 	};
 	};
 
 
+	/**
+	 * @brief	Identifier for a virtual axis. 
+	 * 			
+	 * @note	Primary purpose of this class is to avoid expensive string compare (i.e. axis names),
+	 * 			and instead use a unique axis identifier for compare. Generally you want to create 
+	 * 			one of these using the axis name, and then store it for later use. 
+	 * 			
+	 *			This class is not thread safe and should only be used on the sim thread.
+	 */
+	class BS_EXPORT VirtualAxis
+	{
+	public:
+		VirtualAxis();
+		VirtualAxis(const String& name);
+
+		UINT32 axisIdentifier;
+
+		bool operator== (const VirtualAxis& rhs) const
+		{
+			return (axisIdentifier == rhs.axisIdentifier);
+		}
+
+	private:
+		static Map<String, UINT32> UniqueAxisIds;
+		static UINT32 NextAxisId;
+	};
+
 	class BS_EXPORT InputConfiguration
 	class BS_EXPORT InputConfiguration
 	{
 	{
+		static const int MAX_NUM_DEVICES_PER_TYPE = 8;
+		static const int MAX_NUM_DEVICES = (UINT32)AxisDevice::Count * MAX_NUM_DEVICES_PER_TYPE;
+
 		struct VirtualButtonData
 		struct VirtualButtonData
 		{
 		{
 			String name;
 			String name;
@@ -63,20 +108,35 @@ namespace BansheeEngine
 			VIRTUAL_BUTTON_DESC desc;
 			VIRTUAL_BUTTON_DESC desc;
 		};
 		};
 
 
+		struct VirtualAxisData
+		{
+			String name;
+			VirtualAxis axis;
+			VIRTUAL_AXIS_DESC desc;
+		};
+
+		struct DeviceAxisData
+		{
+			VirtualAxisData axes[(UINT32)AxisType::Count];
+		};
+
 	public:
 	public:
 		InputConfiguration();
 		InputConfiguration();
 
 
 		void registerButton(const String& name, ButtonCode buttonCode, VButtonModifier modifiers = VButtonModifier::None, bool repeatable = false);
 		void registerButton(const String& name, ButtonCode buttonCode, VButtonModifier modifiers = VButtonModifier::None, bool repeatable = false);
 		void unregisterButton(const String& name);
 		void unregisterButton(const String& name);
 
 
+		void registerAxis(const String& name, const VIRTUAL_AXIS_DESC& desc);
+		void unregisterAxis(const String& name);
+
 		void setRepeatInterval(UINT64 milliseconds) { mRepeatInterval = milliseconds; }
 		void setRepeatInterval(UINT64 milliseconds) { mRepeatInterval = milliseconds; }
 		UINT64 getRepeatInterval() const { return mRepeatInterval; }
 		UINT64 getRepeatInterval() const { return mRepeatInterval; }
 
 
-		bool getButton(ButtonCode code, UINT32 modifiers, VirtualButton& btn, VIRTUAL_BUTTON_DESC& btnDesc) const;
-
-		// TODO - registerAxis
+		bool _getButton(ButtonCode code, UINT32 modifiers, VirtualButton& btn, VIRTUAL_BUTTON_DESC& btnDesc) const;
+		bool _getAxis(const VirtualAxis& axis, VIRTUAL_AXIS_DESC& axisDesc) const;
 	private:
 	private:
 		Vector<VirtualButtonData> mButtons[BC_Count];
 		Vector<VirtualButtonData> mButtons[BC_Count];
+		Vector<VirtualAxisData> mAxes;
 
 
 		UINT64 mRepeatInterval;
 		UINT64 mRepeatInterval;
 	};
 	};

+ 2 - 0
BansheeEngine/Include/BsVirtualInput.h

@@ -42,6 +42,8 @@ namespace BansheeEngine
 		bool isButtonUp(const VirtualButton& button) const;
 		bool isButtonUp(const VirtualButton& button) const;
 		bool isButtonHeld(const VirtualButton& button) const;
 		bool isButtonHeld(const VirtualButton& button) const;
 
 
+		float getAxisValue(const VirtualAxis& axis) const;
+
 		void update();
 		void update();
 
 
 		Event<void(const VirtualButton&)> onButtonDown;
 		Event<void(const VirtualButton&)> onButtonDown;

+ 65 - 1
BansheeEngine/Source/BsInputConfiguration.cpp

@@ -5,6 +5,9 @@ namespace BansheeEngine
 	Map<String, UINT32> VirtualButton::UniqueButtonIds;
 	Map<String, UINT32> VirtualButton::UniqueButtonIds;
 	UINT32 VirtualButton::NextButtonId = 0;
 	UINT32 VirtualButton::NextButtonId = 0;
 
 
+	Map<String, UINT32> VirtualAxis::UniqueAxisIds;
+	UINT32 VirtualAxis::NextAxisId = 0;
+
 	VIRTUAL_BUTTON_DESC::VIRTUAL_BUTTON_DESC()
 	VIRTUAL_BUTTON_DESC::VIRTUAL_BUTTON_DESC()
 		:buttonCode(BC_0), modifiers(VButtonModifier::None), repeatable(false)
 		:buttonCode(BC_0), modifiers(VButtonModifier::None), repeatable(false)
 	{ }
 	{ }
@@ -13,6 +16,17 @@ namespace BansheeEngine
 		:buttonCode(buttonCode), modifiers(modifiers), repeatable(repeatable)
 		:buttonCode(buttonCode), modifiers(modifiers), repeatable(repeatable)
 	{ }
 	{ }
 
 
+	VIRTUAL_AXIS_DESC::VIRTUAL_AXIS_DESC()
+		: type(AxisType::MainX), device(AxisDevice::Mouse), deviceIndex(0), deadZone(0.0001f),
+		sensitivity(1.0f), invert(false), smooth(true)
+	{ }
+
+	VIRTUAL_AXIS_DESC::VIRTUAL_AXIS_DESC(AxisType type, AxisDevice device, float deadZone, UINT32 deviceIndex, 
+		float sensitivity, bool invert, bool smooth)
+		:type(type), device(device), deadZone(deadZone), deviceIndex(deviceIndex), sensitivity(sensitivity),
+		invert(invert), smooth(smooth)
+	{ }
+
 	VirtualButton::VirtualButton()
 	VirtualButton::VirtualButton()
 		:buttonIdentifier(0)
 		:buttonIdentifier(0)
 	{ }
 	{ }
@@ -30,6 +44,23 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	VirtualAxis::VirtualAxis()
+		:axisIdentifier(0)
+	{ }
+
+	VirtualAxis::VirtualAxis(const String& name)
+	{
+		auto findIter = UniqueAxisIds.find(name);
+
+		if (findIter != UniqueAxisIds.end())
+			axisIdentifier = findIter->second;
+		else
+		{
+			axisIdentifier = NextAxisId;
+			UniqueAxisIds[name] = NextAxisId++;
+		}
+	}
+
 	InputConfiguration::InputConfiguration()
 	InputConfiguration::InputConfiguration()
 		:mRepeatInterval(300)
 		:mRepeatInterval(300)
 	{ }
 	{ }
@@ -84,7 +115,31 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
-	bool InputConfiguration::getButton(ButtonCode code, UINT32 modifiers, VirtualButton& btn, VIRTUAL_BUTTON_DESC& btnDesc) const
+	void InputConfiguration::registerAxis(const String& name, const VIRTUAL_AXIS_DESC& desc)
+	{
+		VirtualAxis axis(name);
+
+		if (axis.axisIdentifier >= (UINT32)mAxes.size())
+			mAxes.resize(axis.axisIdentifier + 1);
+
+		mAxes[axis.axisIdentifier].name = name;
+		mAxes[axis.axisIdentifier].desc = desc;
+		mAxes[axis.axisIdentifier].axis = axis;
+	}
+
+	void InputConfiguration::unregisterAxis(const String& name)
+	{
+		for (UINT32 i = 0; i < (UINT32)mAxes.size(); i++)
+		{
+			if (mAxes[i].name == name)
+			{
+				mAxes.erase(mAxes.begin() + i);
+				i--;
+			}
+		}
+	}
+
+	bool InputConfiguration::_getButton(ButtonCode code, UINT32 modifiers, VirtualButton& btn, VIRTUAL_BUTTON_DESC& btnDesc) const
 	{
 	{
 		const Vector<VirtualButtonData>& btnData = mButtons[code & 0x0000FFFF];
 		const Vector<VirtualButtonData>& btnData = mButtons[code & 0x0000FFFF];
 
 
@@ -100,4 +155,13 @@ namespace BansheeEngine
 
 
 		return false;
 		return false;
 	}
 	}
+
+	bool InputConfiguration::_getAxis(const VirtualAxis& axis, VIRTUAL_AXIS_DESC& axisDesc) const
+	{
+		if (axis.axisIdentifier >= (UINT32)mAxes.size())
+			return false;
+
+		axisDesc = mAxes[axis.axisIdentifier].desc;
+		return true;
+	}
 }
 }

+ 30 - 2
BansheeEngine/Source/BsVirtualInput.cpp

@@ -1,5 +1,6 @@
 #include "BsVirtualInput.h"
 #include "BsVirtualInput.h"
 #include "BsInput.h"
 #include "BsInput.h"
+#include "BsMath.h"
 #include "BsTime.h"
 #include "BsTime.h"
 
 
 using namespace std::placeholders;
 using namespace std::placeholders;
@@ -58,6 +59,33 @@ namespace BansheeEngine
 		return false;
 		return false;
 	}
 	}
 
 
+	float VirtualInput::getAxisValue(const VirtualAxis& axis) const
+	{
+		VIRTUAL_AXIS_DESC axisDesc;
+		if (mInputConfiguration->_getAxis(axis, axisDesc))
+		{
+			float axisValue = gInput().getAxisValue(axisDesc.device, axisDesc.type, axisDesc.deviceIndex, axisDesc.smooth);
+
+			if (axisDesc.deadZone > 0.0f)
+			{
+				// Scale to [-1, 1] range after removing the dead zone
+				if (axisValue > 0)
+					axisValue = std::max(0.f, axisValue - axisDesc.deadZone) / (1.0f - axisDesc.deadZone);
+				else
+					axisValue = -std::max(0.f, -axisValue - axisDesc.deadZone) / (1.0f - axisDesc.deadZone);
+			}
+
+			axisValue = Math::clamp(axisValue * axisDesc.sensitivity, -1.0f, 1.0f);
+
+			if (axisDesc.invert)
+				axisValue = -axisValue;
+
+			return axisValue;
+		}
+
+		return 0.0f;
+	}
+
 	void VirtualInput::update()
 	void VirtualInput::update()
 	{
 	{
 		for(auto& state : mCachedStates)
 		for(auto& state : mCachedStates)
@@ -132,7 +160,7 @@ namespace BansheeEngine
 		{
 		{
 			VirtualButton btn;
 			VirtualButton btn;
 			VIRTUAL_BUTTON_DESC btnDesc;
 			VIRTUAL_BUTTON_DESC btnDesc;
-			if(mInputConfiguration->getButton(event.buttonCode, mActiveModifiers, btn, btnDesc))
+			if(mInputConfiguration->_getButton(event.buttonCode, mActiveModifiers, btn, btnDesc))
 			{
 			{
 				ButtonData& data = mCachedStates[btn.buttonIdentifier];
 				ButtonData& data = mCachedStates[btn.buttonIdentifier];
 
 
@@ -162,7 +190,7 @@ namespace BansheeEngine
 		{
 		{
 			VirtualButton btn;
 			VirtualButton btn;
 			VIRTUAL_BUTTON_DESC btnDesc;
 			VIRTUAL_BUTTON_DESC btnDesc;
-			if(mInputConfiguration->getButton(event.buttonCode, mActiveModifiers, btn, btnDesc))
+			if(mInputConfiguration->_getButton(event.buttonCode, mActiveModifiers, btn, btnDesc))
 			{
 			{
 				ButtonData& data = mCachedStates[btn.buttonIdentifier];
 				ButtonData& data = mCachedStates[btn.buttonIdentifier];
 
 

+ 11 - 23
Polish.txt

@@ -3,8 +3,6 @@ Polish TODO:
  - Fix FBX importer so proper number of vertices show
  - Fix FBX importer so proper number of vertices show
  - Test and fix full-screen transitions
  - Test and fix full-screen transitions
  - Add virtual input axes
  - Add virtual input axes
-   - Update Input so it can return multiple joystick and mouse axes
-   - Add better smoothing (look into UE4)
  - Compress and generate mips for texture on input (testing NVTT stuff)
  - Compress and generate mips for texture on input (testing NVTT stuff)
  - Add frustum culling and sorting code
  - Add frustum culling and sorting code
  - Finalize example
  - Finalize example
@@ -14,28 +12,20 @@ Polish TODO:
  - Make a separate release branch with no editor/script stuff, and without .txt files and other development data
  - Make a separate release branch with no editor/script stuff, and without .txt files and other development data
  - (HIGHLY OPTIONAL) Make a Getting Started guide, along with the example project. Or just finish up the manual.
  - (HIGHLY OPTIONAL) Make a Getting Started guide, along with the example project. Or just finish up the manual.
 
 
- Input:
- Modify Input so it  has:
- - getHorizonalAxis(Device, Index)
- - getVerticalAxis(Device, Index)
-
-Where Device is Mouse, Gamepad, etc.
-Index is index of the device (e.g. 1 and 2 if two Gamepads are connected)
-
-Then in VirtualInput I should add VirtualAxis
- - Represented by device, axis index and optionally other properties
- - Virtual axes are registered with InputConfiguration same as virtual buttons
-
 Rename Joystick -> Gamepad
 Rename Joystick -> Gamepad
-
 OIS has getNumberOfDevices and listFreeDevices that I don't currently use. I should use that for Gamepad input.
 OIS has getNumberOfDevices and listFreeDevices that I don't currently use. I should use that for Gamepad input.
 
 
-Optional VirtualInput axis data
- - Axis sensitivity
- - Axis invert
- - Dead zone, exponent (See UE4 PlayerInput.cpp MassageAxisInput)
- - Should smoothing be part of this as well? Probably, especially if I plan to add fake axes like keyboard ones
-  - See http://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtml
+Refactor Input so that axis state isn't received in integers but in [-1.0, 1.0] float range
+Refactor input so X and Y axes are received separately
+Refactor OIS input so it properly weights Mouse state according to MouseState.width, MouseState.height
+Rebuild OIS with XINPUT support (OIS_WIN32_XINPUT_SUPPORT)
+Rename CamelotOIS to BansheeOIS
+Add Joystick support to OIS input handler and make sure the axes are properly weighed in OISJoystick::MIN_AXIS, OISJoystick::MAX_AXIS range.
+Add smoothing
+  - See http://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtml or UE4 implementation
+
+Ensure that all button methods accept a DeviceIdx so that multiple joysticks can work.
+ - When button events are triggered from Input and VirtualInput include device type and index in them
 
 
 -----------------
 -----------------
 
 
@@ -61,8 +51,6 @@ DISREGARD MONITOR INDEX ON DX9
 
 
  -----------------------------
  -----------------------------
 
 
-Rename CamelotOIS external library
-
 Refactor GUIElement & GUIElementBase
 Refactor GUIElement & GUIElementBase
  - Sprite rendering methods should probably be marked as internal (possibly others too)
  - Sprite rendering methods should probably be marked as internal (possibly others too)
   - A lot could be made private
   - A lot could be made private