Browse Source

Moving Input and VirtualInput to C# (WIP, having some crashes)

Marko Pintera 11 years ago
parent
commit
7a5bfc0969

+ 1 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -103,6 +103,7 @@ namespace BansheeEngine
 	typedef std::shared_ptr<RenderableProxy> RenderableProxyPtr;
 	typedef std::shared_ptr<RenderableProxy> RenderableProxyPtr;
 	typedef std::shared_ptr<CameraHandler> CameraHandlerPtr;
 	typedef std::shared_ptr<CameraHandler> CameraHandlerPtr;
 	typedef std::shared_ptr<RenderableHandler> RenderableHandlerPtr;
 	typedef std::shared_ptr<RenderableHandler> RenderableHandlerPtr;
+	typedef std::shared_ptr<InputConfiguration> InputConfigurationPtr;
 
 
 	typedef GameObjectHandle<GUIWidget> HGUIWidget;
 	typedef GameObjectHandle<GUIWidget> HGUIWidget;
 	typedef GameObjectHandle<Camera> HCamera;
 	typedef GameObjectHandle<Camera> HCamera;

+ 366 - 0
MBansheeEngine/Input.cs

@@ -0,0 +1,366 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace BansheeEngine
+{
+	public struct ButtonEvent
+	{
+		internal ButtonCode buttonCode;
+        internal int deviceIdx;
+
+	    internal ButtonEvent(ButtonCode buttonCode, int deviceIdx)
+	    {
+	        this.buttonCode = buttonCode;
+	        this.deviceIdx = deviceIdx;
+	    }
+
+        public ButtonCode Button { get { return buttonCode; } }
+        public int DeviceIndex { get { return deviceIdx; } }
+
+		public bool IsKeyboard { get { return ((int)buttonCode & 0xC0000000) == 0; }}
+        public bool IsMouse { get { return ((int)buttonCode & 0x80000000) != 0; } }
+        public bool IsGamepad { get { return ((int)buttonCode & 0x40000000) != 0; } }
+	};
+
+    // Do not change IDs, must match PointerEventButton C++ enum
+	public enum PointerEventButton
+	{
+		Left, Middle, Right, Count
+	};
+
+    public struct PointerEvent
+    {
+        internal Vector2I screenPos;
+        internal PointerEventButton button;
+
+        internal bool shift;
+        internal bool control;
+        internal bool alt;
+
+        internal float mouseWheelScrollAmount;
+
+        internal PointerEvent(Vector2I screenPos, PointerEventButton button,
+            bool shift, bool control, bool alt, float mouseWheelScrollAmount)
+        {
+            this.screenPos = screenPos;
+            this.button = button;
+
+            this.shift = shift;
+            this.control = control;
+            this.alt = alt;
+
+            this.mouseWheelScrollAmount = mouseWheelScrollAmount;
+        }
+
+        public Vector2I ScreenPos { get { return screenPos; } }
+        public PointerEventButton Button { get { return button; } }
+
+        public bool Shift { get { return shift; } }
+        public bool Control { get { return control; } }
+        public bool Alt { get { return alt; } }
+
+        public float ScrollAmount { get { return mouseWheelScrollAmount; } }
+    }
+
+    public struct TextInputEvent
+    {
+        internal int textChar;
+
+        internal TextInputEvent(int textChar)
+        {
+            this.textChar = textChar;
+        }
+
+        public int Char { get { return textChar; } }
+    }
+
+    public static class Input
+    {
+        public delegate void ButtonEventDelegate(ButtonEvent ev);
+        public delegate void TextInputEventDelegate(TextInputEvent ev);
+        public delegate void PointerEventDelegate(PointerEvent ev);
+
+		public static event ButtonEventDelegate OnButtonDown;
+        public static event ButtonEventDelegate OnButtonUp;
+        public static event TextInputEventDelegate OnCharInput;
+        public static event PointerEventDelegate OnPointerMoved;
+        public static event PointerEventDelegate OnPointerPressed;
+        public static event PointerEventDelegate OnPointerReleased;
+        public static event PointerEventDelegate OnPointerDoubleClick;
+
+        public static float GetAxisValue(InputAxis axis, int deviceIdx = 0)
+        {
+            return Internal_GetAxisValue(axis, deviceIdx);
+        }
+
+        public static bool IsButtonHeld(ButtonCode code, int deviceIdx = 0)
+        {
+            return Internal_IsButtonHeld(code, deviceIdx);
+        }
+
+        public static bool IsButtonUp(ButtonCode code, int deviceIdx = 0)
+        {
+            return Internal_IsButtonUp(code, deviceIdx);
+        }
+
+        public static bool IsButtonDown(ButtonCode code, int deviceIdx = 0)
+        {
+            return Internal_IsButtonDown(code, deviceIdx);
+        }
+
+        private static void Internal_TriggerButtonDown(ButtonCode code, int deviceIdx)
+        {
+            ButtonEvent ev = new ButtonEvent(code, deviceIdx);
+
+            if (OnButtonDown != null)
+                OnButtonDown(ev);
+        }
+
+        private static void Internal_TriggerButtonUp(ButtonCode code, int deviceIdx)
+        {
+            ButtonEvent ev = new ButtonEvent(code, deviceIdx);
+
+            if (OnButtonUp != null)
+                OnButtonUp(ev);
+        }
+
+        private static void Internal_TriggerCharInput(int textChar)
+        {
+            TextInputEvent ev = new TextInputEvent(textChar);
+
+            if (OnCharInput != null)
+                OnCharInput(ev);
+        }
+
+        private static void Internal_TriggerPointerMove(Vector2I screenPos, PointerEventButton button, bool shift, 
+            bool ctrl, bool alt, float scrollAmount)
+        {
+            PointerEvent ev = new PointerEvent(screenPos, button, shift, ctrl, alt, scrollAmount);
+
+            if (OnPointerMoved != null)
+                OnPointerMoved(ev);
+        }
+
+        private static void Internal_TriggerPointerPressed(Vector2I screenPos, PointerEventButton button, bool shift,
+            bool ctrl, bool alt, float scrollAmount)
+        {
+            PointerEvent ev = new PointerEvent(screenPos, button, shift, ctrl, alt, scrollAmount);
+
+            if (OnPointerPressed != null)
+                OnPointerPressed(ev);
+        }
+
+        private static void Internal_TriggerPointerReleased(Vector2I screenPos, PointerEventButton button, bool shift,
+            bool ctrl, bool alt, float scrollAmount)
+        {
+            PointerEvent ev = new PointerEvent(screenPos, button, shift, ctrl, alt, scrollAmount);
+
+            if (OnPointerReleased != null)
+                OnPointerReleased(ev);
+        }
+
+        private static void Internal_TriggerPointerDoubleClick(Vector2I screenPos, PointerEventButton button, bool shift,
+            bool ctrl, bool alt, float scrollAmount)
+        {
+            PointerEvent ev = new PointerEvent(screenPos, button, shift, ctrl, alt, scrollAmount);
+
+            if (OnPointerDoubleClick != null)
+                OnPointerDoubleClick(ev);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetAxisValue(InputAxis axis, int deviceIdx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsButtonHeld(ButtonCode keyCode, int deviceIdx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsButtonUp(ButtonCode keyCode, int deviceIdx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsButtonDown(ButtonCode keyCode, int deviceIdx);
+    }
+
+    // Do not change IDs, must match ButtonCode C++ enum
+    public enum ButtonCode : uint
+    {
+        Unassigned = 0x00,
+        Escape = 0x01,
+        Num1 = 0x02,
+        Num2 = 0x03,
+        Num3 = 0x04,
+        Num4 = 0x05,
+        Num5 = 0x06,
+        Num6 = 0x07,
+        Num7 = 0x08,
+        Num8 = 0x09,
+        Num9 = 0x0A,
+        Num0 = 0x0B,
+        Minus = 0x0C, 
+        Equals = 0x0D,
+        Back = 0x0E,
+        Tab = 0x0F,
+        Q = 0x10,
+        W = 0x11,
+        E = 0x12,
+        R = 0x13,
+        T = 0x14,
+        Y = 0x15,
+        U = 0x16,
+        I = 0x17,
+        O = 0x18,
+        P = 0x19,
+        LeftBracket = 0x1A,
+        RightBracket = 0x1B,
+        Return = 0x1C,
+        LeftControl = 0x1D,
+        A = 0x1E,
+        S = 0x1F,
+        D = 0x20,
+        F = 0x21,
+        G = 0x22,
+        H = 0x23,
+        J = 0x24,
+        K = 0x25,
+        L = 0x26,
+        Semicolon = 0x27,
+        Apostrophe = 0x28,
+        Grave = 0x29,
+        LeftShift = 0x2A,
+        Backslash = 0x2B,
+        Z = 0x2C,
+        X = 0x2D,
+        C = 0x2E,
+        V = 0x2F,
+        B = 0x30,
+        N = 0x31,
+        M = 0x32,
+        Comma = 0x33,
+        Period = 0x34, 
+        Slash = 0x35,
+        RightShift = 0x36,
+        KeypadMultiply = 0x37,
+        LeftMenu = 0x38,
+        Space = 0x39,
+        CapsLock = 0x3A,
+        F1 = 0x3B,
+        F2 = 0x3C,
+        F3 = 0x3D,
+        F4 = 0x3E,
+        F5 = 0x3F,
+        F6 = 0x40,
+        F7 = 0x41,
+        F8 = 0x42,
+        F9 = 0x43,
+        F10 = 0x44,
+        NumLock = 0x45,
+        ScrollLock = 0x46,
+        Keypad7 = 0x47,
+        Keypad8 = 0x48,
+        Keypad9 = 0x49,
+        KeypadSubtract = 0x4A,
+        Keypad4 = 0x4B,
+        Keypad5 = 0x4C,
+        Keypad6 = 0x4D,
+        KeypadAdd = 0x4E,
+        Keypad1 = 0x4F,
+        Keypad2 = 0x50,
+        Keypad3 = 0x51,
+        Keypad0 = 0x52,
+        KeypadDecimal = 0x53,
+        F11 = 0x57,
+        F12 = 0x58,
+        F13 = 0x64,
+        F14 = 0x65,
+        F15 = 0x66,
+        KeypadEquals = 0x8D,
+        At = 0x91,
+        Colon = 0x92,
+        NumpadEnter = 0x9C,
+        RightControl = 0x9D,
+        KeypadComma = 0xB3,
+        KeypadDivide = 0xB5,
+        RightMenu = 0xB8,
+        Pause = 0xC5,
+        Home = 0xC7,
+        Up = 0xC8,
+        PageUp = 0xC9,
+        Left = 0xCB,
+        Right = 0xCD,
+        End = 0xCF,
+        Down = 0xD0,
+        PageDown = 0xD1,
+        Insert = 0xD2,
+        Delete = 0xD3,
+        LeftWindows = 0xDB,
+        RightWindows = 0xDC,
+        MouseLeft = 0x800000EE,
+        MouseRight = 0x800000EF,
+        MouseMiddle = 0x800000F0,
+        MouseBtn4 = 0x800000F1,
+        MouseBtn5 = 0x800000F2,
+        MouseBtn6 = 0x800000F3,
+        MouseBtn7 = 0x800000F4,
+        MouseBtn8 = 0x800000F5,
+        MouseBtn9 = 0x800000F6,
+        MouseBtn10 = 0x800000F7,
+        MouseBtn11 = 0x800000F8,
+        MouseBtn12 = 0x800000F9,
+        MouseBtn13 = 0x800000FA,
+        MouseBtn14 = 0x800000FB,
+        MouseBtn15 = 0x800000FC,
+        MouseBtn16 = 0x800000FD,
+        GamepadA = 0x4000010F,
+        GamepadB = 0x40000110,
+        GamepadX = 0x40000111,
+        GamepadY = 0x40000112,
+        GamepadLB = 0x40000113,
+        GamepadRB = 0x40000114,
+        GamepadLS = 0x40000115,
+        GamepadRS = 0x40000116,
+        GamepadBack = 0x40000117,
+        GamepadStart = 0x40000118,
+        GamepadDPadLeft = 0x40000119,
+        GamepadDPadRight = 0x4000011A,
+        GamepadDPadUp = 0x4000011B,
+        GamepadDPatDown = 0x4000011C,
+        GamepadBtn1 = 0x4000011D,
+        GamepadBtn2 = 0x4000011E,
+        GamepadBtn3 = 0x4000011F,
+        GamepadBtn4 = 0x40000120,
+        GamepadBtn5 = 0x40000121,
+        GamepadBtn6 = 0x40000122,
+        GamepadBtn7 = 0x40000123,
+        GamepadBtn8 = 0x40000124,
+        GamepadBtn9 = 0x40000125,
+        GamepadBtn10 = 0x40000126,
+        GamepadBtn11 = 0x40000127,
+        GamepadBtn12 = 0x40000128,
+        GamepadBtn13 = 0x40000129,
+        GamepadBtn14 = 0x4000012A,
+        GamepadBtn15 = 0x4000012B,
+        GamepadBtn16 = 0x4000012C,
+        Count = 249,
+        NumKeys = 203, // IMPORTANT: Make sure to update these if you modify the values above
+        NumMouseButtons = 16,
+        NumGamepadButtons = 30,
+    };
+
+    // Do not change IDs, must match InputAxis C++ enum
+	public enum InputAxis
+	{
+		MouseX,
+		MouseY,
+		MouseZ,
+		LeftStickX,
+		LeftStickY,
+		RightStickX,
+		RightStickY,
+		LeftTrigger,
+		RightTrigger,
+		Count // Keep at end
+	};
+}

+ 170 - 0
MBansheeEngine/InputConfiguration.cs

@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public sealed class InputConfiguration : ScriptObject
+    {
+        public InputConfiguration()
+        {
+            Internal_CreateInstance(this);
+        }
+
+        public UInt64 RepeatInternal
+        {
+            get
+            {
+                return Internal_GetRepeatInterval(mCachedPtr);
+            }
+
+            set
+            {
+                Internal_SetRepeatInterval(mCachedPtr, value);
+            }
+        }
+
+        public void RegisterButton(String name, ButtonCode buttonCode, 
+            VButtonModifier modifiers = VButtonModifier.None, bool repeatable = false)
+        {
+            Internal_RegisterButton(mCachedPtr, name, buttonCode, modifiers, repeatable);
+        }
+
+        public void UnregisterButton(String name)
+        {
+            Internal_UnregisterButton(mCachedPtr, name);
+        }
+
+        public void RegisterAxis(String name, InputAxis type, float deadZone = 0.0001f, 
+            float sensitivity = 1.0f, bool invert = false)
+        {
+            Internal_RegisterAxis(mCachedPtr, name, type, deadZone, sensitivity, invert);
+        }
+
+        public void UnregisterAxis(String name)
+        {
+            Internal_UnregisterAxis(mCachedPtr, name);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(InputConfiguration inputConfig);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_RegisterButton(IntPtr thisPtr, String name, ButtonCode buttonCode,
+            VButtonModifier modifiers, bool repeatable);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UnregisterButton(IntPtr thisPtr, String name);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_RegisterAxis(IntPtr thisPtr, String name, InputAxis type, float deadZone,
+            float sensitivity, bool invert);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UnregisterAxis(IntPtr thisPtr, String name);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetRepeatInterval(IntPtr thisPtr, UInt64 milliseconds);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern UInt64 Internal_GetRepeatInterval(IntPtr thisPtr);
+    }
+
+    // Do not modify IDs, they must match the C++ enum VButtonModifier
+    public enum VButtonModifier
+    {
+        None = 0x00,
+        Shift = 0x01,
+        Ctrl = 0x02,
+        Alt = 0x04,
+        ShiftCtrl = 0x03,
+        CtrlAlt = 0x06,
+        ShiftAlt = 0x05,
+        ShiftCtrlAlt = 0x07
+    };
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct VirtualButton
+    {
+        private readonly int buttonId;
+
+        public VirtualButton(string name)
+        {
+            buttonId = Internal_InitVirtualButton(name);
+        }
+
+        public static bool operator ==(VirtualButton lhs, VirtualButton rhs)
+        {
+            return lhs.buttonId == rhs.buttonId;
+        }
+
+        public static bool operator !=(VirtualButton lhs, VirtualButton rhs)
+        {
+            return !(lhs == rhs);
+        }
+
+        public override int GetHashCode()
+        {
+            return buttonId.GetHashCode();
+        }
+
+        public override bool Equals(object other)
+        {
+            if (!(other is VirtualButton))
+                return false;
+
+            VirtualButton otherBtn = (VirtualButton)other;
+            if (buttonId.Equals(otherBtn.buttonId))
+                return true;
+
+            return false;
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_InitVirtualButton(String name);
+    }
+
+    [StructLayout(LayoutKind.Sequential)]
+    public struct VirtualAxis
+    {
+        private readonly int axisId;
+
+        public VirtualAxis(string name)
+        {
+            axisId = Internal_InitVirtualAxis(name);
+        }
+
+        public static bool operator ==(VirtualAxis lhs, VirtualAxis rhs)
+        {
+            return lhs.axisId == rhs.axisId;
+        }
+
+        public static bool operator !=(VirtualAxis lhs, VirtualAxis rhs)
+        {
+            return !(lhs == rhs);
+        }
+
+        public override int GetHashCode()
+        {
+            return axisId.GetHashCode();
+        }
+
+        public override bool Equals(object other)
+        {
+            if (!(other is VirtualAxis))
+                return false;
+
+            VirtualAxis otherAxis = (VirtualAxis)other;
+            if (axisId.Equals(otherAxis.axisId))
+                return true;
+
+            return false;
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_InitVirtualAxis(String name);
+    }
+}

+ 3 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -76,6 +76,8 @@
     <Compile Include="GUI\GUIToggle.cs" />
     <Compile Include="GUI\GUIToggle.cs" />
     <Compile Include="GUI\GUIToggleGroup.cs" />
     <Compile Include="GUI\GUIToggleGroup.cs" />
     <Compile Include="HideInInspector.cs" />
     <Compile Include="HideInInspector.cs" />
+    <Compile Include="Input.cs" />
+    <Compile Include="InputConfiguration.cs" />
     <Compile Include="LocString.cs" />
     <Compile Include="LocString.cs" />
     <Compile Include="ManagedResource.cs" />
     <Compile Include="ManagedResource.cs" />
     <Compile Include="Math\AABox.cs" />
     <Compile Include="Math\AABox.cs" />
@@ -110,6 +112,7 @@
     <Compile Include="Math\Vector2.cs" />
     <Compile Include="Math\Vector2.cs" />
     <Compile Include="Math\Vector3.cs" />
     <Compile Include="Math\Vector3.cs" />
     <Compile Include="Math\Vector4.cs" />
     <Compile Include="Math\Vector4.cs" />
+    <Compile Include="VirtualInput.cs" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 86 - 0
MBansheeEngine/VirtualInput.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public static class VirtualInput
+    {
+        public delegate void OnButtonEventDelegate(VirtualButton btn, int deviceIdx);
+
+        public static event OnButtonEventDelegate OnButtonDown;
+        public static event OnButtonEventDelegate OnButtonUp;
+        public static event OnButtonEventDelegate OnButtonHeld;
+
+        public static InputConfiguration KeyConfig
+        {
+            get
+            {
+                return Internal_GetKeyConfig();
+            }
+
+            set
+            {
+                Internal_SetKeyConfig(value);
+            }
+        }
+		
+		public static bool IsButtonDown(VirtualButton button, int deviceIdx = 0)
+	    {
+            return Internal_IsButtonDown(button, deviceIdx);
+	    }
+
+        public static bool IsButtonUp(VirtualButton button, int deviceIdx = 0)
+        {
+            return Internal_IsButtonUp(button, deviceIdx);
+        }
+
+        public static bool IsButtonHeld(VirtualButton button, int deviceIdx = 0)
+        {
+            return Internal_IsButtonHeld(button, deviceIdx);
+        }
+
+        public static float GetAxisValue(VirtualAxis axis, int deviceIdx = 0)
+        {
+            return Internal_GetAxisValue(axis, deviceIdx);
+        }
+
+        private static void Internal_TriggerButtonDown(VirtualButton button, int deviceIdx)
+        {
+            if (OnButtonDown != null)
+                OnButtonDown(button, deviceIdx);
+        }
+
+        private static void Internal_TriggerButtonUp(VirtualButton button, int deviceIdx)
+        {
+            if (OnButtonUp != null)
+                OnButtonUp(button, deviceIdx);
+        }
+
+        private static void Internal_TriggerButtonHeld(VirtualButton button, int deviceIdx)
+        {
+            if (OnButtonHeld != null)
+                OnButtonHeld(button, deviceIdx);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern InputConfiguration Internal_GetKeyConfig();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetKeyConfig(InputConfiguration inputConfig);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsButtonDown(VirtualButton button, int deviceIdx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsButtonUp(VirtualButton button, int deviceIdx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_IsButtonHeld(VirtualButton button, int deviceIdx);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetAxisValue(VirtualAxis button, int deviceIdx);
+	};
+}

+ 11 - 1
Notes.txt

@@ -44,7 +44,7 @@ Reminders:
   - Perhaps add code generation functionality to the engine through Mono? I know Mono has support for it though its Embed interface
   - Perhaps add code generation functionality to the engine through Mono? I know Mono has support for it though its Embed interface
   - Add instancing to engine: All I really need to add are per-instance attributes in VertexData (and MeshData). And then RenderSystem::renderInstance method that also accepts an instance count.
   - Add instancing to engine: All I really need to add are per-instance attributes in VertexData (and MeshData). And then RenderSystem::renderInstance method that also accepts an instance count.
   - Debug console
   - Debug console
-    - Add ability to add colors tags like <color=#123>
+    - Add ability to add colors tags like <color=#123>. Possibly also reference custom styles using those same tags.
 	- When showing a debug message, also provide a (clickable?) reference to Component it was triggered on (if applicable)
 	- When showing a debug message, also provide a (clickable?) reference to Component it was triggered on (if applicable)
 	  - It really helps when you get an error on a Component that hundreds of SceneObjects use
 	  - It really helps when you get an error on a Component that hundreds of SceneObjects use
 	- When displaying an error with a callstack, make each line of the callstack clickable where it opens the external editor
 	- When displaying an error with a callstack, make each line of the callstack clickable where it opens the external editor
@@ -78,11 +78,21 @@ Reminders:
   - Think about removing C# GUIPanel. If I replace current EditorWidget's GUIArea with GUIWidget then panels aren't needed as those widgets can just
   - Think about removing C# GUIPanel. If I replace current EditorWidget's GUIArea with GUIWidget then panels aren't needed as those widgets can just
     use areas directly. This means that the EditorWidgetContainer will also have a GUIWidget but it will be separate.
     use areas directly. This means that the EditorWidgetContainer will also have a GUIWidget but it will be separate.
 	 - When doing this also consider refactoring how GUIArea anchoring works (currently it's limited and have ugly interface)
 	 - When doing this also consider refactoring how GUIArea anchoring works (currently it's limited and have ugly interface)
+  - Get rid of exceptions. I don't use any of their properties that I can't reproduce with a Debug::log and a manual stack trace. Also disable them in compiler to improve compile time.
+  - Improve compile time: Disable inlining in debug builds (Ob1 option), use precompiled headers
 
 
 Potential optimizations:
 Potential optimizations:
  - bulkPixelConversion is EXTREMELY poorly unoptimized. Each pixel it calls a separate method that does redudant operations every pixel.
  - bulkPixelConversion is EXTREMELY poorly unoptimized. Each pixel it calls a separate method that does redudant operations every pixel.
  - UI shader resolution params for gui should be in a separate constant buffer
  - UI shader resolution params for gui should be in a separate constant buffer
 
 
+Things that I need to automate:
+ - Automatic C++ RTTI generation by parsing the C++ class source
+ - Shader definitions - They should be specified in text field and parsed like shader code
+ - Shader cross compiling - Write shaders in one language and have them automatically compiled in others
+ - C++ <-> C# glue code. A lot of it relies on enums not changing IDs, structs not changing layouts and methods
+   not changing parameters between C++ and C# code. It makes it easy to modify one and forget the other.
+   Instead make a smarter system that parses a common file and outputs both C++ and C# glue code.
+
 ----------------------------------------------------------------------------------------------
 ----------------------------------------------------------------------------------------------
 More detailed thought out system descriptions:
 More detailed thought out system descriptions:
 
 

+ 53 - 0
SBansheeEngine/Include/BsScriptInput.h

@@ -0,0 +1,53 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsInputFwd.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptInput : public ScriptObject<ScriptInput>
+	{
+	public:
+		SCRIPT_OBJ(BansheeEngineAssemblyName, "BansheeEngine", "Input")
+
+		static void startUp();
+		static void shutDown();
+	private:
+		static void onButtonDown(const ButtonEvent& ev);
+		static void onButtonUp(const ButtonEvent& ev);
+		static void onCharInput(const TextInputEvent& ev);
+		static void onPointerMoved(const PointerEvent& ev);
+
+		static void onPointerPressed(const PointerEvent& ev);
+		static void onPointerReleased(const PointerEvent& ev);
+		static void onPointerDoubleClick(const PointerEvent& ev);
+
+		static bool internal_isButtonHeld(ButtonCode code, UINT32 deviceIdx);
+		static bool internal_isButtonDown(ButtonCode code, UINT32 deviceIdx);
+		static bool internal_isButtonUp(ButtonCode code, UINT32 deviceIdx);
+		static float internal_getAxisValue(UINT32 axisType, UINT32 deviceIdx);
+
+		typedef void(__stdcall *OnButtonEventThunkDef) (ButtonCode, UINT32, MonoException**);
+		typedef void(__stdcall *OnCharInputEventThunkDef) (UINT32, MonoException**);
+		typedef void(__stdcall *OnPointerEventThunkDef) (Vector2I, PointerEventButton, bool, bool, bool, float, MonoException**);
+
+		static OnButtonEventThunkDef OnButtonPressedThunk;
+		static OnButtonEventThunkDef OnButtonReleasedThunk;
+		static OnCharInputEventThunkDef OnCharInputThunk;
+		static OnPointerEventThunkDef OnPointerPressedThunk;
+		static OnPointerEventThunkDef OnPointerReleasedThunk;
+		static OnPointerEventThunkDef OnPointerMovedThunk;
+		static OnPointerEventThunkDef OnPointerDoubleClickThunk;
+
+		static HEvent OnButtonPressedConn;
+		static HEvent OnButtonReleasedConn;
+		static HEvent OnCharInputConn;
+		static HEvent OnPointerPressedConn;
+		static HEvent OnPointerReleasedConn;
+		static HEvent OnPointerMovedConn;
+		static HEvent OnPointerDoubleClickConn;
+
+		ScriptInput(MonoObject* instance);
+	};
+}

+ 63 - 0
SBansheeEngine/Include/BsScriptInputConfiguration.h

@@ -0,0 +1,63 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsInputConfiguration.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptInputConfiguration : public ScriptObject<ScriptInputConfiguration>
+	{
+	public:
+		SCRIPT_OBJ(BansheeEngineAssemblyName, "BansheeEngine", "InputConfiguration")
+
+		InputConfigurationPtr getInternalValue() const { return mInputConfig; }
+
+		static ScriptInputConfiguration* getScriptInputConfig(const InputConfigurationPtr& inputConfig);
+		static ScriptInputConfiguration* createScriptInputConfig(const InputConfigurationPtr& inputConfig);
+
+	private:
+		static void internal_CreateInstance(MonoObject* object);
+
+		static void internal_RegisterButton(ScriptInputConfiguration* thisPtr, MonoString* name, ButtonCode buttonCode,
+			VButtonModifier modifiers, bool repeatable);
+		static void internal_UnregisterButton(ScriptInputConfiguration* thisPtr, MonoString* name);
+
+		static void internal_RegisterAxis(ScriptInputConfiguration* thisPtr, MonoString* name, InputAxis type, float deadZone,
+			float sensitivity, bool invert);
+		static void internal_UnregisterAxis(ScriptInputConfiguration* thisPtr, MonoString* name);
+
+		static void internal_SetRepeatInterval(ScriptInputConfiguration* thisPtr, UINT64 milliseconds);
+		static UINT64 internal_GetRepeatInterval(ScriptInputConfiguration* thisPtr);
+
+		ScriptInputConfiguration(MonoObject* instance, const InputConfigurationPtr& inputConfig);
+
+		void _onManagedInstanceDeleted();
+
+		InputConfigurationPtr mInputConfig;
+
+		static Map<UINT64, ScriptInputConfiguration*> ScriptInputConfigurations;
+	};
+
+	class BS_SCR_BE_EXPORT ScriptVirtualButton : public ScriptObject <ScriptVirtualButton>
+	{
+	public:
+		SCRIPT_OBJ(BansheeEngineAssemblyName, "BansheeEngine", "VirtualButton")
+
+	private:
+		static UINT32 internal_InitVirtualButton(MonoString* name);
+
+		ScriptVirtualButton(MonoObject* instance);
+	};
+
+	class BS_SCR_BE_EXPORT ScriptVirtualAxis : public ScriptObject <ScriptVirtualAxis>
+	{
+	public:
+		SCRIPT_OBJ(BansheeEngineAssemblyName, "BansheeEngine", "VirtualAxis")
+
+	private:
+		static UINT32 internal_InitVirtualAxis(MonoString* name);
+
+		ScriptVirtualAxis(MonoObject* instance);
+	};
+}

+ 40 - 0
SBansheeEngine/Include/BsScriptVirtualInput.h

@@ -0,0 +1,40 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsInputConfiguration.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptVirtualInput : public ScriptObject<ScriptVirtualInput>
+	{
+	public:
+		SCRIPT_OBJ(BansheeEngineAssemblyName, "BansheeEngine", "VirtualInput")
+
+		static void startUp();
+		static void shutDown();
+	private:
+		static void onButtonDown(const VirtualButton& btn, UINT32 deviceIdx);
+		static void onButtonUp(const VirtualButton& btn, UINT32 deviceIdx);
+		static void onButtonHeld(const VirtualButton& btn, UINT32 deviceIdx);
+
+		static MonoObject* internal_getKeyConfig();
+		static void internal_setKeyConfig(MonoObject* keyConfig);
+		static bool internal_isButtonHeld(VirtualButton btn, UINT32 deviceIdx);
+		static bool internal_isButtonDown(VirtualButton btn, UINT32 deviceIdx);
+		static bool internal_isButtonUp(VirtualButton btn, UINT32 deviceIdx);
+		static float internal_getAxisValue(VirtualAxis axis, UINT32 deviceIdx);
+
+		typedef void(__stdcall *OnButtonEventThunkDef) (VirtualButton, UINT32, MonoException**);
+
+		static OnButtonEventThunkDef OnButtonUpThunk;
+		static OnButtonEventThunkDef OnButtonDownThunk;
+		static OnButtonEventThunkDef OnButtonHeldThunk;
+
+		static HEvent OnButtonPressedConn;
+		static HEvent OnButtonReleasedConn;
+		static HEvent OnButtonHeldConn;
+
+		ScriptVirtualInput(MonoObject* instance);
+	};
+}

+ 6 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -268,6 +268,8 @@
     <ClInclude Include="Include\BsScriptGUIToggle.h" />
     <ClInclude Include="Include\BsScriptGUIToggle.h" />
     <ClInclude Include="Include\BsScriptGUIToggleGroup.h" />
     <ClInclude Include="Include\BsScriptGUIToggleGroup.h" />
     <ClInclude Include="Include\BsScriptHString.h" />
     <ClInclude Include="Include\BsScriptHString.h" />
+    <ClInclude Include="Include\BsScriptInput.h" />
+    <ClInclude Include="Include\BsScriptInputConfiguration.h" />
     <ClInclude Include="Include\BsScriptMacros.h" />
     <ClInclude Include="Include\BsScriptMacros.h" />
     <ClInclude Include="Include\BsScriptManagedResource.h" />
     <ClInclude Include="Include\BsScriptManagedResource.h" />
     <ClInclude Include="Include\BsScriptObject.h" />
     <ClInclude Include="Include\BsScriptObject.h" />
@@ -288,6 +290,7 @@
     <ClInclude Include="Include\BsScriptTexture2D.h" />
     <ClInclude Include="Include\BsScriptTexture2D.h" />
     <ClInclude Include="Include\BsScriptGUIContent.h" />
     <ClInclude Include="Include\BsScriptGUIContent.h" />
     <ClInclude Include="Include\BsManagedSerializableObjectInfo.h" />
     <ClInclude Include="Include\BsManagedSerializableObjectInfo.h" />
+    <ClInclude Include="Include\BsScriptVirtualInput.h" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsManagedComponent.cpp" />
     <ClCompile Include="Source\BsManagedComponent.cpp" />
@@ -319,6 +322,8 @@
     <ClCompile Include="Source\BsScriptGUIToggleGroup.cpp" />
     <ClCompile Include="Source\BsScriptGUIToggleGroup.cpp" />
     <ClCompile Include="Source\BsScriptHString.cpp" />
     <ClCompile Include="Source\BsScriptHString.cpp" />
     <ClCompile Include="Source\BsScriptGUIInputBox.cpp" />
     <ClCompile Include="Source\BsScriptGUIInputBox.cpp" />
+    <ClCompile Include="Source\BsScriptInput.cpp" />
+    <ClCompile Include="Source\BsScriptInputConfiguration.cpp" />
     <ClCompile Include="Source\BsScriptManagedResource.cpp" />
     <ClCompile Include="Source\BsScriptManagedResource.cpp" />
     <ClCompile Include="Source\BsScriptObject.cpp" />
     <ClCompile Include="Source\BsScriptObject.cpp" />
     <ClCompile Include="Source\BsScriptObjectImpl.cpp" />
     <ClCompile Include="Source\BsScriptObjectImpl.cpp" />
@@ -340,6 +345,7 @@
     <ClCompile Include="Source\BsScriptStringTable.cpp" />
     <ClCompile Include="Source\BsScriptStringTable.cpp" />
     <ClCompile Include="Source\BsScriptTexture2D.cpp" />
     <ClCompile Include="Source\BsScriptTexture2D.cpp" />
     <ClCompile Include="Source\BsScriptGUIContent.cpp" />
     <ClCompile Include="Source\BsScriptGUIContent.cpp" />
+    <ClCompile Include="Source\BsScriptVirtualInput.cpp" />
   </ItemGroup>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   <ImportGroup Label="ExtensionTargets">

+ 18 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -219,6 +219,15 @@
     <ClInclude Include="Include\BsScriptCamera.h">
     <ClInclude Include="Include\BsScriptCamera.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsScriptInput.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsScriptInputConfiguration.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsScriptVirtualInput.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -371,5 +380,14 @@
     <ClCompile Include="Source\BsScriptCamera.cpp">
     <ClCompile Include="Source\BsScriptCamera.cpp">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsScriptInput.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsScriptInputConfiguration.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsScriptVirtualInput.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 6 - 0
SBansheeEngine/Source/BsScriptEnginePlugin.cpp

@@ -6,6 +6,8 @@
 #include "BsScriptGameObjectManager.h"
 #include "BsScriptGameObjectManager.h"
 #include "BsManagedResourceManager.h"
 #include "BsManagedResourceManager.h"
 #include "BsScriptManager.h"
 #include "BsScriptManager.h"
+#include "BsScriptInput.h"
+#include "BsScriptVirtualInput.h"
 
 
 // DEBUG ONLY
 // DEBUG ONLY
 #include "BsScriptSceneObject.h"
 #include "BsScriptSceneObject.h"
@@ -45,6 +47,8 @@ namespace BansheeEngine
 		RuntimeScriptObjects::startUp();
 		RuntimeScriptObjects::startUp();
 		ScriptResourceManager::startUp();
 		ScriptResourceManager::startUp();
 		ScriptGameObjectManager::startUp();
 		ScriptGameObjectManager::startUp();
+		ScriptInput::startUp();
+		ScriptVirtualInput::startUp();
 
 
 		RuntimeScriptObjects::instance().refreshScriptObjects(BansheeEngineAssemblyName);
 		RuntimeScriptObjects::instance().refreshScriptObjects(BansheeEngineAssemblyName);
 
 
@@ -55,6 +59,8 @@ namespace BansheeEngine
 
 
 	extern "C" BS_SCR_BE_EXPORT void unloadPlugin()
 	extern "C" BS_SCR_BE_EXPORT void unloadPlugin()
 	{
 	{
+		ScriptVirtualInput::shutDown();
+		ScriptInput::shutDown();
 		ManagedResourceManager::shutDown();
 		ManagedResourceManager::shutDown();
 		ScriptManager::instance().destroy();
 		ScriptManager::instance().destroy();
 		ScriptGameObjectManager::shutDown();
 		ScriptGameObjectManager::shutDown();

+ 146 - 0
SBansheeEngine/Source/BsScriptInput.cpp

@@ -0,0 +1,146 @@
+#include "BsScriptInput.h"
+#include "BsMonoManager.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsMonoUtil.h"
+#include "BsDebug.h"
+#include "BsInput.h"
+
+namespace BansheeEngine
+{
+	ScriptInput::OnButtonEventThunkDef ScriptInput::OnButtonPressedThunk;
+	ScriptInput::OnButtonEventThunkDef ScriptInput::OnButtonReleasedThunk;
+	ScriptInput::OnCharInputEventThunkDef ScriptInput::OnCharInputThunk;
+	ScriptInput::OnPointerEventThunkDef ScriptInput::OnPointerPressedThunk;
+	ScriptInput::OnPointerEventThunkDef ScriptInput::OnPointerReleasedThunk;
+	ScriptInput::OnPointerEventThunkDef ScriptInput::OnPointerMovedThunk;
+	ScriptInput::OnPointerEventThunkDef ScriptInput::OnPointerDoubleClickThunk;
+
+	HEvent ScriptInput::OnButtonPressedConn;
+	HEvent ScriptInput::OnButtonReleasedConn;
+	HEvent ScriptInput::OnCharInputConn;
+	HEvent ScriptInput::OnPointerPressedConn;
+	HEvent ScriptInput::OnPointerReleasedConn;
+	HEvent ScriptInput::OnPointerMovedConn;
+	HEvent ScriptInput::OnPointerDoubleClickConn;
+
+	ScriptInput::ScriptInput(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptInput::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_IsButtonHeld", &ScriptInput::internal_isButtonHeld);
+		metaData.scriptClass->addInternalCall("Internal_IsButtonDown", &ScriptInput::internal_isButtonDown);
+		metaData.scriptClass->addInternalCall("Internal_IsButtonUp", &ScriptInput::internal_isButtonUp);
+		metaData.scriptClass->addInternalCall("Internal_GetAxisValue", &ScriptInput::internal_getAxisValue);
+
+		OnButtonPressedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonDown", "ButtonCode,int")->getThunk();
+		OnButtonReleasedThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonUp", "ButtonCode,int")->getThunk();
+		OnCharInputThunk = (OnCharInputEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerCharInput", "int")->getThunk();
+		OnPointerPressedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerPressed", "Vector2I,PointerEventButton,bool,bool,bool,single")->getThunk();
+		OnPointerReleasedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerReleased", "Vector2I,PointerEventButton,bool,bool,bool,single")->getThunk();
+		OnPointerMovedThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerMove", "Vector2I,PointerEventButton,bool,bool,bool,single")->getThunk();
+		OnPointerDoubleClickThunk = (OnPointerEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerPointerDoubleClick", "Vector2I,PointerEventButton,bool,bool,bool,single")->getThunk();
+	}
+
+	void ScriptInput::startUp()
+	{
+		Input& input = Input::instance();
+
+		OnButtonPressedConn = input.onButtonDown.connect(&ScriptInput::onButtonDown);
+		OnButtonReleasedConn = input.onButtonUp.connect(&ScriptInput::onButtonUp);
+		OnCharInputConn = input.onCharInput.connect(&ScriptInput::onCharInput);
+		OnPointerPressedConn = input.onPointerPressed.connect(&ScriptInput::onPointerPressed);
+		OnPointerReleasedConn = input.onPointerReleased.connect(&ScriptInput::onPointerReleased);
+		OnPointerMovedConn = input.onPointerMoved.connect(&ScriptInput::onPointerMoved);
+		OnPointerDoubleClickConn = input.onPointerDoubleClick.connect(&ScriptInput::onPointerDoubleClick);
+	}
+
+	void ScriptInput::shutDown()
+	{
+		OnButtonPressedConn.disconnect();
+		OnButtonReleasedConn.disconnect();
+		OnCharInputConn.disconnect();
+		OnPointerPressedConn.disconnect();
+		OnPointerReleasedConn.disconnect();
+		OnPointerMovedConn.disconnect();
+		OnPointerDoubleClickConn.disconnect();
+	}
+
+	void ScriptInput::onButtonDown(const ButtonEvent& ev)
+	{
+		MonoException* exception = nullptr;
+		OnButtonPressedThunk(ev.buttonCode, ev.deviceIdx, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptInput::onButtonUp(const ButtonEvent& ev)
+	{
+		MonoException* exception = nullptr;
+		OnButtonReleasedThunk(ev.buttonCode, ev.deviceIdx, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptInput::onCharInput(const TextInputEvent& ev)
+	{
+		MonoException* exception = nullptr;
+		OnCharInputThunk(ev.textChar, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptInput::onPointerMoved(const PointerEvent& ev)
+	{
+		MonoException* exception = nullptr;
+		OnPointerMovedThunk(ev.screenPos, ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptInput::onPointerPressed(const PointerEvent& ev)
+	{
+		MonoException* exception = nullptr;
+		OnPointerPressedThunk(ev.screenPos, ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptInput::onPointerReleased(const PointerEvent& ev)
+	{
+		MonoException* exception = nullptr;
+		OnPointerReleasedThunk(ev.screenPos, ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptInput::onPointerDoubleClick(const PointerEvent& ev)
+	{
+		MonoException* exception = nullptr;
+		OnPointerDoubleClickThunk(ev.screenPos, ev.button, ev.shift, ev.control, ev.alt, ev.mouseWheelScrollAmount, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	bool ScriptInput::internal_isButtonHeld(ButtonCode code, UINT32 deviceIdx)
+	{
+		return Input::instance().isButtonHeld(code, deviceIdx);
+	}
+
+	bool ScriptInput::internal_isButtonDown(ButtonCode code, UINT32 deviceIdx)
+	{
+		return Input::instance().isButtonDown(code, deviceIdx);
+	}
+
+	bool ScriptInput::internal_isButtonUp(ButtonCode code, UINT32 deviceIdx)
+	{
+		return Input::instance().isButtonUp(code, deviceIdx);
+	}
+
+	float ScriptInput::internal_getAxisValue(UINT32 axisType, UINT32 deviceIdx)
+	{
+		return Input::instance().getAxisValue(axisType, deviceIdx);
+	}
+}

+ 142 - 0
SBansheeEngine/Source/BsScriptInputConfiguration.cpp

@@ -0,0 +1,142 @@
+#include "BsScriptInputConfiguration.h"
+#include "BsMonoManager.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsMonoUtil.h"
+#include "BsVirtualInput.h"
+
+namespace BansheeEngine
+{
+	Map<UINT64, ScriptInputConfiguration*> ScriptInputConfiguration::ScriptInputConfigurations;
+
+	ScriptInputConfiguration::ScriptInputConfiguration(MonoObject* instance, const InputConfigurationPtr& inputConfig)
+		:ScriptObject(instance), mInputConfig(inputConfig)
+	{ 
+		UINT64 configId = (UINT64)inputConfig.get();
+		ScriptInputConfigurations[configId] = this;
+	}
+
+	void ScriptInputConfiguration::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptInputConfiguration::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_RegisterButton", &ScriptInputConfiguration::internal_RegisterButton);
+		metaData.scriptClass->addInternalCall("Internal_UnregisterButton", &ScriptInputConfiguration::internal_UnregisterButton);
+		metaData.scriptClass->addInternalCall("Internal_RegisterAxis", &ScriptInputConfiguration::internal_RegisterAxis);
+		metaData.scriptClass->addInternalCall("Internal_UnregisterAxis", &ScriptInputConfiguration::internal_UnregisterAxis);
+		metaData.scriptClass->addInternalCall("Internal_SetRepeatInterval", &ScriptInputConfiguration::internal_SetRepeatInterval);
+		metaData.scriptClass->addInternalCall("Internal_GetRepeatInterval", &ScriptInputConfiguration::internal_GetRepeatInterval);
+	}
+
+	ScriptInputConfiguration* ScriptInputConfiguration::getScriptInputConfig(const InputConfigurationPtr& inputConfig)
+	{
+		UINT64 configId = (UINT64)inputConfig.get();
+
+		auto iterFind = ScriptInputConfigurations.find(configId);
+		if (iterFind != ScriptInputConfigurations.end())
+			return iterFind->second;
+
+		return nullptr;
+	}
+
+	ScriptInputConfiguration* ScriptInputConfiguration::createScriptInputConfig(const InputConfigurationPtr& inputConfig)
+	{
+		MonoObject* instance = metaData.scriptClass->createInstance();
+
+		return ScriptInputConfiguration::toNative(instance);
+	}
+
+	void ScriptInputConfiguration::internal_CreateInstance(MonoObject* object)
+	{
+		InputConfigurationPtr inputConfig = VirtualInput::createConfiguration();
+
+		ScriptInputConfiguration* nativeInstance = new (bs_alloc<ScriptInputConfiguration>()) ScriptInputConfiguration(object, inputConfig);
+	}
+
+	void ScriptInputConfiguration::internal_RegisterButton(ScriptInputConfiguration* thisPtr, MonoString* name, ButtonCode buttonCode,
+		VButtonModifier modifiers, bool repeatable)
+	{
+		String nameStr = MonoUtil::monoToString(name);
+
+		thisPtr->getInternalValue()->registerButton(nameStr, buttonCode, modifiers, repeatable);
+	}
+
+	void ScriptInputConfiguration::internal_UnregisterButton(ScriptInputConfiguration* thisPtr, MonoString* name)
+	{
+		String nameStr = MonoUtil::monoToString(name);
+
+		thisPtr->getInternalValue()->unregisterButton(nameStr);
+	}
+
+	void ScriptInputConfiguration::internal_RegisterAxis(ScriptInputConfiguration* thisPtr, MonoString* name, InputAxis type, float deadZone,
+		float sensitivity, bool invert)
+	{
+		String nameStr = MonoUtil::monoToString(name);
+
+		VIRTUAL_AXIS_DESC axisDesc;
+		axisDesc.type = (UINT32)type;
+		axisDesc.deadZone = deadZone;
+		axisDesc.invert = invert;
+		axisDesc.sensitivity = sensitivity;
+
+		thisPtr->getInternalValue()->registerAxis(nameStr, axisDesc);
+	}
+
+	void ScriptInputConfiguration::internal_UnregisterAxis(ScriptInputConfiguration* thisPtr, MonoString* name)
+	{
+		String nameStr = MonoUtil::monoToString(name);
+
+		thisPtr->getInternalValue()->unregisterAxis(nameStr);
+	}
+
+	void ScriptInputConfiguration::internal_SetRepeatInterval(ScriptInputConfiguration* thisPtr, UINT64 milliseconds)
+	{
+		thisPtr->getInternalValue()->setRepeatInterval(milliseconds);
+	}
+
+	UINT64 ScriptInputConfiguration::internal_GetRepeatInterval(ScriptInputConfiguration* thisPtr)
+	{
+		return thisPtr->getInternalValue()->getRepeatInterval();
+	}
+
+	void ScriptInputConfiguration::_onManagedInstanceDeleted()
+	{
+		mManagedInstance = nullptr;
+
+		UINT64 configId = (UINT64)mInputConfig.get();
+		ScriptInputConfigurations.erase(configId);
+	}
+
+	ScriptVirtualButton::ScriptVirtualButton(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptVirtualButton::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_InitVirtualButton", &ScriptVirtualButton::internal_InitVirtualButton);
+	}
+
+	UINT32 ScriptVirtualButton::internal_InitVirtualButton(MonoString* name)
+	{
+		String nameStr = MonoUtil::monoToString(name);
+
+		VirtualButton vb(nameStr);
+		return vb.buttonIdentifier;
+	}
+
+	ScriptVirtualAxis::ScriptVirtualAxis(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptVirtualAxis::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_InitVirtualAxis", &ScriptVirtualAxis::internal_InitVirtualAxis);
+	}
+
+	UINT32 ScriptVirtualAxis::internal_InitVirtualAxis(MonoString* name)
+	{
+		String nameStr = MonoUtil::monoToString(name);
+
+		VirtualAxis vb(nameStr);
+		return vb.axisIdentifier;
+	}
+}

+ 115 - 0
SBansheeEngine/Source/BsScriptVirtualInput.cpp

@@ -0,0 +1,115 @@
+#include "BsScriptVirtualInput.h"
+#include "BsMonoManager.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsMonoUtil.h"
+#include "BsDebug.h"
+#include "BsVirtualInput.h"
+#include "BsScriptInputConfiguration.h"
+
+namespace BansheeEngine
+{
+	ScriptVirtualInput::OnButtonEventThunkDef ScriptVirtualInput::OnButtonUpThunk;
+	ScriptVirtualInput::OnButtonEventThunkDef ScriptVirtualInput::OnButtonDownThunk;
+	ScriptVirtualInput::OnButtonEventThunkDef ScriptVirtualInput::OnButtonHeldThunk;
+
+	HEvent ScriptVirtualInput::OnButtonPressedConn;
+	HEvent ScriptVirtualInput::OnButtonReleasedConn;
+	HEvent ScriptVirtualInput::OnButtonHeldConn;
+
+	ScriptVirtualInput::ScriptVirtualInput(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptVirtualInput::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_GetKeyConfig", &ScriptVirtualInput::internal_getKeyConfig);
+		metaData.scriptClass->addInternalCall("Internal_SetKeyConfig", &ScriptVirtualInput::internal_setKeyConfig);
+		metaData.scriptClass->addInternalCall("Internal_IsButtonHeld", &ScriptVirtualInput::internal_isButtonHeld);
+		metaData.scriptClass->addInternalCall("Internal_IsButtonDown", &ScriptVirtualInput::internal_isButtonDown);
+		metaData.scriptClass->addInternalCall("Internal_IsButtonUp", &ScriptVirtualInput::internal_isButtonUp);
+		metaData.scriptClass->addInternalCall("Internal_GetAxisValue", &ScriptVirtualInput::internal_getAxisValue);
+
+		OnButtonUpThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonDown", "VirtualButton,int")->getThunk();
+		OnButtonDownThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonUp", "VirtualButton,int")->getThunk();
+		OnButtonHeldThunk = (OnButtonEventThunkDef)metaData.scriptClass->getMethodExact("Internal_TriggerButtonHeld", "VirtualButton,int")->getThunk();
+	}
+
+	void ScriptVirtualInput::startUp()
+	{
+		VirtualInput& input = VirtualInput::instance();
+
+		OnButtonPressedConn = input.onButtonDown.connect(&ScriptVirtualInput::onButtonDown);
+		OnButtonReleasedConn = input.onButtonUp.connect(&ScriptVirtualInput::onButtonUp);
+		OnButtonHeldConn = input.onButtonHeld.connect(&ScriptVirtualInput::onButtonHeld);
+	}
+
+	void ScriptVirtualInput::shutDown()
+	{
+		OnButtonPressedConn.disconnect();
+		OnButtonReleasedConn.disconnect();
+		OnButtonHeldConn.disconnect();
+	}
+
+	void ScriptVirtualInput::onButtonDown(const VirtualButton& btn, UINT32 deviceIdx)
+	{
+		MonoException* exception = nullptr;
+		OnButtonDownThunk(btn, deviceIdx, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptVirtualInput::onButtonUp(const VirtualButton& btn, UINT32 deviceIdx)
+	{
+		MonoException* exception = nullptr;
+		OnButtonUpThunk(btn, deviceIdx, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	void ScriptVirtualInput::onButtonHeld(const VirtualButton& btn, UINT32 deviceIdx)
+	{
+		MonoException* exception = nullptr;
+		OnButtonHeldThunk(btn, deviceIdx, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
+	MonoObject* ScriptVirtualInput::internal_getKeyConfig()
+	{
+		InputConfigurationPtr inputConfig = VirtualInput::instance().getConfiguration();
+
+		ScriptInputConfiguration* scriptInputConfig = ScriptInputConfiguration::getScriptInputConfig(inputConfig);
+		if (scriptInputConfig == nullptr)
+			scriptInputConfig = ScriptInputConfiguration::createScriptInputConfig(inputConfig);
+
+		return scriptInputConfig->getManagedInstance();
+	}
+
+	void ScriptVirtualInput::internal_setKeyConfig(MonoObject* keyConfig)
+	{
+		ScriptInputConfiguration* inputConfig = ScriptInputConfiguration::toNative(keyConfig);
+
+		VirtualInput::instance().setConfiguration(inputConfig->getInternalValue());
+	}
+
+	bool ScriptVirtualInput::internal_isButtonHeld(VirtualButton btn, UINT32 deviceIdx)
+	{
+		return VirtualInput::instance().isButtonHeld(btn, deviceIdx);
+	}
+
+	bool ScriptVirtualInput::internal_isButtonDown(VirtualButton btn, UINT32 deviceIdx)
+	{
+		return VirtualInput::instance().isButtonDown(btn, deviceIdx);
+	}
+
+	bool ScriptVirtualInput::internal_isButtonUp(VirtualButton btn, UINT32 deviceIdx)
+	{
+		return VirtualInput::instance().isButtonUp(btn, deviceIdx);
+	}
+
+	float ScriptVirtualInput::internal_getAxisValue(VirtualAxis axis, UINT32 deviceIdx)
+	{
+		return VirtualInput::instance().getAxisValue(axis, deviceIdx);
+	}
+}

+ 2 - 0
SceneView.txt

@@ -3,6 +3,8 @@
   - Make a C# wrapper for Camera and Renderable
   - Make a C# wrapper for Camera and Renderable
 
 
 The bounds I retrieve from GUIUtility are relative to the entire window, not just the current widget. Another reason to move editor widgets to their own GUIWidget.
 The bounds I retrieve from GUIUtility are relative to the entire window, not just the current widget. Another reason to move editor widgets to their own GUIWidget.
+Hook up Input callbacks in ScriptInput
+Passing a Vector2I (and I'm assuming any script) through a thunk crashes the runtime
 
 
 Set up Application::getPrimaryViewport so it returns a valid viewport otherwise C# Camera won't work properly
 Set up Application::getPrimaryViewport so it returns a valid viewport otherwise C# Camera won't work properly