From dca0b05927c510da238afffefcd85da18b53c823 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 9 Feb 2014 12:08:17 -0800 Subject: [PATCH] added easier support for calculating multitouch rotating behavior including deltaAngle, isRotating, rotating(direction) properties --- examples/controllerExample.js | 4 +- examples/multitouchExample.js | 121 +++++++++++++++++++++ interface/src/Application.cpp | 14 ++- libraries/script-engine/src/EventTypes.cpp | 44 ++++---- libraries/script-engine/src/EventTypes.h | 8 +- 5 files changed, 160 insertions(+), 31 deletions(-) create mode 100644 examples/multitouchExample.js diff --git a/examples/controllerExample.js b/examples/controllerExample.js index 12cf5940d1..eae94c812b 100644 --- a/examples/controllerExample.js +++ b/examples/controllerExample.js @@ -146,8 +146,8 @@ function printTouchEvent(eventName, event) { for (var i = 0; i < event.points.length; i++) { print(" event.angles[" + i + "]:" + event.angles[i]); } - print(" event.rotatingClockwise=" + event.rotatingClockwise); - print(" event.rotatingCounterClockwise=" + event.rotatingCounterClockwise); + print(" event.isRotating=" + event.isRotating); + print(" event.rotating=" + event.rotating); } function touchBeginEvent(event) { diff --git a/examples/multitouchExample.js b/examples/multitouchExample.js new file mode 100644 index 0000000000..b33f1cf1a4 --- /dev/null +++ b/examples/multitouchExample.js @@ -0,0 +1,121 @@ +// +// multitouchExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 2/9/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class's multi-touch features +// +// When this script is running: +// * Four finger rotate gesture will rotate your avatar. +// * Three finger swipe up/down will adjust the pitch of your avatars head. +// + +var lastX = 0; +var lastY = 0; +var lastAngle = 0; +var yawFromMultiTouch = 0; +var pitchFromMultiTouch = 0; +var wantDebugging = false; +var ROTATE_YAW_SCALE = 0.15; +var MOUSE_PITCH_SCALE = -12.5; +var FIXED_MOUSE_TIMESTEP = 0.016; + +var ROTATE_TOUCH_POINTS = 4; +var PITCH_TOUCH_POINTS = 3; + + +function printTouchEvent(eventName, event) { + print(eventName); + + print(" event.x,y=" + event.x + ", " + event.y); + print(" event.isPressed=" + event.isPressed); + print(" event.isMoved=" + event.isMoved); + print(" event.isStationary=" + event.isStationary); + print(" event.isReleased=" + event.isReleased); + print(" event.isShifted=" + event.isShifted); + print(" event.isControl=" + event.isControl); + print(" event.isMeta=" + event.isMeta); + print(" event.isAlt=" + event.isAlt); + print(" event.touchPoints=" + event.touchPoints); + for (var i = 0; i < event.points.length; i++) { + print(" event.points[" + i + "].x.y:" + event.points[i].x + ", " + event.points[i].y); + } + print(" event.radius=" + event.radius); + print(" event.isPinching=" + event.isPinching); + print(" event.isPinchOpening=" + event.isPinchOpening); + + print(" event.angle=" + event.angle); + print(" event.deltaAngle=" + event.deltaAngle); + for (var i = 0; i < event.points.length; i++) { + print(" event.angles[" + i + "]:" + event.angles[i]); + } + print(" event.isRotating=" + event.isRotating); + print(" event.rotating=" + event.rotating); +} + +function touchBeginEvent(event) { + printTouchEvent("touchBeginEvent", event); + lastX = event.x; + lastY = event.y; +} + +function touchUpdateEvent(event) { + printTouchEvent("touchUpdateEvent", event); + + if (event.isRotating && event.touchPoints == ROTATE_TOUCH_POINTS) { + // it's possible for the multitouch rotate gesture to generate angle changes which are faster than comfortable to + // view, so we will scale this change in angle to make it more comfortable + var scaledRotate = event.deltaAngle * ROTATE_YAW_SCALE; + print(">>> event.deltaAngle=" + event.deltaAngle); + print(">>> scaledRotate=" + scaledRotate); + yawFromMultiTouch += scaledRotate; + } + + if (event.touchPoints == PITCH_TOUCH_POINTS) { + pitchFromMultiTouch += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); + } + lastX = event.x; + lastY = event.y; + lastAngle = event.angle; +} + +function touchEndEvent(event) { + printTouchEvent("touchEndEvent", event); +} +// Map touch events to our callbacks +Controller.touchBeginEvent.connect(touchBeginEvent); +Controller.touchUpdateEvent.connect(touchUpdateEvent); +Controller.touchEndEvent.connect(touchEndEvent); + + + +function update() { + // rotate body yaw for yaw received from multitouch rotate + var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMultiTouch, z: 0 } )); + if (wantDebugging) { + print("changing orientation" + + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," + + MyAvatar.orientation.z + "," + MyAvatar.orientation.w + + " newOrientation="+newOrientation.x + "," + newOrientation.y + "," + newOrientation.z + "," + newOrientation.w); + } + MyAvatar.orientation = newOrientation; + yawFromMultiTouch = 0; + + // apply pitch from mouse + var newPitch = MyAvatar.headPitch + pitchFromMultiTouch; + if (wantDebugging) { + print("changing pitch [old]MyAvatar.headPitch="+MyAvatar.headPitch+ " newPitch="+newPitch); + } + MyAvatar.headPitch = newPitch; + pitchFromMultiTouch = 0; +} +Script.willSendVisualDataCallback.connect(update); + + +function scriptEnding() { + // handle any shutdown logic here +} + +Script.scriptEnding.connect(scriptEnding); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6e96abe30d..ef66d96c2c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1218,6 +1218,11 @@ void Application::mouseMoveEvent(QMouseEvent* event) { } } +const bool MAKE_SOUND_ON_VOXEL_HOVER = false; +const bool MAKE_SOUND_ON_VOXEL_CLICK = true; +const float HOVER_VOXEL_FREQUENCY = 7040.f; +const float HOVER_VOXEL_DECAY = 0.999f; + void Application::mousePressEvent(QMouseEvent* event) { _controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts @@ -1320,7 +1325,7 @@ void Application::touchUpdateEvent(QTouchEvent* event) { } void Application::touchBeginEvent(QTouchEvent* event) { - TouchEvent thisEvent(*event, _lastTouchEvent); + TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event _controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts touchUpdateEvent(event); _lastTouchEvent = thisEvent; @@ -2007,12 +2012,9 @@ void Application::updateHoverVoxels(float deltaTime, float& distance, BoxFace& f bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels()"); - // Check for a new hover voxel if (!_mousePressed) { - { - PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); - _isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face); - } + PerformanceWarning warn(showWarnings, "Application::updateHoverVoxels() _voxels.findRayIntersection()"); + _isHoverVoxel = _voxels.findRayIntersection(_mouseRayOrigin, _mouseRayDirection, _hoverVoxel, distance, face); } } diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index 8f0d4192f5..671405ae8f 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -323,14 +323,16 @@ TouchEvent::TouchEvent() : isControl(false), isMeta(false), isAlt(false), + touchPoints(0), points(), radius(0.0f), isPinching(false), isPinchOpening(false), angles(), angle(0.0f), - rotatingClockwise(false), - rotatingCounterClockwise(false) + deltaAngle(0.0f), + isRotating(false), + rotating("none") { }; @@ -359,9 +361,9 @@ void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { const QList& tPoints = event.touchPoints(); float touchAvgX = 0.0f; float touchAvgY = 0.0f; - int numTouches = tPoints.count(); - if (numTouches > 1) { - for (int i = 0; i < numTouches; ++i) { + touchPoints = tPoints.count(); + if (touchPoints > 1) { + for (int i = 0; i < touchPoints; ++i) { touchAvgX += tPoints[i].pos().x(); touchAvgY += tPoints[i].pos().y(); @@ -369,8 +371,8 @@ void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { glm::vec2 thisPoint(tPoints[i].pos().x(), tPoints[i].pos().y()); points << thisPoint; } - touchAvgX /= (float)(numTouches); - touchAvgY /= (float)(numTouches); + touchAvgX /= (float)(touchPoints); + touchAvgY /= (float)(touchPoints); } else { // I'm not sure this should ever happen, why would Qt send us a touch event for only one point? // maybe this happens in the case of a multi-touch where all but the last finger is released? @@ -384,7 +386,7 @@ void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { // also calculate the rotation angle for each point float maxRadius = 0.0f; glm::vec2 center(x,y); - for (int i = 0; i < numTouches; ++i) { + for (int i = 0; i < touchPoints; ++i) { glm::vec2 touchPoint(tPoints[i].pos().x(), tPoints[i].pos().y()); float thisRadius = glm::distance(center,touchPoint); if (thisRadius > maxRadius) { @@ -397,13 +399,12 @@ void TouchEvent::initWithQTouchEvent(const QTouchEvent& event) { } radius = maxRadius; - // after calculating the center point (average touch point), determine the maximum radius + // after calculating the angles for each touch point, determine the average angle float totalAngle = 0.0f; - for (int i = 0; i < numTouches; ++i) { + for (int i = 0; i < touchPoints; ++i) { totalAngle += angles[i]; } - angle = totalAngle/numTouches; - + angle = totalAngle/(float)touchPoints; isPressed = event.touchPointStates().testFlag(Qt::TouchPointPressed); isMoved = event.touchPointStates().testFlag(Qt::TouchPointMoved); @@ -431,15 +432,16 @@ void TouchEvent::calculateMetaAttributes(const TouchEvent& other) { } // determine if the points are rotating... + deltaAngle = angle - other.angle; if (other.angle < angle) { - rotatingClockwise = true; - rotatingCounterClockwise = false; + isRotating = true; + rotating = "clockwise"; } else if (other.angle > angle) { - rotatingClockwise = false; - rotatingCounterClockwise = true; + isRotating = true; + rotating = "counterClockwise"; } else { - rotatingClockwise = false; - rotatingCounterClockwise = false; + isRotating = false; + rotating = "none"; } } @@ -456,6 +458,7 @@ QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& ev obj.setProperty("isMeta", event.isMeta); obj.setProperty("isControl", event.isControl); obj.setProperty("isAlt", event.isAlt); + obj.setProperty("touchPoints", event.touchPoints); QScriptValue pointsObj = engine->newArray(); int index = 0; @@ -470,6 +473,7 @@ QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& ev obj.setProperty("isPinchOpening", event.isPinchOpening); obj.setProperty("angle", event.angle); + obj.setProperty("deltaAngle", event.deltaAngle); QScriptValue anglesObj = engine->newArray(); index = 0; foreach (float angle, event.angles) { @@ -478,8 +482,8 @@ QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& ev } obj.setProperty("angles", anglesObj); - obj.setProperty("rotatingClockwise", event.rotatingClockwise); - obj.setProperty("rotatingCounterClockwise", event.rotatingCounterClockwise); + obj.setProperty("isRotating", event.isRotating); + obj.setProperty("rotating", event.rotating); return obj; } diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h index 55abcde2f6..a416747a85 100644 --- a/libraries/script-engine/src/EventTypes.h +++ b/libraries/script-engine/src/EventTypes.h @@ -67,14 +67,16 @@ public: bool isControl; bool isMeta; bool isAlt; + int touchPoints; QVector points; float radius; bool isPinching; bool isPinchOpening; QVector angles; // angle from center to each point - float angle; - bool rotatingClockwise; - bool rotatingCounterClockwise; + float angle; // the average of the angles + float deltaAngle; // the change in average angle from last event + bool isRotating; + QString rotating; private: void initWithQTouchEvent(const QTouchEvent& event);