From 1868717520fa25d8cac195b047ed3305671dc5e8 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 3 Jul 2013 11:10:40 -0700 Subject: [PATCH 1/3] Improve gyro look to smoothly follow the head and not move for small movements --- interface/src/Application.cpp | 23 +++++++----------- interface/src/Avatar.cpp | 12 ++++------ interface/src/Avatar.h | 2 +- interface/src/Head.cpp | 44 +++++++++++++++++++++++++++++++---- interface/src/Head.h | 9 ++++++- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4eb2f8e25a..4669730e4b 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -362,9 +362,7 @@ void Application::paintGL() { glEnable(GL_LINE_SMOOTH); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - float headCameraScale = (_serialHeadSensor.isActive() || _webcam.isActive()) ? _headCameraPitchYawScale : 1.0f; - + if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myCamera.setTightness (100.0f); _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); @@ -380,11 +378,11 @@ void Application::paintGL() { } else if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { _myCamera.setTightness(0.0f); // In first person, camera follows head exactly without delay _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); - _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale)); + _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { _myCamera.setTargetPosition(_myAvatar.getUprightHeadPosition()); - _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation(headCameraScale)); + _myCamera.setTargetRotation(_myAvatar.getHead().getCameraOrientation()); } // Update camera position @@ -1451,7 +1449,7 @@ void Application::initMenu() { (_echoAudioMode = optionsMenu->addAction("Echo Audio"))->setCheckable(true); optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N)->setCheckable(true); - (_gyroLook = optionsMenu->addAction("Gyro Look"))->setCheckable(true); + (_gyroLook = optionsMenu->addAction("Smooth Gyro Look"))->setCheckable(true); _gyroLook->setChecked(false); (_mouseLook = optionsMenu->addAction("Mouse Look"))->setCheckable(true); _mouseLook->setChecked(true); @@ -1903,18 +1901,13 @@ void Application::update(float deltaTime) { void Application::updateAvatar(float deltaTime) { // Update my avatar's head position from gyros and/or webcam - _myAvatar.updateHeadFromGyrosAndOrWebcam(); + _myAvatar.updateHeadFromGyrosAndOrWebcam(_gyroLook->isChecked(), + glm::vec3(_headCameraPitchYawScale, + _headCameraPitchYawScale, + _headCameraPitchYawScale) ); if (_serialHeadSensor.isActive()) { - // Update avatar head translation - if (_gyroLook->isChecked()) { - glm::vec3 headPosition = _serialHeadSensor.getEstimatedPosition(); - const float HEAD_OFFSET_SCALING = 3.f; - headPosition *= HEAD_OFFSET_SCALING; - _myCamera.setEyeOffsetPosition(headPosition); - } - // Grab latest readings from the gyros float measuredPitchRate = _serialHeadSensor.getLastPitchRate(); float measuredYawRate = _serialHeadSensor.getLastYawRate(); diff --git a/interface/src/Avatar.cpp b/interface/src/Avatar.cpp index 97e0f71c5a..2598e7531e 100755 --- a/interface/src/Avatar.cpp +++ b/interface/src/Avatar.cpp @@ -285,10 +285,7 @@ void Avatar::reset() { } // Update avatar head rotation with sensor data -void Avatar::updateHeadFromGyrosAndOrWebcam() { - const float AMPLIFY_PITCH = 2.f; - const float AMPLIFY_YAW = 2.f; - const float AMPLIFY_ROLL = 2.f; +void Avatar::updateHeadFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngle) { SerialInterface* gyros = Application::getInstance()->getSerialHeadSensor(); Webcam* webcam = Application::getInstance()->getWebcam(); @@ -306,9 +303,10 @@ void Avatar::updateHeadFromGyrosAndOrWebcam() { } else { return; } - _head.setPitch(estimatedRotation.x * AMPLIFY_PITCH); - _head.setYaw(estimatedRotation.y * AMPLIFY_YAW); - _head.setRoll(estimatedRotation.z * AMPLIFY_ROLL); + _head.setPitch(estimatedRotation.x * amplifyAngle.x); + _head.setYaw(estimatedRotation.y * amplifyAngle.y); + _head.setRoll(estimatedRotation.z * amplifyAngle.z); + _head.setCameraFollowsHead(gyroLook); // Update torso lean distance based on accelerometer data const float TORSO_LENGTH = 0.5f; diff --git a/interface/src/Avatar.h b/interface/src/Avatar.h index 86c333dad4..6351d37a74 100755 --- a/interface/src/Avatar.h +++ b/interface/src/Avatar.h @@ -87,7 +87,7 @@ public: void reset(); void simulate(float deltaTime, Transmitter* transmitter); void updateThrust(float deltaTime, Transmitter * transmitter); - void updateHeadFromGyrosAndOrWebcam(); + void updateHeadFromGyrosAndOrWebcam(bool gyroLook, const glm::vec3& amplifyAngles); void updateFromMouse(int mouseX, int mouseY, int screenWidth, int screenHeight); void updateFromTouch(float touchAvgDistX, float touchAvgDistY); void addBodyYaw(float y) {_bodyYaw += y;}; diff --git a/interface/src/Head.cpp b/interface/src/Head.cpp index 44eddeeb93..75c62ce87e 100644 --- a/interface/src/Head.cpp +++ b/interface/src/Head.cpp @@ -77,7 +77,12 @@ Head::Head(Avatar* owningAvatar) : _rightEyeBlink(0.0f), _leftEyeBlinkVelocity(0.0f), _rightEyeBlinkVelocity(0.0f), - _timeWithoutTalking(0.0f) + _timeWithoutTalking(0.0f), + _cameraPitch(_pitch), + _cameraYaw(_yaw), + _isCameraMoving(false), + _cameraFollowsHead(false), + _cameraFollowHeadRate(0.0f) { if (USING_PHYSICAL_MOHAWK) { resetHairPhysics(); @@ -213,6 +218,37 @@ void Head::simulate(float deltaTime, bool isMine) { updateHairPhysics(deltaTime); } + // Update camera pitch and yaw independently from motion of head (for gyro-based interface) + if (isMine && _cameraFollowsHead) { + // If we are using gyros and using gyroLook, have the camera follow head but with a null region + // to create stable rendering view with small head movements. + const float CAMERA_FOLLOW_HEAD_RATE_START = 0.05f; + const float CAMERA_FOLLOW_HEAD_RATE_MAX = 0.25f; + const float CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE = 1.5f; + const float CAMERA_STOP_TOLERANCE_DEGREES = 0.25f; + const float CAMERA_START_TOLERANCE_DEGREES = 15.0f; + float cameraHeadAngleDifference = glm::length(glm::vec2(_pitch - _cameraPitch, _yaw - _cameraYaw)); + if (_isCameraMoving) { + _cameraFollowHeadRate = glm::clamp(_cameraFollowHeadRate * CAMERA_FOLLOW_HEAD_RATE_RAMP_RATE, + 0.f, + CAMERA_FOLLOW_HEAD_RATE_MAX); + + _cameraPitch += (_pitch - _cameraPitch) * _cameraFollowHeadRate; + _cameraYaw += (_yaw - _cameraYaw) * _cameraFollowHeadRate; + if (cameraHeadAngleDifference < CAMERA_STOP_TOLERANCE_DEGREES) { + _isCameraMoving = false; + } + } else { + if (cameraHeadAngleDifference > CAMERA_START_TOLERANCE_DEGREES) { + _isCameraMoving = true; + _cameraFollowHeadRate = CAMERA_FOLLOW_HEAD_RATE_START; + } + } + } else { + // Camera always locked to head + _cameraPitch = _pitch; + _cameraYaw = _yaw; + } } void Head::calculateGeometry() { @@ -358,10 +394,10 @@ glm::quat Head::getOrientation() const { glm::vec3(_pitch, -_yaw, -_roll) : glm::vec3(_pitch, _yaw, _roll))); } -glm::quat Head::getCameraOrientation (float pitchYawScale) const { +glm::quat Head::getCameraOrientation () const { Avatar* owningAvatar = static_cast(_owningAvatar); - return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3( - _pitch * pitchYawScale, _yaw * pitchYawScale, 0.0f))); + return owningAvatar->getWorldAlignedOrientation() + * glm::quat(glm::radians(glm::vec3(_cameraPitch, _cameraYaw, 0.0f))); } void Head::renderHeadSphere() { diff --git a/interface/src/Head.h b/interface/src/Head.h index 85976d33d8..8564cd94b0 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -48,8 +48,10 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff ) { _renderLookatVectors = onOff; } + void setCameraFollowsHead(bool b) { _cameraFollowsHead = b; } + glm::quat getOrientation() const; - glm::quat getCameraOrientation (float pitchYawScale) const; + glm::quat getCameraOrientation () const; glm::vec3 getPosition() const { return _position; } glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } @@ -112,6 +114,11 @@ private: float _leftEyeBlinkVelocity; float _rightEyeBlinkVelocity; float _timeWithoutTalking; + float _cameraPitch; // Used to position the camera differently from the head + float _cameraYaw; + bool _isCameraMoving; + bool _cameraFollowsHead; + float _cameraFollowHeadRate; static ProgramObject* _irisProgram; static GLuint _irisTextureID; From 7880c74304c5737cd8fd5aad5d0fa9ce5a8f3618 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 3 Jul 2013 11:17:06 -0700 Subject: [PATCH 2/3] smooth gyro look on by default if not in settings --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index af3e3501df..94a2962d74 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1452,7 +1452,7 @@ void Application::initMenu() { optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N)->setCheckable(true); (_gyroLook = optionsMenu->addAction("Smooth Gyro Look"))->setCheckable(true); - _gyroLook->setChecked(false); + _gyroLook->setChecked(true); (_mouseLook = optionsMenu->addAction("Mouse Look"))->setCheckable(true); _mouseLook->setChecked(true); (_touchLook = optionsMenu->addAction("Touch Look"))->setCheckable(true); From 3388fccc86b791f3bf195468771a68255c842303 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Wed, 3 Jul 2013 13:09:55 -0700 Subject: [PATCH 3/3] Fixes per code review --- interface/src/Application.cpp | 2 +- interface/src/Head.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e6b86b4896..56469938ff 100755 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1914,7 +1914,7 @@ void Application::updateAvatar(float deltaTime) { _myAvatar.updateHeadFromGyrosAndOrWebcam(_gyroLook->isChecked(), glm::vec3(_headCameraPitchYawScale, _headCameraPitchYawScale, - _headCameraPitchYawScale) ); + _headCameraPitchYawScale)); if (_serialHeadSensor.isActive()) { diff --git a/interface/src/Head.h b/interface/src/Head.h index 8564cd94b0..e8bcfb5277 100644 --- a/interface/src/Head.h +++ b/interface/src/Head.h @@ -48,7 +48,7 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff ) { _renderLookatVectors = onOff; } - void setCameraFollowsHead(bool b) { _cameraFollowsHead = b; } + void setCameraFollowsHead(bool cameraFollowsHead) { _cameraFollowsHead = cameraFollowsHead; } glm::quat getOrientation() const; glm::quat getCameraOrientation () const;