1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Input/BsInput.h"
4#include "Input/BsMouse.h"
5#include "Input/BsKeyboard.h"
6#include "Input/BsGamepad.h"
7#include "Utility/BsTime.h"
8#include "Math/BsMath.h"
9#include "Managers/BsRenderWindowManager.h"
10#include "BsCoreApplication.h"
11
12using namespace std::placeholders;
13
14namespace bs
15{
16 // Note: Input polling methods for button/axis could be re-written so their query immediate state
17 // instead of returning cached state from event callbacks. This /might/ result in even less input lag?
18
19 const int Input::HISTORY_BUFFER_SIZE = 10; // Size of buffer used for input smoothing
20 const float Input::WEIGHT_MODIFIER = 0.5f;
21
22 Input::DeviceData::DeviceData()
23 {
24 for (UINT32 i = 0; i < BC_Count; i++)
25 keyStates[i] = ButtonState::Off;
26 }
27
28 Input::Input()
29 {
30 SPtr<RenderWindow> primaryWindow = gCoreApplication().getPrimaryWindow();
31 primaryWindow->getCustomAttribute("WINDOW", &mWindowHandle);
32
33 // Subscribe to events
34 mCharInputConn = Platform::onCharInput.connect(std::bind(&Input::charInput, this, _1));
35 mCursorMovedConn = Platform::onCursorMoved.connect(std::bind(&Input::cursorMoved, this, _1, _2));
36 mCursorPressedConn = Platform::onCursorButtonPressed.connect(std::bind(&Input::cursorPressed, this, _1, _2, _3));
37 mCursorReleasedConn = Platform::onCursorButtonReleased.connect(std::bind(&Input::cursorReleased, this, _1, _2, _3));
38 mCursorDoubleClickConn = Platform::onCursorDoubleClick.connect(std::bind(&Input::cursorDoubleClick, this, _1, _2));
39 mInputCommandConn = Platform::onInputCommand.connect(std::bind(&Input::inputCommandEntered, this, _1));
40
41 mMouseWheelScrolledConn = Platform::onMouseWheelScrolled.connect(std::bind(&Input::mouseWheelScrolled, this, _1));
42
43 RenderWindowManager::instance().onFocusGained.connect(std::bind(&Input::inputWindowChanged, this, _1));
44 RenderWindowManager::instance().onFocusLost.connect(std::bind(&Input::inputFocusLost, this));
45
46 for (int i = 0; i < 3; i++)
47 mPointerButtonStates[i] = ButtonState::Off;
48
49 // Mouse smoothing
50 mMouseSampleAccumulator[0] = 0;
51 mMouseSampleAccumulator[1] = 0;
52 mTotalMouseSamplingTime[0] = 1.0f / 125.0f; // Use 125Hz as initial pooling rate for mice
53 mTotalMouseSamplingTime[1] = 1.0f / 125.0f;
54 mTotalMouseNumSamples[0] = 1;
55 mTotalMouseNumSamples[1] = 1;
56 mMouseSmoothedAxis[0] = 0.0f;
57 mMouseSmoothedAxis[1] = 0.0f;
58 mMouseZeroTime[0] = 0.0f;
59 mMouseZeroTime[1] = 0.0f;
60
61 // Raw input
62 initRawInput();
63 }
64
65 Input::~Input()
66 {
67 cleanUpRawInput();
68
69 mCharInputConn.disconnect();
70 mCursorMovedConn.disconnect();
71 mCursorPressedConn.disconnect();
72 mCursorReleasedConn.disconnect();
73 mCursorDoubleClickConn.disconnect();
74 mInputCommandConn.disconnect();
75 mMouseWheelScrolledConn.disconnect();
76 }
77
78 void Input::_update()
79 {
80 // Toggle states only remain active for a single frame before they are transitioned
81 // into permanent state
82
83 for (auto& deviceData : mDevices)
84 {
85 for (UINT32 i = 0; i < BC_Count; i++)
86 {
87 if (deviceData.keyStates[i] == ButtonState::ToggledOff || deviceData.keyStates[i] == ButtonState::ToggledOnOff)
88 deviceData.keyStates[i] = ButtonState::Off;
89 else if (deviceData.keyStates[i] == ButtonState::ToggledOn)
90 deviceData.keyStates[i] = ButtonState::On;
91 }
92
93 UINT32 numAxes = (UINT32)deviceData.axes.size();
94 for (UINT32 i = 0; i < numAxes; i++)
95 deviceData.axes[i] = 0.0f;
96 }
97
98 for (UINT32 i = 0; i < 3; i++)
99 {
100 if (mPointerButtonStates[i] == ButtonState::ToggledOff || mPointerButtonStates[i] == ButtonState::ToggledOnOff)
101 mPointerButtonStates[i] = ButtonState::Off;
102 else if (mPointerButtonStates[i] == ButtonState::ToggledOn)
103 mPointerButtonStates[i] = ButtonState::On;
104 }
105
106 mPointerDelta = Vector2I::ZERO; // Reset delta in case we don't receive any mouse input this frame
107 mPointerDoubleClicked = false;
108
109 // Capture raw input
110 if (mMouse != nullptr)
111 mMouse->capture();
112
113 if (mKeyboard != nullptr)
114 mKeyboard->capture();
115
116 for (auto& gamepad : mGamepads)
117 gamepad->capture();
118
119 float rawXValue = 0.0f;
120 float rawYValue = 0.0f;
121
122 // Smooth mouse axes if needed
123 if (mMouseSmoothingEnabled)
124 {
125 rawXValue = smoothMouse((float)mMouseSampleAccumulator[0], 0);
126 rawYValue = smoothMouse((float)mMouseSampleAccumulator[1], 1);
127 }
128 else
129 {
130 rawXValue = (float)mMouseSampleAccumulator[0];
131 rawYValue = (float)mMouseSampleAccumulator[1];
132 }
133
134 rawXValue *= 0.1f;
135 rawYValue *= 0.1f;
136
137 mMouseSampleAccumulator[0] = 0;
138 mMouseSampleAccumulator[1] = 0;
139
140 axisMoved(0, -rawXValue, (UINT32)InputAxis::MouseX);
141 axisMoved(0, -rawYValue, (UINT32)InputAxis::MouseY);
142 }
143
144 void Input::_triggerCallbacks()
145 {
146 Vector2I pointerPos;
147 float mouseScroll;
148 OSPointerButtonStates pointerState;
149
150 {
151 Lock lock(mMutex);
152
153 std::swap(mQueuedEvents[0], mQueuedEvents[1]);
154
155 std::swap(mButtonDownEvents[0], mButtonDownEvents[1]);
156 std::swap(mButtonUpEvents[0], mButtonUpEvents[1]);
157
158 std::swap(mPointerPressedEvents[0], mPointerPressedEvents[1]);
159 std::swap(mPointerReleasedEvents[0], mPointerReleasedEvents[1]);
160 std::swap(mPointerDoubleClickEvents[0], mPointerDoubleClickEvents[1]);
161
162 std::swap(mTextInputEvents[0], mTextInputEvents[1]);
163 std::swap(mCommandEvents[0], mCommandEvents[1]);
164
165 pointerPos = mPointerPosition;
166 mouseScroll = mMouseScroll;
167 pointerState = mPointerState;
168
169 mMouseScroll = 0.0f;
170 }
171
172 if(pointerPos != mLastPointerPosition || mouseScroll != 0.0f)
173 {
174 PointerEvent event;
175 event.alt = false;
176 event.shift = pointerState.shift;
177 event.control = pointerState.ctrl;
178 event.buttonStates[0] = pointerState.mouseButtons[0];
179 event.buttonStates[1] = pointerState.mouseButtons[1];
180 event.buttonStates[2] = pointerState.mouseButtons[2];
181 event.mouseWheelScrollAmount = mouseScroll;
182
183 event.type = PointerEventType::CursorMoved;
184 event.screenPos = pointerPos;
185
186 if (mLastPositionSet)
187 mPointerDelta = event.screenPos - mLastPointerPosition;
188
189 event.delta = mPointerDelta;
190
191 onPointerMoved(event);
192
193 mLastPointerPosition = event.screenPos;
194 mLastPositionSet = true;
195 }
196
197 for (auto& event : mQueuedEvents[1])
198 {
199 switch (event.type)
200 {
201 case EventType::ButtonDown:
202 {
203 const ButtonEvent& eventData = mButtonDownEvents[1][event.idx];
204
205 mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] = ButtonState::ToggledOn;
206 onButtonDown(mButtonDownEvents[1][event.idx]);
207 }
208 break;
209 case EventType::ButtonUp:
210 {
211 const ButtonEvent& eventData = mButtonUpEvents[1][event.idx];
212
213 while (eventData.deviceIdx >= (UINT32)mDevices.size())
214 mDevices.push_back(DeviceData());
215
216 if (mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] == ButtonState::ToggledOn)
217 mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] = ButtonState::ToggledOnOff;
218 else
219 mDevices[eventData.deviceIdx].keyStates[eventData.buttonCode & 0x0000FFFF] = ButtonState::ToggledOff;
220
221 onButtonUp(mButtonUpEvents[1][event.idx]);
222 }
223 break;
224 case EventType::PointerDown:
225 {
226 const PointerEvent& eventData = mPointerPressedEvents[1][event.idx];
227 mPointerButtonStates[(UINT32)eventData.button] = ButtonState::ToggledOn;
228
229 onPointerPressed(eventData);
230 }
231 break;
232 case EventType::PointerUp:
233 {
234 const PointerEvent& eventData = mPointerReleasedEvents[1][event.idx];
235
236 if (mPointerButtonStates[(UINT32)eventData.button] == ButtonState::ToggledOn)
237 mPointerButtonStates[(UINT32)eventData.button] = ButtonState::ToggledOnOff;
238 else
239 mPointerButtonStates[(UINT32)eventData.button] = ButtonState::ToggledOff;
240
241 onPointerReleased(eventData);
242 }
243 break;
244 case EventType::PointerDoubleClick:
245 mPointerDoubleClicked = true;
246 onPointerDoubleClick(mPointerDoubleClickEvents[1][event.idx]);
247 break;
248 case EventType::TextInput:
249 onCharInput(mTextInputEvents[1][event.idx]);
250 break;
251 case EventType::Command:
252 onInputCommand(mCommandEvents[1][event.idx]);
253 break;
254 default:
255 break;
256 }
257 }
258
259 mQueuedEvents[1].clear();
260 mButtonDownEvents[1].clear();
261 mButtonUpEvents[1].clear();
262 mPointerPressedEvents[1].clear();
263 mPointerReleasedEvents[1].clear();
264 mPointerDoubleClickEvents[1].clear();
265 mTextInputEvents[1].clear();
266 mCommandEvents[1].clear();
267 }
268
269 void Input::inputWindowChanged(RenderWindow& win)
270 {
271 UINT64 hWnd = 0;
272 win.getCustomAttribute("WINDOW", &hWnd);
273
274 if(mKeyboard != nullptr)
275 mKeyboard->changeCaptureContext(hWnd);
276
277 if(mMouse != nullptr)
278 mMouse->changeCaptureContext(hWnd);
279
280 for (auto& gamepad : mGamepads)
281 gamepad->changeCaptureContext(hWnd);
282 }
283
284 void Input::inputFocusLost()
285 {
286 if(mKeyboard != nullptr)
287 mKeyboard->changeCaptureContext((UINT64)-1);
288
289 if(mMouse != nullptr)
290 mMouse->changeCaptureContext((UINT64)-1);
291
292 for (auto& gamepad : mGamepads)
293 gamepad->changeCaptureContext((UINT64)-1);
294 }
295
296 void Input::_notifyMouseMoved(INT32 relX, INT32 relY, INT32 relZ)
297 {
298 mMouseSampleAccumulator[0] += relX;
299 mMouseSampleAccumulator[1] += relY;
300
301 mTotalMouseNumSamples[0] += Math::roundToInt(Math::abs((float)relX));
302 mTotalMouseNumSamples[1] += Math::roundToInt(Math::abs((float)relY));
303
304 // Update sample times used for determining sampling rate. But only if something was
305 // actually sampled, and only if this isn't the first non-zero sample.
306 if (mLastMouseUpdateFrame != gTime().getFrameIdx())
307 {
308 if (relX != 0 && !Math::approxEquals(mMouseSmoothedAxis[0], 0.0f))
309 mTotalMouseSamplingTime[0] += gTime().getFrameDelta();
310
311 if (relY != 0 && !Math::approxEquals(mMouseSmoothedAxis[1], 0.0f))
312 mTotalMouseSamplingTime[1] += gTime().getFrameDelta();
313
314 mLastMouseUpdateFrame = gTime().getFrameIdx();
315 }
316
317 axisMoved(0, (float)relZ, (UINT32)InputAxis::MouseZ);
318 }
319
320 void Input::_notifyAxisMoved(UINT32 gamepadIdx, UINT32 axisIdx, INT32 value)
321 {
322 // Move axis values into [-1.0f, 1.0f] range
323 float axisRange = Math::abs((float)Gamepad::MAX_AXIS) + Math::abs((float)Gamepad::MIN_AXIS);
324
325 float axisValue = ((value + Math::abs((float)Gamepad::MIN_AXIS)) / axisRange) * 2.0f - 1.0f;
326 axisMoved(gamepadIdx, axisValue, axisIdx);
327 }
328
329 void Input::_notifyButtonPressed(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
330 {
331 buttonDown(deviceIdx, code, timestamp - mTimestampClockOffset);
332 }
333
334 void Input::_notifyButtonReleased(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
335 {
336 buttonUp(deviceIdx, code, timestamp - mTimestampClockOffset);
337 }
338
339 void Input::buttonDown(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
340 {
341 Lock lock(mMutex);
342
343 while (deviceIdx >= (UINT32)mDevices.size())
344 mDevices.push_back(DeviceData());
345
346 ButtonEvent btnEvent;
347 btnEvent.buttonCode = code;
348 btnEvent.timestamp = timestamp;
349 btnEvent.deviceIdx = deviceIdx;
350
351 mQueuedEvents[0].push_back(QueuedEvent(EventType::ButtonDown, (UINT32)mButtonDownEvents[0].size()));
352 mButtonDownEvents[0].push_back(btnEvent);
353 }
354
355 void Input::buttonUp(UINT32 deviceIdx, ButtonCode code, UINT64 timestamp)
356 {
357 Lock lock(mMutex);
358
359 ButtonEvent btnEvent;
360 btnEvent.buttonCode = code;
361 btnEvent.timestamp = timestamp;
362 btnEvent.deviceIdx = deviceIdx;
363
364 mQueuedEvents[0].push_back(QueuedEvent(EventType::ButtonUp, (UINT32)mButtonUpEvents[0].size()));
365 mButtonUpEvents[0].push_back(btnEvent);
366 }
367
368 void Input::axisMoved(UINT32 deviceIdx, float value, UINT32 axis)
369 {
370 // Note: This method must only ever be called from the main thread, as we don't lock access to axis data
371 while (deviceIdx >= (UINT32)mDevices.size())
372 mDevices.push_back(DeviceData());
373
374 Vector<float>& axes = mDevices[deviceIdx].axes;
375 while (axis >= (UINT32)axes.size())
376 axes.push_back(0.0f);
377
378 mDevices[deviceIdx].axes[axis] = value;
379 }
380
381 void Input::cursorMoved(const Vector2I& cursorPos, const OSPointerButtonStates& btnStates)
382 {
383 Lock lock(mMutex);
384
385 mPointerPosition = cursorPos;
386 mPointerState = btnStates;
387 }
388
389 void Input::cursorPressed(const Vector2I& cursorPos, OSMouseButton button, const OSPointerButtonStates& btnStates)
390 {
391 Lock lock(mMutex);
392
393 PointerEvent event;
394 event.alt = false;
395 event.shift = btnStates.shift;
396 event.control = btnStates.ctrl;
397 event.buttonStates[0] = btnStates.mouseButtons[0];
398 event.buttonStates[1] = btnStates.mouseButtons[1];
399 event.buttonStates[2] = btnStates.mouseButtons[2];
400
401 switch(button)
402 {
403 case OSMouseButton::Left:
404 event.button = PointerEventButton::Left;
405 break;
406 case OSMouseButton::Middle:
407 event.button = PointerEventButton::Middle;
408 break;
409 case OSMouseButton::Right:
410 event.button = PointerEventButton::Right;
411 break;
412 default:
413 break;
414 }
415
416 event.screenPos = cursorPos;
417 event.type = PointerEventType::ButtonPressed;
418
419 mQueuedEvents[0].push_back(QueuedEvent(EventType::PointerDown, (UINT32)mPointerPressedEvents[0].size()));
420 mPointerPressedEvents[0].push_back(event);
421 }
422
423 void Input::cursorReleased(const Vector2I& cursorPos, OSMouseButton button, const OSPointerButtonStates& btnStates)
424 {
425 Lock lock(mMutex);
426
427 PointerEvent event;
428 event.alt = false;
429 event.shift = btnStates.shift;
430 event.control = btnStates.ctrl;
431 event.buttonStates[0] = btnStates.mouseButtons[0];
432 event.buttonStates[1] = btnStates.mouseButtons[1];
433 event.buttonStates[2] = btnStates.mouseButtons[2];
434
435 switch(button)
436 {
437 case OSMouseButton::Left:
438 event.button = PointerEventButton::Left;
439 break;
440 case OSMouseButton::Middle:
441 event.button = PointerEventButton::Middle;
442 break;
443 case OSMouseButton::Right:
444 event.button = PointerEventButton::Right;
445 break;
446 default:
447 break;
448 }
449
450 event.screenPos = cursorPos;
451 event.type = PointerEventType::ButtonReleased;
452
453 mQueuedEvents[0].push_back(QueuedEvent(EventType::PointerUp, (UINT32)mPointerReleasedEvents[0].size()));
454 mPointerReleasedEvents[0].push_back(event);
455 }
456
457 void Input::cursorDoubleClick(const Vector2I& cursorPos, const OSPointerButtonStates& btnStates)
458 {
459 Lock lock(mMutex);
460
461 PointerEvent event;
462 event.alt = false;
463 event.shift = btnStates.shift;
464 event.control = btnStates.ctrl;
465 event.buttonStates[0] = btnStates.mouseButtons[0];
466 event.buttonStates[1] = btnStates.mouseButtons[1];
467 event.buttonStates[2] = btnStates.mouseButtons[2];
468 event.button = PointerEventButton::Left;
469 event.screenPos = cursorPos;
470 event.type = PointerEventType::DoubleClick;
471
472 mQueuedEvents[0].push_back(QueuedEvent(EventType::PointerDoubleClick, (UINT32)mPointerDoubleClickEvents[0].size()));
473 mPointerDoubleClickEvents[0].push_back(event);
474 }
475
476 void Input::inputCommandEntered(InputCommandType commandType)
477 {
478 Lock lock(mMutex);
479
480 mQueuedEvents[0].push_back(QueuedEvent(EventType::Command, (UINT32)mCommandEvents[0].size()));
481 mCommandEvents[0].push_back(commandType);
482 }
483
484 void Input::mouseWheelScrolled(float scrollPos)
485 {
486 Lock lock(mMutex);
487
488 mMouseScroll = scrollPos;
489 }
490
491 void Input::charInput(UINT32 chr)
492 {
493 Lock lock(mMutex);
494
495 TextInputEvent textInputEvent;
496 textInputEvent.textChar = chr;
497
498 mQueuedEvents[0].push_back(QueuedEvent(EventType::TextInput, (UINT32)mTextInputEvents[0].size()));
499 mTextInputEvents[0].push_back(textInputEvent);
500 }
501
502 float Input::getAxisValue(UINT32 type, UINT32 deviceIdx) const
503 {
504 if (deviceIdx >= (UINT32)mDevices.size())
505 return 0.0f;
506
507 const Vector<float>& axes = mDevices[deviceIdx].axes;
508 if (type >= (UINT32)axes.size())
509 return 0.0f;
510
511 return axes[type];
512 }
513
514 bool Input::isButtonHeld(ButtonCode button, UINT32 deviceIdx) const
515 {
516 if (deviceIdx >= (UINT32)mDevices.size())
517 return false;
518
519 return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::On ||
520 mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn ||
521 mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
522 }
523
524 bool Input::isButtonUp(ButtonCode button, UINT32 deviceIdx) const
525 {
526 if (deviceIdx >= (UINT32)mDevices.size())
527 return false;
528
529 return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOff ||
530 mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
531 }
532
533 bool Input::isButtonDown(ButtonCode button, UINT32 deviceIdx) const
534 {
535 if (deviceIdx >= (UINT32)mDevices.size())
536 return false;
537
538 return mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOn ||
539 mDevices[deviceIdx].keyStates[button & 0x0000FFFF] == ButtonState::ToggledOnOff;
540 }
541
542 bool Input::isPointerButtonHeld(PointerEventButton pointerButton) const
543 {
544 return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::On ||
545 mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn ||
546 mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
547 }
548
549 bool Input::isPointerButtonUp(PointerEventButton pointerButton) const
550 {
551 return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOff ||
552 mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
553 }
554
555 bool Input::isPointerButtonDown(PointerEventButton pointerButton) const
556 {
557 return mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOn ||
558 mPointerButtonStates[(UINT32)pointerButton] == ButtonState::ToggledOnOff;
559 }
560
561 bool Input::isPointerDoubleClicked() const
562 {
563 return mPointerDoubleClicked;
564 }
565
566 Vector2I Input::getPointerPosition() const
567 {
568 return mPointerPosition;
569 }
570
571 String Input::getDeviceName(InputDevice type, UINT32 idx)
572 {
573 switch(type)
574 {
575 case InputDevice::Keyboard:
576 if (mKeyboard != nullptr && idx == 0)
577 return mKeyboard->getName();
578
579 return StringUtil::BLANK;
580 case InputDevice::Mouse:
581 if (mMouse != nullptr && idx == 0)
582 return mMouse->getName();
583
584 return StringUtil::BLANK;
585 case InputDevice::Gamepad:
586 if (idx < (UINT32)mGamepads.size())
587 return mGamepads[idx]->getName();
588
589 return StringUtil::BLANK;
590 default:
591 return StringUtil::BLANK;
592 }
593 }
594
595 void Input::setMouseSmoothing(bool enable)
596 {
597 mMouseSmoothingEnabled = enable;
598 }
599
600 float Input::smoothMouse(float value, UINT32 idx)
601 {
602 UINT32 sampleCount = 1;
603
604 float deltaTime = gTime().getFrameDelta();
605 if (deltaTime < 0.25f)
606 {
607 float secondsPerSample = mTotalMouseSamplingTime[idx] / mTotalMouseNumSamples[idx];
608
609 if (value == 0.0f)
610 {
611 mMouseZeroTime[idx] += deltaTime;
612 if (mMouseZeroTime[idx] < secondsPerSample)
613 value = mMouseSmoothedAxis[idx] * deltaTime / secondsPerSample;
614 else
615 mMouseSmoothedAxis[idx] = 0;
616 }
617 else
618 {
619 mMouseZeroTime[idx] = 0;
620 if (mMouseSmoothedAxis[idx] != 0)
621 {
622 if (deltaTime < secondsPerSample * (sampleCount + 1))
623 value = value * deltaTime / (secondsPerSample * sampleCount);
624 else
625 sampleCount = Math::roundToInt(deltaTime / secondsPerSample);
626 }
627
628 mMouseSmoothedAxis[idx] = value / sampleCount;
629 }
630 }
631 else
632 {
633 mMouseSmoothedAxis[idx] = 0.0f;
634 mMouseZeroTime[idx] = 0.0f;
635 }
636
637 return value;
638 }
639
640 Input& gInput()
641 {
642 return Input::instance();
643 }
644}
645