Redesigned gestures system...

...and improved mouse gestures support
Some testing still required...
This commit is contained in:
Ray 2016-02-02 16:43:42 +01:00
parent e484d58d9c
commit 728e1715cc
3 changed files with 192 additions and 166 deletions

View file

@ -253,6 +253,7 @@ static void InitGamepad(void); // Init raw gamepad inpu
static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value)
static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void ScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel
static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
@ -1415,6 +1416,7 @@ static void InitDisplay(int width, int height)
glfwSetCursorEnterCallback(window, CursorEnterCallback); glfwSetCursorEnterCallback(window, CursorEnterCallback);
glfwSetKeyCallback(window, KeyCallback); glfwSetKeyCallback(window, KeyCallback);
glfwSetMouseButtonCallback(window, MouseButtonCallback); glfwSetMouseButtonCallback(window, MouseButtonCallback);
glfwSetCursorPosCallback(window, MouseCursorPosCallback); // Track mouse position changes
glfwSetCharCallback(window, CharCallback); glfwSetCharCallback(window, CharCallback);
glfwSetScrollCallback(window, ScrollCallback); glfwSetScrollCallback(window, ScrollCallback);
glfwSetWindowIconifyCallback(window, WindowIconifyCallback); glfwSetWindowIconifyCallback(window, WindowIconifyCallback);
@ -1677,7 +1679,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
// Register touch actions // Register touch actions
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_DOWN; if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_DOWN;
else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_MOVE; //else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_MOVE;
else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_UP; else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) gestureEvent.touchAction = TOUCH_UP;
// Register touch points count // Register touch points count
@ -1685,7 +1687,28 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int
// Register touch points position, only one point registered // Register touch points position, only one point registered
gestureEvent.position[0] = GetMousePosition(); gestureEvent.position[0] = GetMousePosition();
// Gesture data is sent to gestures system for processing
ProcessGestureEvent(gestureEvent);
#endif
}
// GLFW3 Cursor Position Callback, runs on mouse move
static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
{
#define ENABLE_MOUSE_GESTURES
#if defined(ENABLE_MOUSE_GESTURES)
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent;
gestureEvent.touchAction = TOUCH_MOVE;
// Register touch points count
gestureEvent.pointCount = 1;
// Register touch points position, only one point registered
gestureEvent.position[0] = (Vector2){ (float)x, (float)y };
// Gesture data is sent to gestures system for processing // Gesture data is sent to gestures system for processing
ProcessGestureEvent(gestureEvent); ProcessGestureEvent(gestureEvent);
#endif #endif
@ -1934,7 +1957,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
// Register touch points count // Register touch points count
gestureEvent.pointCount = AMotionEvent_getPointerCount(event); gestureEvent.pointCount = AMotionEvent_getPointerCount(event);
// Register touch points id DESKTOP // Register touch points id
gestureEvent.pointerId[0] = AMotionEvent_getPointerId(event, 0); gestureEvent.pointerId[0] = AMotionEvent_getPointerId(event, 0);
gestureEvent.pointerId[1] = AMotionEvent_getPointerId(event, 1); gestureEvent.pointerId[1] = AMotionEvent_getPointerId(event, 1);
@ -2496,7 +2519,7 @@ static EM_BOOL EmscriptenInputCallback(int eventType, const EmscriptenTouchEvent
// Register touch points count // Register touch points count
gestureEvent.pointCount = touchEvent->numTouches; gestureEvent.pointCount = touchEvent->numTouches;
// Register touch points id WEB // Register touch points id
gestureEvent.pointerId[0] = touchEvent->touches[0].identifier; gestureEvent.pointerId[0] = touchEvent->touches[0].identifier;
gestureEvent.pointerId[1] = touchEvent->touches[1].identifier; gestureEvent.pointerId[1] = touchEvent->touches[1].identifier;

View file

@ -46,7 +46,11 @@
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#define FORCE_TO_SWIPE 20 #define FORCE_TO_SWIPE 20
#define TAP_TIMEOUT 300 #define FORCE_TO_DRAG 20
#define FORCE_TO_PINCH 5
#define TAP_TIMEOUT 300 // Time in milliseconds
#define PINCH_TIMEOUT 300 // Time in milliseconds
#define DOUBLETAP_RANGE 30
//#define MAX_TOUCH_POINTS 4 //#define MAX_TOUCH_POINTS 4
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -61,10 +65,6 @@ typedef enum {
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Global Variables Definition // Global Variables Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static GestureType gestureType = TYPE_MOTIONLESS;
static double eventTime = 0;
//static int32_t touchId; // Not used...
// Tap gesture variables // Tap gesture variables
static Vector2 initialTapPosition = { 0, 0 }; static Vector2 initialTapPosition = { 0, 0 };
@ -78,6 +78,21 @@ static Vector2 endDragPosition = { 0, 0 };
static Vector2 lastDragPosition = { 0, 0 }; static Vector2 lastDragPosition = { 0, 0 };
static Vector2 dragVector = { 0, 0 }; static Vector2 dragVector = { 0, 0 };
// Albert&Ian
static Vector2 touchDownPosition = { 0, 0 };
static Vector2 touchDownPosition2 = { 0, 0 };
static Vector2 touchUpPosition = { 0, 0 };
static Vector2 moveDownPosition = { 0, 0 };
static Vector2 moveDownPosition2 = { 0, 0 };
static int numTap = 0;
static int numHold = 0;
static int numPinch = 0;
static int pointCount = 0;
static int touchId = -1;
static double eventTime = 0;
static float magnitude = 0; // Distance traveled dragging static float magnitude = 0; // Distance traveled dragging
static float angle = 0; // Angle direction of the drag static float angle = 0; // Angle direction of the drag
static float intensity = 0; // How fast we did the drag (pixels per frame) static float intensity = 0; // How fast we did the drag (pixels per frame)
@ -95,7 +110,7 @@ static int previousGesture = GESTURE_NONE;
static int currentGesture = GESTURE_NONE; static int currentGesture = GESTURE_NONE;
// Enabled gestures flags, all gestures enabled by default // Enabled gestures flags, all gestures enabled by default
static unsigned int enabledGestures = 0b0000011111111111; static unsigned int enabledGestures = 0b0000001111111111;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
@ -105,6 +120,7 @@ static float CalculateAngle(Vector2 initialPosition, Vector2 actualPosition, flo
static float VectorDistance(Vector2 v1, Vector2 v2); static float VectorDistance(Vector2 v1, Vector2 v2);
static float VectorDotProduct(Vector2 v1, Vector2 v2); static float VectorDotProduct(Vector2 v1, Vector2 v2);
static double GetCurrentTime(); static double GetCurrentTime();
static float Vector2Distance();
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition // Module Functions Definition
@ -119,173 +135,160 @@ void ProcessGestureEvent(GestureEvent event)
previousGesture = currentGesture; previousGesture = currentGesture;
switch (gestureType) pointCount = event.pointCount;
{
case TYPE_MOTIONLESS: // Detect TAP, DOUBLE_TAP and HOLD events // Albert&Ian
if (pointCount < 2)
{
touchId = event.pointerId[0];
if (event.touchAction == TOUCH_DOWN)
{ {
if (event.touchAction == TOUCH_DOWN) numTap++; // Tap counter
{
if (event.pointCount > 1) InitPinchGesture(event.position[0], event.position[1]);
else
{
// Set the press position
initialTapPosition = event.position[0];
// If too much time have passed, we reset the double tap
if (GetCurrentTime() - eventTime > TAP_TIMEOUT) untap = false;
// If we are in time, we detect the double tap
if (untap) doubleTapping = true;
// Update our event time
eventTime = GetCurrentTime();
// Set hold
if (doubleTapping) currentGesture = GESTURE_DOUBLETAP;
else currentGesture = GESTURE_TAP;
}
}
else if (event.touchAction == TOUCH_UP)
{
currentGesture = GESTURE_NONE;
// Detect that we are tapping instead of holding // Detect GESTURE_DOUBLE_TAP
if (GetCurrentTime() - eventTime < TAP_TIMEOUT) if ((currentGesture == GESTURE_NONE) && (numTap >= 2) && ((GetCurrentTime() - eventTime) < TAP_TIMEOUT) && (GetMagnitude(touchDownPosition, event.position[0]) < DOUBLETAP_RANGE))
{
if (doubleTapping) untap = false;
else untap = true;
}
// Tap finished
doubleTapping = false;
// Update our event time
eventTime = GetCurrentTime();
}
// Begin dragging
else if (event.touchAction == TOUCH_MOVE)
{ {
if (event.pointCount > 1) InitPinchGesture(event.position[0], event.position[1]); currentGesture = GESTURE_DOUBLETAP;
else numTap = 0;
{
// Set the drag starting position
initialDragPosition = initialTapPosition;
endDragPosition = initialDragPosition;
// Initialize drag
draggingTimeCounter = 0;
gestureType = TYPE_DRAG;
currentGesture = GESTURE_NONE;
}
} }
} break; else // Detect GESTURE_TAP
case TYPE_DRAG: // Detect DRAG and SWIPE events {
numTap = 1;
currentGesture = GESTURE_TAP;
}
touchDownPosition = event.position[0];
touchUpPosition = touchDownPosition;
eventTime = GetCurrentTime();
}
else if (event.touchAction == TOUCH_UP)
{ {
// end of the drag if (currentGesture = GESTURE_DRAG)
if (event.touchAction == TOUCH_UP) {
touchUpPosition = event.position[0];
}
// Calculate for swipe
magnitude = GetMagnitude(touchDownPosition, touchUpPosition);
intensity = magnitude / (float)draggingTimeCounter;
// Detect GESTURE_SWIPE
if ((intensity > FORCE_TO_SWIPE) && (touchId == 0))
{
angle = CalculateAngle(touchDownPosition, touchUpPosition, magnitude);
if ((angle < 30) || (angle > 330)) currentGesture = GESTURE_SWIPE_RIGHT; // Right
else if ((angle > 30) && (angle < 120)) currentGesture = GESTURE_SWIPE_UP; // Up
else if ((angle > 120) && (angle < 210)) currentGesture = GESTURE_SWIPE_LEFT; // Left
else if ((angle > 210) && (angle < 300)) currentGesture = GESTURE_SWIPE_DOWN; // Down
else currentGesture = GESTURE_NONE;
}
else
{ {
// Return Swipe if we have enough sensitivity
if (intensity > FORCE_TO_SWIPE)
{
if (angle < 30 || angle > 330) currentGesture = GESTURE_SWIPE_RIGHT; // Right
else if (angle > 60 && angle < 120) currentGesture = GESTURE_SWIPE_UP; // Up
else if (angle > 150 && angle < 210) currentGesture = GESTURE_SWIPE_LEFT; // Left
else if (angle > 240 && angle < 300) currentGesture = GESTURE_SWIPE_DOWN; // Down
}
magnitude = 0; magnitude = 0;
angle = 0; angle = 0;
intensity = 0; intensity = 0;
gestureType = TYPE_MOTIONLESS; currentGesture = GESTURE_NONE;
} }
// Update while we are dragging
else if (event.touchAction == TOUCH_MOVE) draggingTimeCounter = 0;
{ }
if (event.pointCount > 1) InitPinchGesture(event.position[0], event.position[1]); else if (event.touchAction == TOUCH_MOVE)
else
{
lastDragPosition = endDragPosition;
endDragPosition = event.position[0];
//endDragPosition.x = AMotionEvent_getX(event, 0);
//endDragPosition.y = AMotionEvent_getY(event, 0);
// Calculate attributes
dragVector = (Vector2){ endDragPosition.x - lastDragPosition.x, endDragPosition.y - lastDragPosition.y };
magnitude = sqrt(pow(endDragPosition.x - initialDragPosition.x, 2) + pow(endDragPosition.y - initialDragPosition.y, 2));
angle = CalculateAngle(initialDragPosition, endDragPosition, magnitude);
intensity = magnitude / (float)draggingTimeCounter;
// Check if drag movement is less than minimum to keep it as hold state or switch to drag state
if(magnitude > FORCE_TO_SWIPE)
{
currentGesture = GESTURE_DRAG;
draggingTimeCounter++;
}
else currentGesture = GESTURE_HOLD;
}
}
} break;
case TYPE_DUAL_INPUT:
{ {
if (event.touchAction == TOUCH_UP) if (GetMagnitude(moveDownPosition, event.position[0]) > 5) eventTime = GetCurrentTime();
moveDownPosition = event.position[0];
if (currentGesture == GESTURE_HOLD)
{ {
if (event.pointCount == 1) if (numHold == 1) touchDownPosition = event.position[0];
{
// Set the drag starting position
initialTapPosition = event.position[0];
}
gestureType = TYPE_MOTIONLESS;
}
else if (event.touchAction == TOUCH_MOVE)
{
// Adapt the ending position of the inputs
firstEndPinchPosition = event.position[0];
secondEndPinchPosition = event.position[1];
// If there is no more than two inputs numHold = 2;
if (event.pointCount == 2)
{
// Calculate distances
float initialDistance = VectorDistance(firstInitialPinchPosition, secondInitialPinchPosition);
float endDistance = VectorDistance(firstEndPinchPosition, secondEndPinchPosition);
// Calculate Vectors magnitude = GetMagnitude(touchDownPosition, moveDownPosition);
Vector2 firstTouchVector = { firstEndPinchPosition.x - firstInitialPinchPosition.x, firstEndPinchPosition.y - firstInitialPinchPosition.y };
Vector2 secondTouchVector = { secondEndPinchPosition.x - secondInitialPinchPosition.x, secondEndPinchPosition.y - secondInitialPinchPosition.y };
// Detect the pinch gesture
if (VectorDotProduct(firstTouchVector, secondTouchVector) < -0.5) pinchDelta = initialDistance - endDistance;
else pinchDelta = 0;
// Pinch gesture resolution
if (pinchDelta != 0)
{
if (pinchDelta > 0) currentGesture = GESTURE_PINCH_IN;
else currentGesture = GESTURE_PINCH_OUT;
}
}
else
{
// Set the drag starting position
initialTapPosition = event.position[0];
gestureType = TYPE_MOTIONLESS;
}
// Readapt the initial position of the inputs // Detect GESTURE_DRAG
firstInitialPinchPosition = firstEndPinchPosition; if (magnitude >= FORCE_TO_DRAG) currentGesture = GESTURE_DRAG;
secondInitialPinchPosition = secondEndPinchPosition;
} }
} break;
draggingTimeCounter++;
}
} }
else
{
// two fingers
if (event.touchAction == TOUCH_DOWN)
{
touchDownPosition = event.position[0];
touchDownPosition2 = event.position[1];
currentGesture = GESTURE_HOLD;
}
else if (event.touchAction == TOUCH_MOVE)
{
magnitude = GetMagnitude(moveDownPosition, moveDownPosition2);
touchDownPosition = moveDownPosition;
touchDownPosition2 = moveDownPosition2;
moveDownPosition = event.position[0];
moveDownPosition2 = event.position[1];
if ( (GetMagnitude(touchDownPosition, moveDownPosition) > FORCE_TO_PINCH) || (GetMagnitude(touchDownPosition2, moveDownPosition2) > FORCE_TO_PINCH))
{
if ((GetMagnitude(moveDownPosition, moveDownPosition2) - magnitude) < 0) currentGesture = GESTURE_PINCH_IN;
else currentGesture = GESTURE_PINCH_OUT;
}
else
{
currentGesture = GESTURE_HOLD;
}
}
else if (event.touchAction == TOUCH_UP)
{
currentGesture = GESTURE_NONE;
}
}
}
// Update gestures detected (must be called every frame)
void UpdateGestures(void)
{
// NOTE: Gestures are processed through system callbacks on touch events
// Detect GESTURE_HOLD
if (((currentGesture == GESTURE_TAP) || (currentGesture == GESTURE_DOUBLETAP)) && pointCount < 2) currentGesture = GESTURE_HOLD;
if ((GetCurrentTime() - eventTime) > TAP_TIMEOUT && (currentGesture == GESTURE_DRAG) && pointCount < 2)
{
currentGesture = GESTURE_HOLD;
numHold = 1;
}
// Detect GESTURE_NONE
if ((currentGesture == GESTURE_SWIPE_RIGHT) || (currentGesture == GESTURE_SWIPE_UP) || (currentGesture == GESTURE_SWIPE_LEFT) || (currentGesture == GESTURE_SWIPE_DOWN))
{
currentGesture = GESTURE_NONE;
}
}
// Calculate distance between two vectors
float Vector2Distance(Vector2 v1, Vector3 v2)
{
float result;
float dx = v2.x - v1.x;
float dy = v2.y - v1.y;
result = sqrt(dx*dx + dy*dy);
return result;
} }
// Check if a gesture have been detected // Check if a gesture have been detected
bool IsGestureDetected(void) bool IsGestureDetected(void)
{ {
if (currentGesture != GESTURE_NONE) return true; if ((enabledGestures & currentGesture) != GESTURE_NONE) return true;
else return false; else return false;
} }
@ -298,7 +301,7 @@ int GetGestureType(void)
void SetGesturesEnabled(unsigned int gestureFlags) void SetGesturesEnabled(unsigned int gestureFlags)
{ {
enabledGestures = enabledGestures | gestureFlags; enabledGestures = gestureFlags;
} }
// Get drag intensity (pixels per frame) // Get drag intensity (pixels per frame)
@ -440,6 +443,7 @@ static float VectorDotProduct(Vector2 v1, Vector2 v2)
return result; return result;
} }
// Time measure returned are milliseconds
static double GetCurrentTime() static double GetCurrentTime()
{ {
double time = 0; double time = 0;

View file

@ -435,17 +435,17 @@ typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode;
// Gestures type // Gestures type
// NOTE: It could be used as flags to enable only some gestures // NOTE: It could be used as flags to enable only some gestures
typedef enum { typedef enum {
GESTURE_NONE = 1, GESTURE_NONE = 0,
GESTURE_TAP = 2, GESTURE_TAP = 1,
GESTURE_DOUBLETAP = 4, GESTURE_DOUBLETAP = 2,
GESTURE_HOLD = 8, GESTURE_HOLD = 4,
GESTURE_DRAG = 16, GESTURE_DRAG = 8,
GESTURE_SWIPE_RIGHT = 32, GESTURE_SWIPE_RIGHT = 16,
GESTURE_SWIPE_LEFT = 64, GESTURE_SWIPE_LEFT = 32,
GESTURE_SWIPE_UP = 128, GESTURE_SWIPE_UP = 64,
GESTURE_SWIPE_DOWN = 256, GESTURE_SWIPE_DOWN = 128,
GESTURE_PINCH_IN = 512, GESTURE_PINCH_IN = 256,
GESTURE_PINCH_OUT = 1024 GESTURE_PINCH_OUT = 512
} Gestures; } Gestures;
typedef enum { TOUCH_UP, TOUCH_DOWN, TOUCH_MOVE } TouchAction; typedef enum { TOUCH_UP, TOUCH_DOWN, TOUCH_MOVE } TouchAction;
@ -781,7 +781,6 @@ bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadi
bool CheckCollisionRayBox(Ray ray, Vector3 minBBox, Vector3 maxBBox); // Detect collision between ray and box bool CheckCollisionRayBox(Ray ray, Vector3 minBBox, Vector3 maxBBox); // Detect collision between ray and box
Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius); // Detect collision of player radius with cubicmap Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius); // Detect collision of player radius with cubicmap
// NOTE: Return the normal vector of the impacted surface // NOTE: Return the normal vector of the impacted surface
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Shaders System Functions (Module: rlgl) // Shaders System Functions (Module: rlgl)
// NOTE: This functions are useless when using OpenGL 1.1 // NOTE: This functions are useless when using OpenGL 1.1