Add Touch input handling
This commit is contained in:
17
openVulkanoCpp/Host/iOS/OpenVulkanoView.h
Normal file
17
openVulkanoCpp/Host/iOS/OpenVulkanoView.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <MetalKit/MTKView.h>
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OpenVulkanoView
|
||||
|
||||
//@interface OpenVulkanoView : UIView
|
||||
@interface OpenVulkanoView : MTKView
|
||||
@end
|
||||
125
openVulkanoCpp/Host/iOS/OpenVulkanoView.mm
Normal file
125
openVulkanoCpp/Host/iOS/OpenVulkanoView.mm
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#import "OpenVulkanoView.h"
|
||||
|
||||
#include "Input/Touch/InputDeviceTouch.hpp"
|
||||
#include "Input/InputManager.hpp"
|
||||
#include <map>
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OpenVulkanoView
|
||||
|
||||
using namespace OpenVulkano;
|
||||
|
||||
@interface OpenVulkanoView()
|
||||
{
|
||||
CADisplayLink * m_displayLink;
|
||||
std::map<UITouch*, int32_t> m_touchToIdMap;
|
||||
Input::InputDeviceTouch m_touchDevice;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation OpenVulkanoView
|
||||
/** Returns a Metal-compatible layer. */
|
||||
+(Class) layerClass { return [CAMetalLayer class]; }
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self) {
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)commonInit {
|
||||
[self setMultipleTouchEnabled:YES];
|
||||
m_touchDevice.Init(); //TODO link window
|
||||
Input::InputManager::GetInstance()->RegisterInputDevice(&m_touchDevice);
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
for(auto& it : m_touchToIdMap) {
|
||||
m_touchDevice.RemoveTouch(it.second);
|
||||
}
|
||||
Input::InputManager::GetInstance()->UnregisterInputDevice(&m_touchDevice);
|
||||
m_touchDevice.Close();
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (Math::Vector2f)getTouchPosition:(UITouch*)touch
|
||||
{
|
||||
CGPoint uitouchLocation = [touch locationInView:touch.view];
|
||||
return { uitouchLocation.x * self.contentScaleFactor, uitouchLocation.y * self.contentScaleFactor };
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet*)inTouches withEvent:(UIEvent*)inEvent
|
||||
{
|
||||
for (UITouch* touch in inTouches)
|
||||
{
|
||||
Math::Vector2f touchLocation = [self getTouchPosition: touch];
|
||||
uint32_t touchId = m_touchDevice.AddTouch(touchLocation);
|
||||
m_touchDevice.TouchDown(touchId);
|
||||
m_touchToIdMap.insert(std::make_pair(touch, touchId));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet*)inTouches withEvent:(UIEvent*)inEvent
|
||||
{
|
||||
for (UITouch* touch in inTouches)
|
||||
{
|
||||
Math::Vector2f touchLocation = [self getTouchPosition: touch];
|
||||
|
||||
auto it = m_touchToIdMap.find(touch);
|
||||
if (it != m_touchToIdMap.end())
|
||||
{
|
||||
m_touchDevice.TouchUp(it->second);
|
||||
m_touchDevice.RemoveTouch(it->second);
|
||||
m_touchToIdMap.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet*)inTouches withEvent:(UIEvent*)inEvent
|
||||
{
|
||||
for (UITouch* touch in inTouches)
|
||||
{
|
||||
Math::Vector2f touchLocation = [self getTouchPosition: touch];
|
||||
|
||||
auto it = m_touchToIdMap.find(touch);
|
||||
if (it != m_touchToIdMap.end())
|
||||
{
|
||||
m_touchDevice.TouchUp(it->second);
|
||||
m_touchDevice.RemoveTouch(it->second);
|
||||
m_touchToIdMap.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet*)inTouches withEvent:(UIEvent*)inEvent
|
||||
{
|
||||
for (UITouch* touch in inTouches)
|
||||
{
|
||||
Math::Vector2f touchLocation = [self getTouchPosition: touch];
|
||||
|
||||
auto it = m_touchToIdMap.find(touch);
|
||||
if (it != m_touchToIdMap.end())
|
||||
{
|
||||
m_touchDevice.TouchMoved(it->second, touchLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <MetalKit/MTKView.h>
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OpenVulkanoViewController
|
||||
@@ -14,10 +13,3 @@
|
||||
-(void*) makeGraphicsApp;
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OpenVulkanoView
|
||||
|
||||
@interface OpenVulkanoView : MTKView
|
||||
@end
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "ExampleApps/CubesExampleApp.hpp"
|
||||
#include "Base/UI/IVulkanWindow.hpp"
|
||||
|
||||
#import <MetalKit/MTKView.h>
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OpenVulkanoViewController
|
||||
@@ -203,13 +204,3 @@ public:
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark OpenVulkanoView
|
||||
|
||||
@implementation OpenVulkanoView
|
||||
|
||||
/** Returns a Metal-compatible layer. */
|
||||
+(Class) layerClass { return [CAMetalLayer class]; }
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -242,6 +242,34 @@ namespace OpenVulkano::Input
|
||||
};
|
||||
};
|
||||
|
||||
class Touch
|
||||
{
|
||||
public:
|
||||
enum Button: int16_t
|
||||
{
|
||||
BUTTON_TAP = 0,
|
||||
BUTTON_TWO_FINGER_TAP
|
||||
};
|
||||
|
||||
enum Axis : int16_t
|
||||
{
|
||||
AXIS_TAP_X = 0,
|
||||
AXIS_TAP_Y,
|
||||
AXIS_TAP_DURATION,
|
||||
AXIS_TAP_TWO_FINGERS_X,
|
||||
AXIS_TAP_TWO_FINGERS_Y,
|
||||
AXIS_TAP_TWO_FINGER_DURATION,
|
||||
AXIS_PAN_X,
|
||||
AXIS_PAN_Y,
|
||||
AXIS_PAN_TWO_FINGERS_X,
|
||||
AXIS_PAN_TWO_FINGERS_Y,
|
||||
AXIS_PINCH,
|
||||
AXIS_PINCH_CENTER_X,
|
||||
AXIS_PINCH_CENTER_Y,
|
||||
AXIS_LAST = AXIS_PINCH_CENTER_Y
|
||||
};
|
||||
};
|
||||
|
||||
private:
|
||||
InputDeviceType deviceType;
|
||||
InputType type;
|
||||
@@ -268,6 +296,14 @@ namespace OpenVulkano::Input
|
||||
deviceType(InputDeviceType::CONTROLLER), type(InputType::AXIS), key(controllerAxis)
|
||||
{}
|
||||
|
||||
InputKey(Touch::Button touchButton) :
|
||||
deviceType(InputDeviceType::TOUCH), type(InputType::BUTTON), key(touchButton)
|
||||
{}
|
||||
|
||||
InputKey(Touch::Axis touchAxis) :
|
||||
deviceType(InputDeviceType::TOUCH), type(InputType::AXIS), key(touchAxis)
|
||||
{}
|
||||
|
||||
[[nodiscard]] InputDeviceType GetInputDeviceType() const { return deviceType; }
|
||||
|
||||
[[nodiscard]] InputType GetInputType() const { return type; }
|
||||
|
||||
49
openVulkanoCpp/Input/Touch/Gesture.hpp
Normal file
49
openVulkanoCpp/Input/Touch/Gesture.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
class Touch;
|
||||
class GestureProcessor;
|
||||
|
||||
class Gesture
|
||||
{
|
||||
friend class GestureProcessor;
|
||||
|
||||
uint32_t m_gestureType = -1;
|
||||
bool m_active = false;
|
||||
GestureProcessor* m_processor = nullptr;
|
||||
|
||||
public:
|
||||
static constexpr uint32_t TYPE_TAP = 0;
|
||||
static constexpr uint32_t TYPE_PAN = 1;
|
||||
static constexpr uint32_t TYPE_PINCH = 2;
|
||||
|
||||
Gesture(uint32_t gestureType) : m_gestureType(gestureType) {}
|
||||
virtual ~Gesture() = default;
|
||||
|
||||
virtual void Cancel() {}
|
||||
virtual void Reset() {}
|
||||
|
||||
virtual void TouchDown(const Touch& touch) {}
|
||||
virtual void TouchUp(const Touch& touch) {}
|
||||
virtual void TouchMoved(const Touch& touch) {}
|
||||
|
||||
void SetActive(bool active) { m_active = active; }
|
||||
|
||||
[[nodiscard]] bool IsActive() const { return m_active; }
|
||||
|
||||
[[nodiscard]] uint32_t GetType() const { return m_gestureType; }
|
||||
|
||||
// Impl in GestureProcessor.cpp
|
||||
bool ResolveConflicts();
|
||||
void RemoveFromProcessor();
|
||||
};
|
||||
}
|
||||
34
openVulkanoCpp/Input/Touch/GestureInfos.hpp
Normal file
34
openVulkanoCpp/Input/Touch/GestureInfos.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Math/Math.hpp"
|
||||
#include <chrono>
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
struct PanInfo
|
||||
{
|
||||
Math::Vector2f position;
|
||||
Math::Vector2f distance;
|
||||
Math::Vector2f delta;
|
||||
|
||||
float scale = 0.0f;
|
||||
};
|
||||
|
||||
struct PinchInfo
|
||||
{
|
||||
Math::Vector2f position;
|
||||
float scale = 0.0f;
|
||||
};
|
||||
|
||||
struct TapInfo
|
||||
{
|
||||
Math::Vector2f position;
|
||||
std::chrono::seconds duration;
|
||||
};
|
||||
}
|
||||
174
openVulkanoCpp/Input/Touch/GesturePan.cpp
Normal file
174
openVulkanoCpp/Input/Touch/GesturePan.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "GesturePan.hpp"
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr float MIN_DISTANCE = 20;
|
||||
constexpr float MIN_DISTANCE2 = MIN_DISTANCE * MIN_DISTANCE;
|
||||
}
|
||||
|
||||
void GesturePan::TryStart(const Touch& touch)
|
||||
{
|
||||
uint32_t dragCount = 0, existingActive = 0;
|
||||
for (const auto& t : m_pendingTouches)
|
||||
{
|
||||
if (t.isDrag) dragCount++;
|
||||
if (t.active) existingActive++;
|
||||
}
|
||||
|
||||
if (dragCount >= m_requiredTouchInputs)
|
||||
{
|
||||
if (!IsActive() && !ResolveConflicts()) return;
|
||||
|
||||
uint32_t activeCount = existingActive;
|
||||
for (auto& t : m_pendingTouches)
|
||||
{
|
||||
if (t.isDrag && !t.active && activeCount < m_requiredTouchInputs)
|
||||
{
|
||||
t.active = true;
|
||||
activeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
m_paused = false;
|
||||
|
||||
if (IsActive())
|
||||
{
|
||||
m_panInfo.position = CalculatePosition();
|
||||
|
||||
OnPanMoved(this, m_panInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetActive(true);
|
||||
m_panInfo.position = CalculatePosition();
|
||||
m_panInfo.distance = Math::Vector2f();
|
||||
m_panInfo.delta = Math::Vector2f();
|
||||
|
||||
OnPanStarted.NotifyAll(this, m_panInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Math::Vector2f GesturePan::CalculatePosition() const
|
||||
{
|
||||
Math::Vector2f gesturePos = Math::Vector2f();
|
||||
if (m_pendingTouches.size() >= m_requiredTouchInputs && IsActive() && !m_paused)
|
||||
{
|
||||
int activePointers = 0;
|
||||
for (const auto& t : m_pendingTouches)
|
||||
{
|
||||
gesturePos += t.currentPosition;
|
||||
activePointers++;
|
||||
}
|
||||
gesturePos /= activePointers > 0 ? activePointers : 1;
|
||||
}
|
||||
return gesturePos;
|
||||
}
|
||||
|
||||
void GesturePan::Cancel()
|
||||
{
|
||||
for (auto& pointer : m_pendingTouches)
|
||||
{
|
||||
pointer.active = false;
|
||||
}
|
||||
|
||||
SetActive(false);
|
||||
m_paused = false;
|
||||
|
||||
m_panInfo.delta = Math::Vector2f();
|
||||
OnPanEnded(this, m_panInfo);
|
||||
|
||||
m_panInfo.distance = Math::Vector2f();
|
||||
m_panInfo.position = Math::Vector2f();
|
||||
}
|
||||
|
||||
void GesturePan::TouchDown(const Touch& touch)
|
||||
{
|
||||
TouchInfo ti;
|
||||
ti.touchId = touch.GetId();
|
||||
ti.currentPosition = touch.GetPosition();
|
||||
ti.initialPosition = touch.GetPosition();
|
||||
|
||||
m_pendingTouches.push_back(ti);
|
||||
if (IsActive())
|
||||
{
|
||||
m_panInfo.position = CalculatePosition();
|
||||
}
|
||||
}
|
||||
|
||||
void GesturePan::TouchUp(const Touch& touch)
|
||||
{
|
||||
if (!m_pendingTouches.empty())
|
||||
{
|
||||
for (auto touchIter = m_pendingTouches.begin(); touchIter != m_pendingTouches.end(); touchIter++)
|
||||
{
|
||||
if (touch.GetId() == touchIter->touchId)
|
||||
{
|
||||
if (touchIter->active) m_paused = true;
|
||||
|
||||
m_pendingTouches.erase(touchIter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pendingTouches.empty() && IsActive())
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
else if (IsActive())
|
||||
{
|
||||
m_panInfo.position = CalculatePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GesturePan::TouchMoved(const Touch& touch)
|
||||
{
|
||||
if (m_pendingTouches.size() > 0)
|
||||
{
|
||||
bool isPending = false;
|
||||
bool isActive = false;
|
||||
for (auto& t : m_pendingTouches)
|
||||
{
|
||||
if (touch.GetId() == t.touchId)
|
||||
{
|
||||
t.currentPosition = touch.GetPosition();
|
||||
|
||||
Math::Vector2f displacement = t.currentPosition - t.initialPosition;
|
||||
if (Math::Utils::length2(displacement) > MIN_DISTANCE2)
|
||||
{
|
||||
t.isDrag = true;
|
||||
}
|
||||
|
||||
if (t.active) isActive = true;
|
||||
|
||||
isPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPending)
|
||||
{
|
||||
if (!IsActive() || m_paused)
|
||||
{
|
||||
TryStart(touch);
|
||||
}
|
||||
else if (isActive)
|
||||
{
|
||||
Math::Vector2f newPosition = CalculatePosition();
|
||||
m_panInfo.delta = newPosition - m_panInfo.position;
|
||||
m_panInfo.distance += m_panInfo.delta;
|
||||
m_panInfo.position = newPosition;
|
||||
OnPanMoved.NotifyAll(this, m_panInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
openVulkanoCpp/Input/Touch/GesturePan.hpp
Normal file
47
openVulkanoCpp/Input/Touch/GesturePan.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Gesture.hpp"
|
||||
#include "TouchInfo.hpp"
|
||||
#include "GestureInfos.hpp"
|
||||
#include "Base/Event.hpp"
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
|
||||
class GesturePan final : public Gesture
|
||||
{
|
||||
PanInfo m_panInfo;
|
||||
std::vector<TouchInfo> m_pendingTouches;
|
||||
uint32_t m_requiredTouchInputs;
|
||||
bool m_paused = false;
|
||||
|
||||
void TryStart(const Touch& touch);
|
||||
|
||||
Math::Vector2f CalculatePosition() const;
|
||||
|
||||
public:
|
||||
GesturePan(uint32_t requiredTouchInputs)
|
||||
: Gesture(TYPE_PAN), m_requiredTouchInputs(requiredTouchInputs)
|
||||
{}
|
||||
|
||||
void Cancel() override;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
if (IsActive()) Cancel();
|
||||
m_pendingTouches.clear();
|
||||
}
|
||||
|
||||
void TouchDown(const Touch& touch) override;
|
||||
void TouchUp(const Touch& touch) override;
|
||||
void TouchMoved(const Touch& touch) override;
|
||||
|
||||
Event<GesturePan*, PanInfo&> OnPanStarted, OnPanMoved, OnPanEnded;
|
||||
};
|
||||
}
|
||||
244
openVulkanoCpp/Input/Touch/GesturePinch.cpp
Normal file
244
openVulkanoCpp/Input/Touch/GesturePinch.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "GesturePinch.hpp"
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr float MIN_DISTANCE = 2;
|
||||
constexpr float MIN_DISTANCE2 = MIN_DISTANCE * MIN_DISTANCE;
|
||||
constexpr uint32_t REQUIRED_TOUCHES = 2;
|
||||
}
|
||||
|
||||
void GesturePinch::TryStart()
|
||||
{
|
||||
uint32_t dragCount = 0, existingActive = 0;
|
||||
|
||||
for (const auto& t : m_pendingTouches)
|
||||
{
|
||||
if (t.isDrag) dragCount++;
|
||||
if (t.active) existingActive++;
|
||||
}
|
||||
|
||||
if (dragCount >= REQUIRED_TOUCHES)
|
||||
{
|
||||
if (!IsActive() && !ResolveConflicts()) return;
|
||||
|
||||
uint32_t activeCount = existingActive;
|
||||
|
||||
for (auto& t : m_pendingTouches)
|
||||
{
|
||||
if (t.isDrag && !t.active && activeCount < REQUIRED_TOUCHES)
|
||||
{
|
||||
t.active = true;
|
||||
++activeCount;
|
||||
}
|
||||
}
|
||||
|
||||
m_paused = false;
|
||||
|
||||
if (!IsActive())
|
||||
{
|
||||
SetActive(true);
|
||||
|
||||
m_initialDistance = CalculateDistance();
|
||||
|
||||
float newDistance = m_initialDistance;
|
||||
|
||||
if (m_initialDistance > 0)
|
||||
{
|
||||
m_pinchInfo.scale = newDistance > 0 ? m_initialDistance / newDistance : m_lastDistance;
|
||||
m_pinchInfo.position = CalculateCenter();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pinchInfo.scale = 1.0f;
|
||||
m_pinchInfo.position = m_pendingTouches.front().currentPosition;
|
||||
}
|
||||
|
||||
if (m_lastDistance == 0)
|
||||
{
|
||||
m_lastDistance = newDistance;
|
||||
}
|
||||
|
||||
// fire event
|
||||
OnPinchStarted.NotifyAll(this, m_pinchInfo);
|
||||
|
||||
m_lastDistance = newDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnPinchMoved.NotifyAll(this, m_pinchInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float GesturePinch::LinearLength(const std::vector<Math::Vector2f>& points) const
|
||||
{
|
||||
auto start = points.begin();
|
||||
if (start == points.end()) return 0;
|
||||
|
||||
auto finish = start + 1;
|
||||
float sum = 0;
|
||||
|
||||
while (finish != points.end())
|
||||
{
|
||||
Math::Vector2f displacement = *start - *finish;
|
||||
sum += Math::Utils::length(displacement);
|
||||
start = finish++;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
float GesturePinch::CalculateDistance() const
|
||||
{
|
||||
if (m_pendingTouches.size() >= REQUIRED_TOUCHES && IsActive() && !m_paused)
|
||||
{
|
||||
std::vector<Math::Vector2f> positions;
|
||||
for (const auto& t : m_pendingTouches)
|
||||
{
|
||||
if (t.active) positions.push_back(t.currentPosition);
|
||||
}
|
||||
return LinearLength(positions);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Math::Vector2f GesturePinch::CalculateCenter() const
|
||||
{
|
||||
auto center = Math::Vector2f();
|
||||
if (m_pendingTouches.size() >= REQUIRED_TOUCHES && IsActive() && !m_paused)
|
||||
{
|
||||
std::vector<Math::Vector2f> positions;
|
||||
for (const auto& t : m_pendingTouches)
|
||||
{
|
||||
if (t.active) positions.push_back(t.currentPosition);
|
||||
}
|
||||
for (const auto& position : positions)
|
||||
{
|
||||
center += position / (float)positions.size();
|
||||
}
|
||||
}
|
||||
return center;
|
||||
}
|
||||
|
||||
void GesturePinch::Cancel()
|
||||
{
|
||||
for (auto& t : m_pendingTouches)
|
||||
{
|
||||
t.active = false;
|
||||
}
|
||||
SetActive(false);
|
||||
m_paused = false;
|
||||
OnPinchEnded.NotifyAll(this, m_pinchInfo);
|
||||
m_pinchInfo.position = {};
|
||||
}
|
||||
|
||||
void GesturePinch::TouchDown(const Touch& touch)
|
||||
{
|
||||
TouchInfo ti;
|
||||
ti.touchId = touch.GetId();
|
||||
ti.currentPosition = touch.GetPosition();
|
||||
ti.initialPosition = touch.GetPosition();
|
||||
ti.delta = touch.GetPosition() - touch.GetLastPosition();
|
||||
|
||||
m_pendingTouches.push_back(ti);
|
||||
}
|
||||
|
||||
void GesturePinch::TouchMoved(const Touch& touch)
|
||||
{
|
||||
if (!m_pendingTouches.empty())
|
||||
{
|
||||
bool isPending = false;
|
||||
bool isActive = false;
|
||||
bool oneIsDrag = false;
|
||||
for (auto& t : m_pendingTouches)
|
||||
{
|
||||
if (touch.GetId() == t.touchId)
|
||||
{
|
||||
t.currentPosition = touch.GetPosition();
|
||||
t.delta = touch.GetPosition() - t.initialPosition;
|
||||
|
||||
Math::Vector2f displacement = t.currentPosition - t.initialPosition;
|
||||
|
||||
if (Math::Utils::length2(displacement) > MIN_DISTANCE2)
|
||||
{
|
||||
t.isDrag = true;
|
||||
oneIsDrag = true;
|
||||
}
|
||||
|
||||
if (t.active) isActive = true;
|
||||
|
||||
isPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (oneIsDrag)
|
||||
{
|
||||
// Just to activate both cursors
|
||||
for (auto& t : m_pendingTouches)
|
||||
{
|
||||
t.isDrag = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPending)
|
||||
{
|
||||
if (!IsActive() || m_paused)
|
||||
{
|
||||
TryStart();
|
||||
}
|
||||
else if (isActive)
|
||||
{
|
||||
float newDistance = CalculateDistance();
|
||||
|
||||
if (m_lastDistance == 0)
|
||||
{
|
||||
m_lastDistance = newDistance;
|
||||
}
|
||||
|
||||
if (m_initialDistance > 0)
|
||||
{
|
||||
m_pinchInfo.scale = newDistance > 0 ? m_initialDistance / newDistance : m_lastDistance;
|
||||
m_pinchInfo.position = CalculateCenter();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pinchInfo.scale = 1.0f;
|
||||
m_pinchInfo.position = m_pendingTouches.front().currentPosition;
|
||||
}
|
||||
|
||||
OnPinchMoved.NotifyAll(this, m_pinchInfo);
|
||||
|
||||
m_lastDistance = newDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GesturePinch::TouchUp(const Touch& touch)
|
||||
{
|
||||
if (!m_pendingTouches.empty())
|
||||
{
|
||||
for (auto touchIter = m_pendingTouches.begin(); touchIter != m_pendingTouches.end(); touchIter++)
|
||||
{
|
||||
if (touch.GetId() == touchIter->touchId)
|
||||
{
|
||||
if (touchIter->active) m_paused = true;
|
||||
|
||||
m_pendingTouches.erase(touchIter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pendingTouches.empty() && IsActive()) Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
49
openVulkanoCpp/Input/Touch/GesturePinch.hpp
Normal file
49
openVulkanoCpp/Input/Touch/GesturePinch.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Gesture.hpp"
|
||||
#include "TouchInfo.hpp"
|
||||
#include "GestureInfos.hpp"
|
||||
#include "Base/Event.hpp"
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
class GesturePinch final : public Gesture
|
||||
{
|
||||
PinchInfo m_pinchInfo;
|
||||
std::vector<TouchInfo> m_pendingTouches;
|
||||
float m_initialDistance = 0, m_lastDistance;
|
||||
bool m_paused = false;
|
||||
|
||||
void TryStart();
|
||||
|
||||
float LinearLength(std::vector<Math::Vector2f> const &points) const;
|
||||
|
||||
float CalculateDistance() const;
|
||||
|
||||
Math::Vector2f CalculateCenter() const;
|
||||
|
||||
public:
|
||||
GesturePinch() : Gesture(TYPE_PINCH) {}
|
||||
|
||||
void Cancel() override;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
if (IsActive()) Cancel();
|
||||
m_pendingTouches.clear();
|
||||
m_initialDistance = 0;
|
||||
}
|
||||
|
||||
void TouchDown(const Touch& touch) override;
|
||||
void TouchUp(const Touch& touch) override;
|
||||
void TouchMoved(const Touch& touch) override;
|
||||
|
||||
Event<GesturePinch*, PinchInfo&> OnPinchStarted, OnPinchMoved, OnPinchEnded;
|
||||
};
|
||||
}
|
||||
142
openVulkanoCpp/Input/Touch/GestureProcessor.cpp
Normal file
142
openVulkanoCpp/Input/Touch/GestureProcessor.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "GestureProcessor.hpp"
|
||||
#include "Touch.hpp"
|
||||
#include "Gesture.hpp"
|
||||
#include "Base/Logger.hpp"
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
bool Gesture::ResolveConflicts()
|
||||
{
|
||||
if (m_processor) return m_processor->ResolveConflicts(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Gesture::RemoveFromProcessor()
|
||||
{
|
||||
if (m_processor) m_processor->RemoveGesture(this);
|
||||
}
|
||||
|
||||
GestureProcessor::GestureProcessor()
|
||||
: m_gesturePan(1), m_gesturePanTwoFingers(2)
|
||||
{}
|
||||
|
||||
GestureProcessor::~GestureProcessor()
|
||||
{
|
||||
if (!m_gestures.empty()) Close();
|
||||
}
|
||||
|
||||
void GestureProcessor::Init()
|
||||
{
|
||||
AddGesture(&m_gesturePan);
|
||||
AddGesture(&m_gesturePanTwoFingers);
|
||||
AddGesture(&m_gesturePinch);
|
||||
AddGesture(&m_gestureTap);
|
||||
m_conflictResolutionDelegate = [this](const Gesture* existing, const Gesture* newGesture) {
|
||||
if(existing == &m_gesturePan && newGesture != &m_gestureTap)
|
||||
{
|
||||
Logger::INPUT->debug("Replacing pan with multitouch gesture");
|
||||
return ConflictResult::NewGesture;
|
||||
}
|
||||
return ConflictResult::BothGestures;
|
||||
};
|
||||
}
|
||||
|
||||
void GestureProcessor::Close()
|
||||
{
|
||||
for(const auto& gesture : m_gestures)
|
||||
{
|
||||
gesture->m_processor = nullptr;
|
||||
}
|
||||
m_gestures.clear();
|
||||
m_conflictResolutionDelegate = nullptr;
|
||||
}
|
||||
|
||||
void GestureProcessor::AddGesture(Gesture* gesture)
|
||||
{
|
||||
gesture->m_processor = this;
|
||||
m_gestures.push_back(gesture);
|
||||
}
|
||||
|
||||
void GestureProcessor::RemoveGesture(OpenVulkano::Input::Gesture* gesture)
|
||||
{
|
||||
for (auto it = m_gestures.begin(); it != m_gestures.end(); it++)
|
||||
{
|
||||
if (*it == gesture)
|
||||
{
|
||||
(*it)->m_processor = nullptr;
|
||||
m_gestures.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region forward events
|
||||
void GestureProcessor::Reset()
|
||||
{
|
||||
for (const auto& gesture : m_gestures)
|
||||
{
|
||||
gesture->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void GestureProcessor::TouchDown(const OpenVulkano::Input::Touch& touch)
|
||||
{
|
||||
for (const auto& gesture : m_gestures)
|
||||
{
|
||||
gesture->TouchDown(touch);
|
||||
}
|
||||
}
|
||||
|
||||
void GestureProcessor::TouchUp(const OpenVulkano::Input::Touch& touch)
|
||||
{
|
||||
for (const auto& gesture : m_gestures)
|
||||
{
|
||||
gesture->TouchUp(touch);
|
||||
}
|
||||
}
|
||||
|
||||
void GestureProcessor::TouchMoved(const OpenVulkano::Input::Touch& touch)
|
||||
{
|
||||
for (const auto& gesture : m_gestures)
|
||||
{
|
||||
gesture->TouchMoved(touch);
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
bool GestureProcessor::ResolveConflicts(Gesture* gesture)
|
||||
{
|
||||
if (!m_conflictResolutionDelegate) return true;
|
||||
bool canActivate = true;
|
||||
for (const auto& g : m_gestures)
|
||||
{
|
||||
if (g != gesture && g->IsActive())
|
||||
{
|
||||
ConflictResult conflictResult = m_conflictResolutionDelegate(g, gesture);
|
||||
|
||||
switch (conflictResult)
|
||||
{
|
||||
case ConflictResult::NeitherGesture:
|
||||
g->Reset();
|
||||
canActivate = false;
|
||||
break;
|
||||
case ConflictResult::ExistingGesture:
|
||||
canActivate = false;
|
||||
break;
|
||||
case ConflictResult::NewGesture:
|
||||
g->Reset();
|
||||
break;
|
||||
case ConflictResult::BothGestures:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return canActivate;
|
||||
}
|
||||
}
|
||||
63
openVulkanoCpp/Input/Touch/GestureProcessor.hpp
Normal file
63
openVulkanoCpp/Input/Touch/GestureProcessor.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GesturePan.hpp"
|
||||
#include "GesturePinch.hpp"
|
||||
#include "GestureTap.hpp"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
class GestureProcessor final
|
||||
{
|
||||
friend class InputDeviceTouch;
|
||||
|
||||
GesturePan m_gesturePan;
|
||||
GesturePan m_gesturePanTwoFingers;
|
||||
GesturePinch m_gesturePinch;
|
||||
GestureTap m_gestureTap;
|
||||
GestureTap m_gestureTapTwoFingers; // TODO
|
||||
std::vector<Gesture*> m_gestures;
|
||||
|
||||
public:
|
||||
GestureProcessor();
|
||||
~GestureProcessor();
|
||||
|
||||
void Init();
|
||||
void Close();
|
||||
|
||||
void AddGesture(Gesture* gesture);
|
||||
void RemoveGesture(Gesture* gesture);
|
||||
bool ResolveConflicts(Gesture* gesture);
|
||||
void Reset();
|
||||
|
||||
void TouchDown(const Touch& touch);
|
||||
void TouchUp(const Touch& touch);
|
||||
|
||||
void TouchMoved(const Touch& touch);
|
||||
|
||||
//region conflict resolution
|
||||
enum class ConflictResult
|
||||
{
|
||||
NeitherGesture,
|
||||
ExistingGesture,
|
||||
NewGesture,
|
||||
BothGestures
|
||||
};
|
||||
|
||||
using ConflictResolutionDelegate = std::function<ConflictResult(const Gesture*, const Gesture*)>;
|
||||
|
||||
void SetConflictResolutionDelegate(const ConflictResolutionDelegate& delegate) { m_conflictResolutionDelegate = delegate; }
|
||||
|
||||
private:
|
||||
ConflictResolutionDelegate m_conflictResolutionDelegate;
|
||||
//endregion
|
||||
};
|
||||
}
|
||||
87
openVulkanoCpp/Input/Touch/GestureTap.cpp
Normal file
87
openVulkanoCpp/Input/Touch/GestureTap.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "GestureTap.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr auto MAX_DURATION = 1s;
|
||||
constexpr float MAX_DISTANCE = 12.0f;
|
||||
constexpr float MAX_DISTANCE2 = MAX_DISTANCE * MAX_DISTANCE;
|
||||
}
|
||||
|
||||
int GestureTap::GetTouchCount() const
|
||||
{
|
||||
return std::count_if(m_touchMap.begin(), m_touchMap.end(), [](const auto& t) { return t.second; });
|
||||
}
|
||||
|
||||
void GestureTap::Cancel()
|
||||
{
|
||||
SetActive(false);
|
||||
m_tapInfo.position = {};
|
||||
}
|
||||
|
||||
void GestureTap::Reset()
|
||||
{
|
||||
Cancel();
|
||||
m_touchMap.clear();
|
||||
m_startTime = {};
|
||||
}
|
||||
|
||||
void GestureTap::TouchMoved(const Touch& touch)
|
||||
{
|
||||
if (IsActive())
|
||||
{
|
||||
if ((std::chrono::steady_clock::now() - m_startTime > MAX_DURATION) ||
|
||||
(Math::Utils::distance2(touch.GetPosition(), m_initialPoint) > MAX_DISTANCE2) ||
|
||||
((m_distance += Math::Utils::distance2(touch.GetPosition(), touch.GetLastPosition()) > 3 * MAX_DISTANCE2)))
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tapInfo.position = touch.GetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
void GestureTap::TouchDown(const Touch& touch)
|
||||
{
|
||||
m_touchMap[touch.GetId()] = true;
|
||||
if (1 == m_touchMap.size()) // single tap only
|
||||
{
|
||||
m_startTime = std::chrono::steady_clock::now();
|
||||
m_initialPoint = touch.GetPosition();
|
||||
m_distance = 0.0f;
|
||||
m_tapInfo.position = m_initialPoint;
|
||||
SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void GestureTap::TouchUp(const Touch& touch)
|
||||
{
|
||||
m_touchMap[touch.GetId()] = false;
|
||||
if (IsActive())
|
||||
{
|
||||
m_tapInfo.duration = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - m_startTime);
|
||||
m_tapInfo.position = touch.GetPosition();
|
||||
if (m_tapInfo.duration <= MAX_DURATION)
|
||||
{
|
||||
OnTap.NotifyAll(this, m_tapInfo);
|
||||
}
|
||||
Cancel();
|
||||
}
|
||||
if (!GetTouchCount()) Reset(); // empty map if no touch
|
||||
}
|
||||
}
|
||||
42
openVulkanoCpp/Input/Touch/GestureTap.hpp
Normal file
42
openVulkanoCpp/Input/Touch/GestureTap.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Gesture.hpp"
|
||||
#include "Touch.hpp"
|
||||
#include "GestureInfos.hpp"
|
||||
#include "Base/Event.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
class GestureTap final : public Gesture
|
||||
{
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_startTime;
|
||||
float m_distance;
|
||||
Math::Vector2f m_initialPoint;
|
||||
std::map<TouchId, bool> m_touchMap;
|
||||
TapInfo m_tapInfo;
|
||||
|
||||
int GetTouchCount() const;
|
||||
|
||||
public:
|
||||
GestureTap() : Gesture(TYPE_TAP) {}
|
||||
|
||||
~GestureTap() override = default;
|
||||
|
||||
void Cancel() override;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
void TouchDown(const Touch& touch) override;
|
||||
void TouchUp(const Touch& touch) override;
|
||||
void TouchMoved(const Touch& touch) override;
|
||||
|
||||
Event<const GestureTap*, const TapInfo&> OnTap;
|
||||
};
|
||||
}
|
||||
212
openVulkanoCpp/Input/Touch/InputDeviceTouch.cpp
Normal file
212
openVulkanoCpp/Input/Touch/InputDeviceTouch.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "InputDeviceTouch.hpp"
|
||||
#include "Base/Logger.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
void InputDeviceTouch::Init()
|
||||
{
|
||||
InputDevice::Init(InputDeviceType::TOUCH, 0, "Touch");
|
||||
|
||||
m_gestureProcessor.Init();
|
||||
m_gestureProcessor.m_gestureTap.OnTap += EventHandler(this, &InputDeviceTouch::UpdateTap);
|
||||
m_gestureProcessor.m_gesturePan.OnPanStarted += EventHandler(this, &InputDeviceTouch::UpdatePanStarted);
|
||||
m_gestureProcessor.m_gesturePan.OnPanMoved += EventHandler(this, &InputDeviceTouch::UpdatePanMoved);
|
||||
m_gestureProcessor.m_gesturePanTwoFingers.OnPanStarted += EventHandler(this, &InputDeviceTouch::UpdatePanTwoFingersStarted);
|
||||
m_gestureProcessor.m_gesturePanTwoFingers.OnPanMoved += EventHandler(this, &InputDeviceTouch::UpdatePanTwoFingersMoved);
|
||||
m_gestureProcessor.m_gesturePinch.OnPinchStarted += EventHandler(this, &InputDeviceTouch::UpdatePinchStarted);
|
||||
m_gestureProcessor.m_gesturePinch.OnPinchMoved += EventHandler(this, &InputDeviceTouch::UpdatePinchMoved);
|
||||
}
|
||||
|
||||
decltype(InputDeviceTouch::m_touches)::iterator InputDeviceTouch::FindTouch(TouchId id)
|
||||
{
|
||||
return std::find_if(m_touches.begin(), m_touches.end(), [id](const Touch& touch)
|
||||
{
|
||||
return (id == touch.GetId());
|
||||
});
|
||||
}
|
||||
|
||||
TouchId InputDeviceTouch::AddTouch(const Math::Vector2f& position)
|
||||
{
|
||||
m_touches.emplace_back(m_nextId, position);
|
||||
OnTouchAdded(m_touches.back());
|
||||
return m_nextId++;
|
||||
}
|
||||
|
||||
void InputDeviceTouch::TouchDown(TouchId id)
|
||||
{
|
||||
auto touchIter = FindTouch(id);
|
||||
|
||||
if (touchIter != m_touches.end())
|
||||
{
|
||||
touchIter->m_down = true;
|
||||
if (!m_multiTouch && m_touches.size() > 1)
|
||||
{
|
||||
int count = 0;
|
||||
for (const auto &touch : m_touches)
|
||||
{
|
||||
if(touch.IsDown()) count++;
|
||||
if (count > 1)
|
||||
{
|
||||
Logger::INPUT->debug("Begin multitouch");
|
||||
m_multiTouch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OnTouchDown.NotifyAll(*touchIter);
|
||||
m_gestureProcessor.TouchDown(*touchIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::INPUT->warn("Received touch down event for unknown touch id.");
|
||||
}
|
||||
}
|
||||
|
||||
void InputDeviceTouch::TouchUp(TouchId id)
|
||||
{
|
||||
auto touchIter = FindTouch(id);
|
||||
|
||||
if (touchIter != m_touches.end())
|
||||
{
|
||||
touchIter->m_down = false;
|
||||
|
||||
if (m_multiTouch)
|
||||
{
|
||||
bool hasDown = false;
|
||||
for (const auto &touch : m_touches)
|
||||
{
|
||||
if (touch.IsDown())
|
||||
{
|
||||
hasDown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasDown)
|
||||
{
|
||||
Logger::INPUT->debug("End multitouch");
|
||||
m_multiTouch = false;
|
||||
}
|
||||
}
|
||||
|
||||
OnTouchUp.NotifyAll(*touchIter);
|
||||
m_gestureProcessor.TouchUp(*touchIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::INPUT->warn("Received touch up for unknown touch id.");
|
||||
}
|
||||
}
|
||||
|
||||
void InputDeviceTouch::TouchMoved(TouchId id, const Math::Vector2f& position)
|
||||
{
|
||||
auto touchIter = FindTouch(id);
|
||||
|
||||
if (touchIter != m_touches.end())
|
||||
{
|
||||
touchIter->m_lastPosition = touchIter->m_position;
|
||||
touchIter->m_position = position;
|
||||
|
||||
OnTouchMoved.NotifyAll(*touchIter);
|
||||
m_gestureProcessor.TouchMoved(*touchIter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::INPUT->warn("Received touch moved for unknown touch id");
|
||||
}
|
||||
}
|
||||
|
||||
void InputDeviceTouch::RemoveTouch(TouchId id)
|
||||
{
|
||||
auto touchIter = FindTouch(id);
|
||||
if (touchIter != m_touches.end())
|
||||
{
|
||||
auto removedTouch = *touchIter;
|
||||
m_touches.erase(touchIter);
|
||||
|
||||
if (m_touches.empty())
|
||||
{
|
||||
m_multiTouch = false;
|
||||
}
|
||||
|
||||
OnTouchRemoved.NotifyAll(removedTouch);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::INPUT->warn("Received remove request for unknown touch id");
|
||||
}
|
||||
}
|
||||
|
||||
void InputDeviceTouch::UpdatePinchStarted(const Gesture* pinch, const PinchInfo& info)
|
||||
{
|
||||
m_lastPinchInfo = m_nextPinchInfo = info;
|
||||
}
|
||||
|
||||
void InputDeviceTouch::UpdatePanStarted(const Gesture* pan, const PanInfo& info)
|
||||
{
|
||||
m_lastPanInfo = m_nextPanInfo = info;
|
||||
}
|
||||
|
||||
void InputDeviceTouch::UpdatePanTwoFingersStarted(const Gesture* pan, const PanInfo& info)
|
||||
{
|
||||
m_last2FPanInfo = m_next2FPanInfo = info;
|
||||
}
|
||||
|
||||
void InputDeviceTouch::UpdatePinchMoved(const Gesture* pinch, const PinchInfo& info)
|
||||
{
|
||||
m_nextPinchInfo = info;
|
||||
}
|
||||
|
||||
void InputDeviceTouch::UpdatePanMoved(const Gesture* pan, const PanInfo& info)
|
||||
{
|
||||
m_nextPanInfo = info;
|
||||
}
|
||||
|
||||
void InputDeviceTouch::UpdatePanTwoFingersMoved(const Gesture* pan, const PanInfo& info)
|
||||
{
|
||||
m_next2FPanInfo = info;
|
||||
}
|
||||
|
||||
void InputDeviceTouch::UpdateTap(const Gesture* tap, const TapInfo& info)
|
||||
{
|
||||
m_toBePressedButtons = 1;
|
||||
m_nextTap = info.position;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_TAP_DURATION] = info.duration.count();
|
||||
}
|
||||
|
||||
void InputDeviceTouch::Tick()
|
||||
{
|
||||
m_lastPressedButtons = m_pressedButtons;
|
||||
m_pressedButtons = m_toBePressedButtons;
|
||||
m_toBePressedButtons = 0;
|
||||
|
||||
// Tap
|
||||
auto diff = m_nextTap - m_lastTap;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_TAP_X] = diff.x;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_TAP_Y] = diff.y;
|
||||
m_lastTap = m_nextTap;
|
||||
|
||||
// Pan
|
||||
diff = m_nextPanInfo.position - m_lastPanInfo.position;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_PAN_X] = diff.x;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_PAN_Y] = diff.y;
|
||||
m_lastPanInfo = m_nextPanInfo;
|
||||
diff = m_next2FPanInfo.position - m_last2FPanInfo.position;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_PAN_TWO_FINGERS_X] = diff.x;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_PAN_TWO_FINGERS_Y] = diff.y;
|
||||
m_next2FPanInfo = m_last2FPanInfo;
|
||||
|
||||
// Pinch
|
||||
m_axes[InputKey::Touch::Axis::AXIS_PINCH] = m_nextPinchInfo.scale;
|
||||
diff = m_nextPinchInfo.position - m_lastPinchInfo.position;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_PINCH_CENTER_X] = diff.x;
|
||||
m_axes[InputKey::Touch::Axis::AXIS_PINCH_CENTER_Y] = diff.y;
|
||||
}
|
||||
}
|
||||
88
openVulkanoCpp/Input/Touch/InputDeviceTouch.hpp
Normal file
88
openVulkanoCpp/Input/Touch/InputDeviceTouch.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Touch.hpp"
|
||||
#include "GestureProcessor.hpp"
|
||||
#include "Input/InputDevice.hpp"
|
||||
#include "Base/Event.hpp"
|
||||
#include "GestureInfos.hpp"
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
class InputDeviceTouch final : public InputDevice
|
||||
{
|
||||
GestureProcessor m_gestureProcessor;
|
||||
std::vector<Touch> m_touches;
|
||||
TouchId m_nextId = 0;
|
||||
bool m_multiTouch = false;
|
||||
uint8_t m_pressedButtons = 0, m_lastPressedButtons = 0, m_toBePressedButtons = 0;
|
||||
float m_axes[InputKey::Touch::Axis::AXIS_LAST + 1] = { 0 };
|
||||
Math::Vector2f m_lastTap{}, m_nextTap{};
|
||||
PinchInfo m_lastPinchInfo, m_nextPinchInfo;
|
||||
PanInfo m_lastPanInfo, m_nextPanInfo, m_last2FPanInfo, m_next2FPanInfo;
|
||||
|
||||
decltype(m_touches)::iterator FindTouch(TouchId id);
|
||||
|
||||
[[nodiscard]] bool GetLastButton(InputKey::Touch::Button button) const
|
||||
{
|
||||
return m_lastPressedButtons & (1 << button);
|
||||
}
|
||||
|
||||
public:
|
||||
InputDeviceTouch() = default;
|
||||
|
||||
void Init();
|
||||
|
||||
void Tick() final;
|
||||
|
||||
TouchId AddTouch(const Math::Vector2f& position);
|
||||
void TouchDown(TouchId id);
|
||||
void TouchUp(TouchId id);
|
||||
void TouchMoved(TouchId id, const Math::Vector2f& position);
|
||||
void RemoveTouch(TouchId id);
|
||||
|
||||
void UpdatePinchStarted(const Gesture* pinch, const PinchInfo &info);
|
||||
void UpdatePinchMoved(const Gesture* pinch, const PinchInfo &info);
|
||||
void UpdatePanStarted(const Gesture* pan, const PanInfo &info);
|
||||
void UpdatePanMoved(const Gesture* pan, const PanInfo &info);
|
||||
void UpdatePanTwoFingersStarted(const Gesture* pan, const PanInfo &info);
|
||||
void UpdatePanTwoFingersMoved(const Gesture* pan, const PanInfo &info);
|
||||
void UpdateTap(const Gesture* tap, const TapInfo &info);
|
||||
|
||||
[[nodiscard]] float ReadAxis(int16_t key) const override {return GetAxis( static_cast<InputKey::Touch::Axis>(key)); }
|
||||
[[nodiscard]] bool ReadButton(int16_t key) const override { return GetButton(static_cast<InputKey::Touch::Button>(key)); }
|
||||
[[nodiscard]] bool ReadButtonUp(int16_t key) const override { return GetButtonUp(static_cast<InputKey::Touch::Button>(key)); }
|
||||
[[nodiscard]] bool ReadButtonDown(int16_t key) const override { return GetButtonDown(static_cast<InputKey::Touch::Button>(key)); }
|
||||
|
||||
[[nodiscard]] const Math::Vector2f& GetTapPosition() const { return m_nextTap; }
|
||||
|
||||
[[nodiscard]] float GetAxis(const InputKey::Touch::Axis axis) const { return m_axes[axis]; }
|
||||
|
||||
[[nodiscard]] bool GetButton(const InputKey::Touch::Button button) const
|
||||
{
|
||||
return m_pressedButtons & (1 << button);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool GetButtonUp(const InputKey::Touch::Button button) const
|
||||
{
|
||||
return !GetButton(button) && GetLastButton(button);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool GetButtonDown(const InputKey::Touch::Button button) const
|
||||
{
|
||||
return GetButton(button) && !GetLastButton(button);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasTouch() const { return !m_touches.empty(); }
|
||||
[[nodiscard]] bool IsMultiTouch() const { return m_multiTouch; }
|
||||
|
||||
Event<Touch&> OnTouchAdded, OnTouchDown, OnTouchUp, OnTouchMoved, OnTouchRemoved;
|
||||
};
|
||||
}
|
||||
36
openVulkanoCpp/Input/Touch/Touch.hpp
Normal file
36
openVulkanoCpp/Input/Touch/Touch.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Math/Math.hpp"
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
typedef uint32_t TouchId;
|
||||
|
||||
class Touch final
|
||||
{
|
||||
friend class InputDeviceTouch;
|
||||
|
||||
TouchId m_id;
|
||||
Math::Vector2f m_position, m_lastPosition;
|
||||
bool m_down = false;
|
||||
|
||||
public:
|
||||
Touch(TouchId id, const Math::Vector2f& position)
|
||||
: m_id(id), m_position(position), m_lastPosition(position)
|
||||
{}
|
||||
|
||||
[[nodiscard]] TouchId GetId() const { return m_id; }
|
||||
|
||||
[[nodiscard]] Math::Vector2f GetPosition() const { return m_position; }
|
||||
|
||||
[[nodiscard]] Math::Vector2f GetLastPosition() const { return m_position; }
|
||||
|
||||
[[nodiscard]] bool IsDown() const { return m_down; }
|
||||
};
|
||||
}
|
||||
22
openVulkanoCpp/Input/Touch/TouchInfo.hpp
Normal file
22
openVulkanoCpp/Input/Touch/TouchInfo.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Touch.hpp"
|
||||
|
||||
namespace OpenVulkano::Input
|
||||
{
|
||||
struct TouchInfo final
|
||||
{
|
||||
TouchId touchId;
|
||||
Math::Vector2f initialPosition;
|
||||
Math::Vector2f currentPosition;
|
||||
Math::Vector2f delta;
|
||||
bool isDrag = false;
|
||||
bool active = false;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user