From 33f9acde67e8ac1a7707e9880c5662de271a3eb1 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 17 Jul 2018 17:42:11 -0700 Subject: [PATCH 001/106] latest --- interface/src/avatar/MyAvatar.cpp | 20 +++++++++++++++++++- interface/src/avatar/MyAvatar.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d084ca69f9..9eaf42d25d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -89,9 +89,10 @@ const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; const float MIN_SCALE_CHANGED_DELTA = 0.001f; const int MODE_READINGS_RING_BUFFER_SIZE = 500; +const int HEAD_FACING_RING_BUFFER_SIZE = 250; const float CENTIMETERS_PER_METER = 100.0f; -//#define DEBUG_DRAW_HMD_MOVING_AVERAGE +#define DEBUG_DRAW_HMD_MOVING_AVERAGE MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), @@ -112,6 +113,7 @@ MyAvatar::MyAvatar(QThread* thread) : _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), + _headFacingBuffer(HEAD_FACING_RING_BUFFER_SIZE), _recentModeReadings(MODE_READINGS_RING_BUFFER_SIZE), _bodySensorMatrix(), _goToPending(false), @@ -434,11 +436,25 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); + + glm::vec2 bodyFacing = getFacingDir2D(_bodySensorMatrix); + _headFacingBuffer.insert(_headControllerFacing); + //qCDebug(interfaceapp) << "the body facing is " << -bodyFacing.x << " " << -bodyFacing.y << " the head controller facing is " << _headControllerFacing.x << " " << _headControllerFacing.y; + + float sumHeadFacingX = 0.0f; + float sumHeadFacingY = 0.0f; + for (auto headFacingIterator = _headFacingBuffer.begin(); headFacingIterator != _headFacingBuffer.end(); ++headFacingIterator) { + sumHeadFacingX += (*headFacingIterator).x; + sumHeadFacingY += (*headFacingIterator).y; + } + _averageFacing.x = sumHeadFacingX / (float)_headFacingBuffer.getNumEntries(); + _averageFacing.y= sumHeadFacingY / (float)_headFacingBuffer.getNumEntries(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); + //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); @@ -3478,6 +3494,8 @@ bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, co const float FOLLOW_ROTATION_THRESHOLD = cosf(myAvatar.getRotationThreshold()); glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; + + //return glm::dot(myAvatar._averageFacing, -bodyFacing) < FOLLOW_ROTATION_THRESHOLD; } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 819d5b0066..828ca48e32 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1546,6 +1546,8 @@ private: // cache head controller pose in sensor space glm::vec2 _headControllerFacing; // facing vector in xz plane (sensor space) glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) + RingBufferHistory _headFacingBuffer; + vec2 _averageFacing { 0.0f, 0.0f }; glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; float _currentStandingHeight { 0.0f }; From 7cc724a27bf59709be09e9dc4f48ed0ffcd11eb2 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 18 Jul 2018 17:08:04 -0700 Subject: [PATCH 002/106] added properties to allow the toggling of the debug draw the look at vector, and the reset moving average look at on step detection --- interface/src/avatar/MyAvatar.cpp | 22 +++++++++++++++------- interface/src/avatar/MyAvatar.h | 8 ++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1b9eed9187..63a26fb2ad 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -471,13 +471,15 @@ void MyAvatar::update(float deltaTime) { _averageFacing.y= sumHeadFacingY / (float)_headFacingBuffer.getNumEntries(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE - auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); - glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); - glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); - //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); - glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); - DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); - DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + if (_drawAverageFacingEnabled) { + auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); + glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); + glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); + //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); + glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); + DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); + DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + } #endif if (_goToPending) { @@ -3594,10 +3596,16 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (myAvatar.getCenterOfGravityModelEnabled()) { if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); + if (myAvatar.getEnableStepResetRotation()) { + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + } } } else { if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); + if (myAvatar.getEnableStepResetRotation()) { + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + } } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0a40ac471c..c0f0166e49 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -198,6 +198,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hasAudioEnabledFaceMovement READ getHasAudioEnabledFaceMovement WRITE setHasAudioEnabledFaceMovement) Q_PROPERTY(float rotationRecenterFilterLength READ getRotationRecenterFilterLength WRITE setRotationRecenterFilterLength) Q_PROPERTY(float rotationThreshold READ getRotationThreshold WRITE setRotationThreshold) + Q_PROPERTY(bool enableStepResetRotation READ getEnableStepResetRotation WRITE setEnableStepResetRotation) + Q_PROPERTY(bool enableDrawAverageFacing READ getEnableDrawAverageFacing WRITE setEnableDrawAverageFacing) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) @@ -1464,6 +1466,10 @@ private: float getRotationRecenterFilterLength() const { return _rotationRecenterFilterLength; } void setRotationThreshold(float angleRadians); float getRotationThreshold() const { return _rotationThreshold; } + void setEnableStepResetRotation(bool stepReset) { _stepResetRotationEnabled = stepReset; } + bool getEnableStepResetRotation() const { return _stepResetRotationEnabled; } + void setEnableDrawAverageFacing(bool drawAverage) { _drawAverageFacingEnabled = drawAverage; } + bool getEnableDrawAverageFacing() const { return _drawAverageFacingEnabled; } bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; @@ -1574,6 +1580,8 @@ private: std::atomic _hasScriptedBlendShapes { false }; std::atomic _rotationRecenterFilterLength { 4.0f }; std::atomic _rotationThreshold { 0.5235f }; // 30 degrees in radians + std::atomic _stepResetRotationEnabled { false }; + std::atomic _drawAverageFacingEnabled { false }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; From a8c4f4e6507913a8fde6fce37f14418f4a11027c Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 18 Jul 2018 17:10:09 -0700 Subject: [PATCH 003/106] removed extra whitespace --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 63a26fb2ad..34d5017c50 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -456,11 +456,11 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - + glm::vec2 bodyFacing = getFacingDir2D(_bodySensorMatrix); _headFacingBuffer.insert(_headControllerFacing); //qCDebug(interfaceapp) << "the body facing is " << -bodyFacing.x << " " << -bodyFacing.y << " the head controller facing is " << _headControllerFacing.x << " " << _headControllerFacing.y; - + float sumHeadFacingX = 0.0f; float sumHeadFacingY = 0.0f; for (auto headFacingIterator = _headFacingBuffer.begin(); headFacingIterator != _headFacingBuffer.end(); ++headFacingIterator) { From 5ec7ab3a7d10f50eaefd4638852d68af8b365d6b Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 18 Jul 2018 17:20:47 -0700 Subject: [PATCH 004/106] removed the other way of computing the average lookat which used a ring buffer --- interface/src/avatar/MyAvatar.cpp | 17 +---------------- interface/src/avatar/MyAvatar.h | 2 -- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 34d5017c50..10e38afbe3 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -89,7 +89,6 @@ const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; const float MIN_SCALE_CHANGED_DELTA = 0.001f; const int MODE_READINGS_RING_BUFFER_SIZE = 500; -const int HEAD_FACING_RING_BUFFER_SIZE = 250; const float CENTIMETERS_PER_METER = 100.0f; #define DEBUG_DRAW_HMD_MOVING_AVERAGE @@ -113,7 +112,6 @@ MyAvatar::MyAvatar(QThread* thread) : _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), - _headFacingBuffer(HEAD_FACING_RING_BUFFER_SIZE), _recentModeReadings(MODE_READINGS_RING_BUFFER_SIZE), _bodySensorMatrix(), _goToPending(false), @@ -457,25 +455,14 @@ void MyAvatar::update(float deltaTime) { setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - glm::vec2 bodyFacing = getFacingDir2D(_bodySensorMatrix); - _headFacingBuffer.insert(_headControllerFacing); - //qCDebug(interfaceapp) << "the body facing is " << -bodyFacing.x << " " << -bodyFacing.y << " the head controller facing is " << _headControllerFacing.x << " " << _headControllerFacing.y; - float sumHeadFacingX = 0.0f; - float sumHeadFacingY = 0.0f; - for (auto headFacingIterator = _headFacingBuffer.begin(); headFacingIterator != _headFacingBuffer.end(); ++headFacingIterator) { - sumHeadFacingX += (*headFacingIterator).x; - sumHeadFacingY += (*headFacingIterator).y; - } - _averageFacing.x = sumHeadFacingX / (float)_headFacingBuffer.getNumEntries(); - _averageFacing.y= sumHeadFacingY / (float)_headFacingBuffer.getNumEntries(); + #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE if (_drawAverageFacingEnabled) { auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); - //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); @@ -3516,8 +3503,6 @@ bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, co const float FOLLOW_ROTATION_THRESHOLD = cosf(myAvatar.getRotationThreshold()); glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; - - //return glm::dot(myAvatar._averageFacing, -bodyFacing) < FOLLOW_ROTATION_THRESHOLD; } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c0f0166e49..81315900a7 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1593,8 +1593,6 @@ private: // cache head controller pose in sensor space glm::vec2 _headControllerFacing; // facing vector in xz plane (sensor space) glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) - RingBufferHistory _headFacingBuffer; - vec2 _averageFacing { 0.0f, 0.0f }; glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; float _currentStandingHeight { 0.0f }; From c774dd7f5bf0734b771fc0616a3fff47cfa9081e Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 8 Aug 2018 17:52:22 -0700 Subject: [PATCH 005/106] debugging to solve the no-neck crazy scale problem --- interface/src/Application.cpp | 5 +++++ interface/src/avatar/MyAvatar.cpp | 2 ++ interface/src/avatar/MySkeletonModel.cpp | 5 +++++ .../src/avatars-renderer/Avatar.cpp | 3 +++ .../src/avatars-renderer/SkeletonModel.cpp | 19 +++++++++++++++++++ libraries/shared/src/shared/Camera.cpp | 1 + 6 files changed, 35 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bbd2b1c17..8a7a5c610a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3064,6 +3064,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { auto myAvatar = getMyAvatar(); boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; + //qCDebug(interfaceapp) << "boom offset" << boomOffset; // The render mode is default or mirror if the camera is in mirror mode, assigned further below renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; @@ -3074,10 +3075,12 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + //qCDebug(interfaceapp) << "camera 1rst translation hmd " << extractTranslation(camMat); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); } else { + //qCDebug(interfaceapp) << "camera 1rst translation desktop " << myAvatar->getDefaultEyePosition(); _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); } @@ -3099,6 +3102,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat))); _myCamera.setPosition(extractTranslation(worldCameraMat)); + //qCDebug(interfaceapp) << "camera translation 3rd hmd " << extractTranslation(worldCameraMat); } else { _thirdPersonHMDCameraBoomValid = false; @@ -3112,6 +3116,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() + myAvatar->getWorldOrientation() * boomOffset); } + qCDebug(interfaceapp) << "camera translation 3rd desktop " << (myAvatar->getDefaultEyePosition() + _myCamera.getOrientation() * boomOffset); } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3f738ea4cb..9971f90bd6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1704,8 +1704,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + //qCDebug(interfaceapp) << "position third person " << getWorldPosition(); return getWorldPosition() + getWorldOrientation() * FLIP * _skeletonOffset; } + //qCDebug(interfaceapp) << "position first person " << Avatar::getWorldPosition(); return Avatar::getWorldPosition(); } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0fc5e7521e..86085798ab 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -95,7 +95,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // make sure lookAt is not too close to face (avoid crosseyes) glm::vec3 lookAt = head->getLookAtPosition(); + //qCDebug(interfaceapp) << "look at " << lookAt; glm::vec3 focusOffset = lookAt - _owningAvatar->getHead()->getEyePosition(); + //qCDebug(interfaceapp) << "focus offset " << focusOffset; float focusDistance = glm::length(focusOffset); const float MIN_LOOK_AT_FOCUS_DISTANCE = 1.0f; if (focusDistance < MIN_LOOK_AT_FOCUS_DISTANCE && focusDistance > EPSILON) { @@ -113,6 +115,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()); glm::mat4 sensorToWorldMatrix = myAvatar->getSensorToWorldMatrix(); params.rigToSensorMatrix = glm::inverse(sensorToWorldMatrix) * avatarToWorldMatrix * rigToAvatarMatrix; + //qCDebug(interfaceapp) << "AVATAR: " << avatarToWorldMatrix; + //qCDebug(interfaceapp) << "SENSOR " << sensorToWorldMatrix; // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); @@ -282,6 +286,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + //qCDebug(interfaceapp) << "right eye index is " << geometry.leftEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); updateFingers(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 69356cdfaa..07d1edcabb 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -169,6 +169,8 @@ AABox Avatar::getBounds() const { // approximately 2m tall, scaled to user request. return AABox(getWorldPosition() - glm::vec3(getModelScale()), getModelScale() * 2.0f); } + //float temp = _skeletonModel->getRenderableMeshBound().getLargestDimension(); + //qCDebug(animation) << " largest bounding box dimension " << temp; return _skeletonModel->getRenderableMeshBound(); } @@ -1775,6 +1777,7 @@ void Avatar::buildUnscaledEyeHeightCache() { // Sanity check by looking at the model extents. Extents meshExtents = _skeletonModel->getUnscaledMeshExtents(); float meshHeight = meshExtents.size().y; + //qCDebug(animation) << "mesh height " << meshHeight << " skeleton height " << skeletonHeight; // if we determine the mesh is much larger then the skeleton, then we use the mesh height instead. // This helps prevent absurdly large avatars from exceeding the domain height limit. diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index de8c02f10e..83caaede8d 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -71,14 +71,23 @@ void SkeletonModel::initJointStates() { qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } glm::vec3 leftEyePosition, rightEyePosition; + qCDebug(avatars_renderer) << "initial left and right eyes " << leftEyePosition << " " << rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); + qCDebug(avatars_renderer) << "after setting the left and right eyes " << leftEyePosition << " " << rightEyePosition; glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; int rootJointIndex = geometry.rootJointIndex; glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); + qCDebug(avatars_renderer) << "root joint index " << rootJointIndex << " root position: " << rootModelPosition; _defaultEyeModelPosition = midEyePosition - rootModelPosition; + if (headJointIndex > -1) { + glm::vec3 headModelPosition; + getJointPosition(headJointIndex, headModelPosition); + qCDebug(avatars_renderer) << "we have a head joint " << headJointIndex << " and " << headModelPosition; + } + qCDebug(avatars_renderer) << "the default eye pos " << _defaultEyeModelPosition << " and scale " << _scale; // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; @@ -294,6 +303,16 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; return true; + } else if (getJointPosition(geometry.headJointIndex, headPosition)) { + glm::vec3 baseEyePosition = headPosition; + glm::quat headRotation; + getJointRotation(geometry.headJointIndex, headRotation); + const float EYES_FORWARD = 0.25f; + const float EYE_SEPARATION = 0.1f; + float headHeight = glm::distance(neckPosition, headPosition); + firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD); + secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD); + return true; } return false; } diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index 787b7bfb1a..c80e14d87c 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -9,6 +9,7 @@ // #include "Camera.h" +//#include "../SharedLogging.h" /**jsdoc *

Camera modes affect the position of the camera and the controls for camera movement. The camera can be in one of the From a3f2fd1f235b014fd672e3b2f61a153103b84312 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 9 Aug 2018 11:33:27 -0700 Subject: [PATCH 006/106] removed some print statements and added the code to use the head parent when the neck is missing, this allows for cauterization --- interface/src/Application.cpp | 5 ----- interface/src/avatar/MyAvatar.cpp | 8 +++++--- interface/src/avatar/MySkeletonModel.cpp | 5 ----- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 3 --- libraries/render-utils/src/CauterizedModel.cpp | 2 ++ libraries/shared/src/shared/Camera.cpp | 1 - 6 files changed, 7 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8a7a5c610a..0bbd2b1c17 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3064,7 +3064,6 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { auto myAvatar = getMyAvatar(); boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; - //qCDebug(interfaceapp) << "boom offset" << boomOffset; // The render mode is default or mirror if the camera is in mirror mode, assigned further below renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; @@ -3075,12 +3074,10 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - //qCDebug(interfaceapp) << "camera 1rst translation hmd " << extractTranslation(camMat); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); } else { - //qCDebug(interfaceapp) << "camera 1rst translation desktop " << myAvatar->getDefaultEyePosition(); _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); } @@ -3102,7 +3099,6 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat))); _myCamera.setPosition(extractTranslation(worldCameraMat)); - //qCDebug(interfaceapp) << "camera translation 3rd hmd " << extractTranslation(worldCameraMat); } else { _thirdPersonHMDCameraBoomValid = false; @@ -3116,7 +3112,6 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() + myAvatar->getWorldOrientation() * boomOffset); } - qCDebug(interfaceapp) << "camera translation 3rd desktop " << (myAvatar->getDefaultEyePosition() + _myCamera.getOrientation() * boomOffset); } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9971f90bd6..8ba0841196 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1704,10 +1704,8 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - //qCDebug(interfaceapp) << "position third person " << getWorldPosition(); return getWorldPosition() + getWorldOrientation() * FLIP * _skeletonOffset; } - //qCDebug(interfaceapp) << "position first person " << Avatar::getWorldPosition(); return Avatar::getWorldPosition(); } @@ -1992,7 +1990,11 @@ void MyAvatar::initHeadBones() { neckJointIndex = _skeletonModel->getFBXGeometry().neckJointIndex; } if (neckJointIndex == -1) { - return; + neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1); + if (neckJointIndex < 0) { + //return if the head is not even there. can't cauterize!! + return; + } } _headBoneSet.clear(); std::queue q; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 86085798ab..0fc5e7521e 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -95,9 +95,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // make sure lookAt is not too close to face (avoid crosseyes) glm::vec3 lookAt = head->getLookAtPosition(); - //qCDebug(interfaceapp) << "look at " << lookAt; glm::vec3 focusOffset = lookAt - _owningAvatar->getHead()->getEyePosition(); - //qCDebug(interfaceapp) << "focus offset " << focusOffset; float focusDistance = glm::length(focusOffset); const float MIN_LOOK_AT_FOCUS_DISTANCE = 1.0f; if (focusDistance < MIN_LOOK_AT_FOCUS_DISTANCE && focusDistance > EPSILON) { @@ -115,8 +113,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()); glm::mat4 sensorToWorldMatrix = myAvatar->getSensorToWorldMatrix(); params.rigToSensorMatrix = glm::inverse(sensorToWorldMatrix) * avatarToWorldMatrix * rigToAvatarMatrix; - //qCDebug(interfaceapp) << "AVATAR: " << avatarToWorldMatrix; - //qCDebug(interfaceapp) << "SENSOR " << sensorToWorldMatrix; // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); @@ -286,7 +282,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; - //qCDebug(interfaceapp) << "right eye index is " << geometry.leftEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); updateFingers(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 07d1edcabb..69356cdfaa 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -169,8 +169,6 @@ AABox Avatar::getBounds() const { // approximately 2m tall, scaled to user request. return AABox(getWorldPosition() - glm::vec3(getModelScale()), getModelScale() * 2.0f); } - //float temp = _skeletonModel->getRenderableMeshBound().getLargestDimension(); - //qCDebug(animation) << " largest bounding box dimension " << temp; return _skeletonModel->getRenderableMeshBound(); } @@ -1777,7 +1775,6 @@ void Avatar::buildUnscaledEyeHeightCache() { // Sanity check by looking at the model extents. Extents meshExtents = _skeletonModel->getUnscaledMeshExtents(); float meshHeight = meshExtents.size().y; - //qCDebug(animation) << "mesh height " << meshHeight << " skeleton height " << skeletonHeight; // if we determine the mesh is much larger then the skeleton, then we use the mesh height instead. // This helps prevent absurdly large avatars from exceeding the domain height limit. diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 80a9c5ccae..b5b8c039a1 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -139,6 +139,8 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; + // qCDebug(renderutils) << "cauterize matrix: " << cauterizeMatrix; + for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index c80e14d87c..787b7bfb1a 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -9,7 +9,6 @@ // #include "Camera.h" -//#include "../SharedLogging.h" /**jsdoc *

Camera modes affect the position of the camera and the controls for camera movement. The camera can be in one of the From f26d6b8c3dc30957477271987647e4f60c96a708 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 9 Aug 2018 12:15:35 -0700 Subject: [PATCH 007/106] cleaned up code and changed the forward offset for the default eyes when there is no neck bone --- interface/src/avatar/MyAvatar.cpp | 2 +- .../src/avatars-renderer/SkeletonModel.cpp | 15 +++------------ libraries/render-utils/src/CauterizedModel.cpp | 2 -- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8ba0841196..d456028bbe 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1992,7 +1992,7 @@ void MyAvatar::initHeadBones() { if (neckJointIndex == -1) { neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1); if (neckJointIndex < 0) { - //return if the head is not even there. can't cauterize!! + // return if the head is not even there. can't cauterize!! return; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 83caaede8d..11bad6c1b8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -71,23 +71,14 @@ void SkeletonModel::initJointStates() { qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } glm::vec3 leftEyePosition, rightEyePosition; - qCDebug(avatars_renderer) << "initial left and right eyes " << leftEyePosition << " " << rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); - qCDebug(avatars_renderer) << "after setting the left and right eyes " << leftEyePosition << " " << rightEyePosition; glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; int rootJointIndex = geometry.rootJointIndex; glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); - qCDebug(avatars_renderer) << "root joint index " << rootJointIndex << " root position: " << rootModelPosition; _defaultEyeModelPosition = midEyePosition - rootModelPosition; - if (headJointIndex > -1) { - glm::vec3 headModelPosition; - getJointPosition(headJointIndex, headModelPosition); - qCDebug(avatars_renderer) << "we have a head joint " << headJointIndex << " and " << headModelPosition; - } - qCDebug(avatars_renderer) << "the default eye pos " << _defaultEyeModelPosition << " and scale " << _scale; // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; @@ -307,11 +298,11 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& glm::vec3 baseEyePosition = headPosition; glm::quat headRotation; getJointRotation(geometry.headJointIndex, headRotation); - const float EYES_FORWARD = 0.25f; + const float EYES_FORWARD_HEAD_ONLY = 0.30f; const float EYE_SEPARATION = 0.1f; float headHeight = glm::distance(neckPosition, headPosition); - firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD); - secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD); + firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); + secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); return true; } return false; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index b5b8c039a1..80a9c5ccae 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -139,8 +139,6 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; - // qCDebug(renderutils) << "cauterize matrix: " << cauterizeMatrix; - for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); From 93baad73873eb1de6fd0745dd6d430464563293d Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 9 Aug 2018 14:35:44 -0700 Subject: [PATCH 008/106] removed unnecessary computation of head height from eye position code --- .../avatars-renderer/src/avatars-renderer/SkeletonModel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 11bad6c1b8..1ec58fd704 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -300,7 +300,6 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& getJointRotation(geometry.headJointIndex, headRotation); const float EYES_FORWARD_HEAD_ONLY = 0.30f; const float EYE_SEPARATION = 0.1f; - float headHeight = glm::distance(neckPosition, headPosition); firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); return true; From d9b136cf5d3aa2bfb60058fc42175465bac7c317 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 20 Aug 2018 11:06:51 -0700 Subject: [PATCH 009/106] Read resources from other than the main directory --- libraries/fbx/src/GLTFReader.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 300e6f4846..317342b886 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -935,7 +935,7 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping } bool GLTFReader::readBinary(const QString& url, QByteArray& outdata) { - QUrl binaryUrl = _url.resolved(QUrl(url).fileName()); + QUrl binaryUrl = _url.resolved(url); qCDebug(modelformat) << "binaryUrl: " << binaryUrl << " OriginalUrl: " << _url; bool success; @@ -948,7 +948,7 @@ bool GLTFReader::doesResourceExist(const QString& url) { if (_url.isEmpty()) { return false; } - QUrl candidateUrl = _url.resolved(QUrl(url).fileName()); + QUrl candidateUrl = _url.resolved(url); return DependencyManager::get()->resourceExists(candidateUrl); } @@ -1001,8 +1001,9 @@ FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { fbxtex.texcoordSet = 0; if (texture.defined["source"]) { - QString fname = QUrl(_file.images[texture.source].uri).fileName(); - QUrl textureUrl = _url.resolved(fname); + QString url = _file.images[texture.source].uri; + QString fname = QUrl(url).fileName(); + QUrl textureUrl = _url.resolved(url); qCDebug(modelformat) << "fname: " << fname; qCDebug(modelformat) << "textureUrl: " << textureUrl; qCDebug(modelformat) << "Url: " << _url; From 94026fb4dda683a0af532fdc61e693201f2997f1 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 20 Aug 2018 16:47:14 -0700 Subject: [PATCH 010/106] added activate(rotation) to prephysics update --- interface/src/avatar/MyAvatar.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 10e38afbe3..2a6cb0017a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3582,6 +3582,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { + activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } @@ -3589,6 +3590,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { + activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } From a378435683b2bf9c5130ade161a29caa8f4d21ab Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 21 Aug 2018 10:20:51 -0700 Subject: [PATCH 011/106] Fix MS16003: In PAL/GoTo WebView, 'BACK' closes app when you can't go back --- interface/resources/qml/controls/TabletWebView.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index bc958aa876..db695dbfb2 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -52,12 +52,18 @@ Item { id: back enabledColor: hifi.colors.darkGray disabledColor: hifi.colors.lightGrayText - enabled: historyIndex > 0 + enabled: true text: "BACK" MouseArea { anchors.fill: parent - onClicked: goBack() + onClicked: { + if (historyIndex > 0) { + goBack(); + } else { + closeWebEngine(); + } + } } } From bfb9b679687ae4a0e4a5dac2e167b162d5101cd1 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 11:17:33 -0700 Subject: [PATCH 012/106] more debug statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 6 ++++++ scripts/system/menu.js | 1 + scripts/system/tablet-ui/tabletUI.js | 6 +++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 1922b02f93..10a9f01337 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -117,6 +117,7 @@ Item { id: pageRepeater model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) onItemAdded: { + console.log("onItemAdded: " + tabletProxy.buttons.rowCount()); item.proxyModel.sourceModel = tabletProxy.buttons; item.proxyModel.pageIndex = index; } @@ -208,6 +209,7 @@ Item { Component.onCompleted: updateProperties() function updateProperties() { + console.log("updateProperties: " + tabletProxy.buttons.rowCount()); var keys = Object.keys(modelData.properties).forEach(function (key) { if (tabletButton[key] !== modelData.properties[key]) { tabletButton[key] = modelData.properties[key]; @@ -269,6 +271,10 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count + + Component.onCompleted: { + console.log("pageIndicator: " + pageIndicator.count); + } } } diff --git a/scripts/system/menu.js b/scripts/system/menu.js index d669d3d918..1db210216a 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -52,6 +52,7 @@ var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet- if (isHMDMode) { button = tablet.addButton(buttonProperties); button.clicked.connect(onClicked); + print("Adding menu button"); } else if (button) { button.clicked.disconnect(onClicked); tablet.removeButton(button); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index f339475f72..c28040000b 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -22,7 +22,7 @@ var DEFAULT_TABLET_SCALE = 70; var preMakeTime = Date.now(); var validCheckTime = Date.now(); - var debugTablet = false; + var debugTablet = true; var tabletScalePercentage = 70.0; var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; @@ -298,7 +298,7 @@ } wantsMenu = clicked; }); - + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -306,7 +306,7 @@ } wantsMenu = clicked; }); - + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From af5c7a7a674618f16d20f69ccf980c9c2476d63c Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 21 Aug 2018 16:35:47 -0300 Subject: [PATCH 013/106] Android - Domains List - cache in memory only on this instance (deleted if app restarts) allows for fast list display. Showing again also asks for update so in some seconds the list gets updated in case of changes --- .../hifiinterface/fragment/HomeFragment.java | 25 ++++++++---- .../hifiinterface/view/DomainAdapter.java | 38 +++++++++++++++---- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java index 7bd373cf1d..86b8625cfe 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java @@ -76,18 +76,22 @@ public class HomeFragment extends Fragment { }); mDomainAdapter.setListener(new DomainAdapter.AdapterListener() { @Override - public void onEmptyAdapter() { + public void onEmptyAdapter(boolean shouldStopRefreshing) { searchNoResultsView.setText(R.string.search_no_results); searchNoResultsView.setVisibility(View.VISIBLE); mDomainsView.setVisibility(View.GONE); - mSwipeRefreshLayout.setRefreshing(false); + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override - public void onNonEmptyAdapter() { + public void onNonEmptyAdapter(boolean shouldStopRefreshing) { searchNoResultsView.setVisibility(View.GONE); mDomainsView.setVisibility(View.VISIBLE); - mSwipeRefreshLayout.setRefreshing(false); + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override @@ -96,11 +100,20 @@ public class HomeFragment extends Fragment { } }); mDomainsView.setAdapter(mDomainAdapter); + mDomainAdapter.startLoad(); mSearchView = rootView.findViewById(R.id.searchView); mSearchIconView = rootView.findViewById(R.id.search_mag_icon); mClearSearch = rootView.findViewById(R.id.search_clear); + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + + return rootView; + } + + @Override + public void onStart() { + super.onStart(); mSearchView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @@ -142,10 +155,6 @@ public class HomeFragment extends Fragment { mDomainAdapter.loadDomains(mSearchView.getText().toString(), true); } }); - - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - - return rootView; } @Override diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java index 71d634e9ea..6860742a0d 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -12,6 +12,7 @@ import android.widget.TextView; import com.squareup.picasso.Picasso; +import java.util.Arrays; import java.util.List; import io.highfidelity.hifiinterface.R; @@ -36,19 +37,39 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { + mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mDomains.length == 0) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } + } + } + } + public void loadDomains(String filterText, boolean forceRefresh) { domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() { @Override @@ -60,13 +81,16 @@ public class DomainAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 17:47:40 -0300 Subject: [PATCH 014/106] Android - People List - cache in memory only on this instance (deleted if app restarts) allows for fast list display. Showing again also asks for update so in some ms the list gets updated in case of changes --- .../fragment/FriendsFragment.java | 14 +++++--- .../hifiinterface/view/UserListAdapter.java | 35 ++++++++++++++++--- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 2a008d7950..2475c4d887 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -98,13 +98,17 @@ public class FriendsFragment extends Fragment { mUsersAdapter.setListener(new UserListAdapter.AdapterListener() { @Override - public void onEmptyAdapter() { - mSwipeRefreshLayout.setRefreshing(false); + public void onEmptyAdapter(boolean shouldStopRefreshing) { + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override - public void onNonEmptyAdapter() { - mSwipeRefreshLayout.setRefreshing(false); + public void onNonEmptyAdapter(boolean shouldStopRefreshing) { + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override @@ -115,6 +119,8 @@ public class FriendsFragment extends Fragment { mUsersView.setAdapter(mUsersAdapter); + mUsersAdapter.startLoad(); + mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 9f62b21250..a34de5b34e 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -37,28 +37,53 @@ public class UserListAdapter extends RecyclerView.Adapter USERS_TMP_CACHE; + public UserListAdapter(Context c, UsersProvider usersProvider) { mContext = c; mInflater = LayoutInflater.from(mContext); mProvider = usersProvider; - loadUsers(); } public void setListener(AdapterListener adapterListener) { mAdapterListener = adapterListener; } + public void startLoad() { + useTmpCachedUsers(); + loadUsers(); + } + + private void useTmpCachedUsers() { + if (USERS_TMP_CACHE != null && USERS_TMP_CACHE.size() > 0) { + mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); + mUsers.addAll(USERS_TMP_CACHE); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } + } + } + } + public void loadUsers() { mProvider.retrieve(new UsersProvider.UsersCallback() { @Override public void retrieveOk(List users) { mUsers = new ArrayList<>(users); notifyDataSetChanged(); + + USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); + USERS_TMP_CACHE.addAll(mUsers); + if (mAdapterListener != null) { if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(); + mAdapterListener.onEmptyAdapter(true); } else { - mAdapterListener.onNonEmptyAdapter(); + mAdapterListener.onNonEmptyAdapter(true); } } } @@ -240,8 +265,8 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 18:40:10 -0300 Subject: [PATCH 015/106] Android - People and Domain list - synchronize cache and request code --- .../hifiinterface/view/DomainAdapter.java | 40 ++++++++++--------- .../hifiinterface/view/UserListAdapter.java | 37 +++++++++-------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java index 6860742a0d..78251ac4a4 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -57,14 +57,16 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { - mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); - notifyDataSetChanged(); - if (mAdapterListener != null) { - if (mDomains.length == 0) { - mAdapterListener.onEmptyAdapter(false); - } else { - mAdapterListener.onNonEmptyAdapter(false); + synchronized (this) { + if (DOMAINS_TMP_CACHE != null && DOMAINS_TMP_CACHE.length > 0) { + mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mDomains.length == 0) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } } } } @@ -81,16 +83,18 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { - mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); - mUsers.addAll(USERS_TMP_CACHE); - notifyDataSetChanged(); - if (mAdapterListener != null) { - if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(false); - } else { - mAdapterListener.onNonEmptyAdapter(false); + synchronized (this) { + if (USERS_TMP_CACHE != null && USERS_TMP_CACHE.size() > 0) { + mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); + mUsers.addAll(USERS_TMP_CACHE); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } } } } @@ -76,14 +78,16 @@ public class UserListAdapter extends RecyclerView.Adapter(users); notifyDataSetChanged(); - USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); - USERS_TMP_CACHE.addAll(mUsers); + synchronized (this) { + USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); + USERS_TMP_CACHE.addAll(mUsers); - if (mAdapterListener != null) { - if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(true); - } else { - mAdapterListener.onNonEmptyAdapter(true); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(true); + } else { + mAdapterListener.onNonEmptyAdapter(true); + } } } } @@ -269,4 +273,5 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 15:55:00 -0700 Subject: [PATCH 016/106] adding fix --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 4e920e430b..1081f8c4e7 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -140,8 +140,7 @@ int TabletButtonsProxyModel::buttonIndex(const QString &uuid) { return -1; } -void TabletButtonsProxyModel::setPageIndex(int pageIndex) -{ +void TabletButtonsProxyModel::setPageIndex(int pageIndex) { if (_pageIndex == pageIndex) return; @@ -465,6 +464,9 @@ void TabletProxy::onTabletShown() { _showRunningScripts = false; pushOntoStack("hifi/dialogs/TabletRunningScripts.qml"); } + if (_currentPathLoaded == TABLET_HOME_SOURCE_URL) { + loadHomeScreen(true); + } } } From 81d0e212dd737b34f43f2f0fff845855a06ee0f9 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 15:57:17 -0700 Subject: [PATCH 017/106] removing print statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 10a9f01337..cccd37ad95 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -117,7 +117,6 @@ Item { id: pageRepeater model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) onItemAdded: { - console.log("onItemAdded: " + tabletProxy.buttons.rowCount()); item.proxyModel.sourceModel = tabletProxy.buttons; item.proxyModel.pageIndex = index; } @@ -209,7 +208,6 @@ Item { Component.onCompleted: updateProperties() function updateProperties() { - console.log("updateProperties: " + tabletProxy.buttons.rowCount()); var keys = Object.keys(modelData.properties).forEach(function (key) { if (tabletButton[key] !== modelData.properties[key]) { tabletButton[key] = modelData.properties[key]; @@ -272,9 +270,6 @@ Item { anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count - Component.onCompleted: { - console.log("pageIndicator: " + pageIndicator.count); - } } } From bb176eb22834b01d6c1e89d38f18533240f5cef7 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 16:00:01 -0700 Subject: [PATCH 018/106] trailing debug statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 1 - scripts/system/menu.js | 1 - scripts/system/tablet-ui/tabletUI.js | 4 +--- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index cccd37ad95..1922b02f93 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -269,7 +269,6 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count - } } diff --git a/scripts/system/menu.js b/scripts/system/menu.js index 1db210216a..d669d3d918 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -52,7 +52,6 @@ var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet- if (isHMDMode) { button = tablet.addButton(buttonProperties); button.clicked.connect(onClicked); - print("Adding menu button"); } else if (button) { button.clicked.disconnect(onClicked); tablet.removeButton(button); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index c28040000b..d78e90b789 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -22,7 +22,7 @@ var DEFAULT_TABLET_SCALE = 70; var preMakeTime = Date.now(); var validCheckTime = Date.now(); - var debugTablet = true; + var debugTablet = false; var tabletScalePercentage = 70.0; var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; @@ -298,7 +298,6 @@ } wantsMenu = clicked; }); - clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -306,7 +305,6 @@ } wantsMenu = clicked; }); - clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From 4e9a5446187187ffcba87a846ec1b22f36cdbf15 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 16:01:24 -0700 Subject: [PATCH 019/106] redoing newline --- scripts/system/tablet-ui/tabletUI.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index d78e90b789..80ddbeca8b 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -298,6 +298,7 @@ } wantsMenu = clicked; }); + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -305,6 +306,7 @@ } wantsMenu = clicked; }); + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From 11c1845dfa2ca7e1e01abc663582fff876562dbb Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 21 Aug 2018 16:11:45 -0700 Subject: [PATCH 020/106] added rotate script changes that allow using the hand hip azimuth --- scripts/developer/rotateApp.js | 340 +++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 scripts/developer/rotateApp.js diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js new file mode 100644 index 0000000000..d65eedfbbe --- /dev/null +++ b/scripts/developer/rotateApp.js @@ -0,0 +1,340 @@ +/* global Script, Vec3, MyAvatar, Tablet, Messages, Quat, +DebugDraw, Mat4, Entities, Xform, Controller, Camera, console, document*/ + +Script.registerValue("ROTATEAPP", true); + +var TABLET_BUTTON_NAME = "ROTATE"; +var CHANGE_OF_BASIS_ROTATION = { x: 0, y: 1, z: 0, w: 0 }; +var DEFAULT_HEAD_TURN_THRESHOLD = 0.5333; +var DEFAULT_HEAD_TURN_FILTER_LENGTH = 4.0; +var LOADING_DELAY = 2000; +var AVERAGING_RATE = 0.03; +var INCREASING = 1.0; +var DECREASING = -1.0; +var DEGREES_PER_PI_RADIANS = 180.0; +var FILTER_FUDGE_RANGE = 0.9; + +var activated = false; +var documentLoaded = false; +var sciptLoaded = false; +var headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; +var hipToLeftHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; +var hipToRightHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; +var averageAzimuth = 0.0; +var hipsPositionRigSpace = { x: 0, y: 0, z: 0 }; +var spine2PositionRigSpace = { x: 0, y: 0, z: 0 }; +var hipsRotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; +var spine2RotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; +var spine2Rotation = { x: 0, y: 0, z: 0, w: 1 }; +var hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; +var hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; + +var ikTypes = { + RotationAndPosition: 0, + RotationOnly: 1, + HmdHead: 2, + HipsRelativeRotationAndPosition: 3, + Spline: 4, + Unknown: 5 +}; + + +var ANIM_VARS = [ + //"headType", + "spine2Type", + //"hipsType", + "spine2Position", + "spine2Rotation", + //"hipsPosition", + //"hipsRotation" +]; + +var handlerId = MyAvatar.addAnimationStateHandler(function (props) { + //print("in callback"); + //print("props spine2 pos: " + props.spine2Position.x + " " + props.spine2Position.y + " " + props.spine2Position.z); + //print("props hip pos: " + props.hipsPosition.x + " " + props.hipsPosition.y + " " + props.hipsPosition.z); + var result = {}; + //{x:0,y:0,z:0} + //result.headType = ikTypes.HmdHead; + //result.hipsType = ikTypes.RotationAndPosition; + //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; + //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // + result.spine2Type = ikTypes.Spline; + result.spine2Position = { x: 0, y: 1.3, z: 0 }; + result.spine2Rotation = spine2Rotation; + + return result; +}, ANIM_VARS); + +// define state readings constructor +function StateReading(headPose, rhandPose, lhandPose, diffFromAverageEulers) { + this.headPose = headPose; + this.rhandPose = rhandPose; + this.lhandPose = lhandPose; + this.diffFromAverageEulers = diffFromAverageEulers; +} + +// define current state readings object for holding tracker readings and current differences from averages +var currentStateReadings = new StateReading(Controller.getPoseValue(Controller.Standard.Head), + Controller.getPoseValue(Controller.Standard.RightHand), + Controller.getPoseValue(Controller.Standard.LeftHand), + { x: 0, y: 0, z: 0 }); + +// declare the checkbox constructor +function AppCheckbox(type,id,eventType,isChecked) { + this.type = type; + this.id = id; + this.eventType = eventType; + this.data = {value: isChecked}; +} + +var usingStepResetRotationDirection = new AppCheckbox("checkboxtick", "stepReset", "onStepResetCheckBox", false); +var usingDrawAverageFacing = new AppCheckbox("checkboxtick", "drawAverage", "onDrawAverageFacingCheckBox", false); +var checkBoxArray = new Array(usingStepResetRotationDirection, usingDrawAverageFacing); + +// declare the html slider constructor +function AppProperty(name, type, eventType, signalType, setFunction, initValue, convertToThreshold, convertToSlider, signalOn) { + this.name = name; + this.type = type; + this.eventType = eventType; + this.signalType = signalType; + this.setValue = setFunction; + this.value = initValue; + this.get = function () { + return this.value; + }; + this.convertToThreshold = convertToThreshold; + this.convertToSlider = convertToSlider; +} + +// var HTML_URL = Script.resolvePath("file:///c:/dev/hifi_fork/hifi/scripts/developer/rotateRecenterApp.html"); +var HTML_URL = Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/rotateRecenterApp.html"); +var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + +// define the sliders +var filterLengthProperty = new AppProperty("#filterLength-slider", "slider", "onFilterLengthSlider", "filterSignal", + setFilterLength, DEFAULT_HEAD_TURN_FILTER_LENGTH, function (num) { + var base = 5; + var shift = 0; + return convertExponential(base, num, INCREASING, shift); + }, function (num) { + var base = 5; + var shift = 0; + return convertLog(base, num, INCREASING, shift); + }, true); +var angleThresholdProperty = new AppProperty("#angleThreshold-slider", "slider", "onAngleThresholdSlider", "angleSignal", + setAngleThreshold, DEFAULT_HEAD_TURN_THRESHOLD, function (num) { + return convertToRadians(num); + }, function (num) { + return convertToDegrees(num); + }, true); + +var propArray = new Array(filterLengthProperty, angleThresholdProperty); + +function setFilterLength(num) { + filterLengthProperty.value = num; + MyAvatar.rotationRecenterFilterLength = filterLengthProperty.value; + +} + +function setAngleThreshold(num) { + angleThresholdProperty.value = num; + MyAvatar.rotationThreshold = angleThresholdProperty.value; +} + +function convertToRadians(num) { + return (num / DEGREES_PER_PI_RADIANS) * Math.PI; +} + +function convertToDegrees(num) { + return (num / Math.PI) * DEGREES_PER_PI_RADIANS; +} + +function getLog(x, y) { + return Math.log(y) / Math.log(x); +} + +function convertLog(base, num, direction, shift) { + return direction * getLog(base, (num + FILTER_FUDGE_RANGE)) + shift; +} + +function convertExponential(base, num, direction, shift) { + return Math.pow(base, (direction * num + shift)) - FILTER_FUDGE_RANGE; +} + +function manageClick() { + if (activated) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(HTML_URL); + } +} + +var tabletButton = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg"), + activeIcon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg") +}); + +function onKeyPress(event) { + if (event.text === "'") { + // when the sensors are reset, then reset the mode. + } +} + +function onWebEventReceived(msg) { + var message = JSON.parse(msg); + print(" we have a message from html dialog " + message.type); + propArray.forEach(function (prop) { + if (prop.eventType === message.type) { + prop.setValue(prop.convertToThreshold(message.data.value)); + print("message from " + prop.name); + // break; + } + }); + checkBoxArray.forEach(function(cbox) { + if (cbox.eventType === message.type) { + cbox.data.value = message.data.value; + if (cbox.id === "stepReset") { + MyAvatar.enableStepResetRotation = cbox.data.value; + } + if (cbox.id === "drawAverage") { + MyAvatar.enableDrawAverageFacing = cbox.data.value; + } + // break; + } + + }); + if (message.type === "onCreateRotateApp") { + print("document loaded"); + documentLoaded = true; + Script.setTimeout(initAppForm, LOADING_DELAY); + } +} + +function initAppForm() { + print("step app is loaded: " + documentLoaded); + if (documentLoaded === true) { + propArray.forEach(function (prop) { + print(prop.name); + tablet.emitScriptEvent(JSON.stringify({ + "type": "slider", + "id": prop.name, + "data": { "value": prop.convertToSlider(prop.value) } + })); + }); + checkBoxArray.forEach(function (cbox) { + tablet.emitScriptEvent(JSON.stringify({ + "type": "checkboxtick", + "id": cbox.id, + "data": { value: cbox.data.value } + })); + }); + } + +} + + +function onScreenChanged(type, url) { + print("Screen changed"); + if (type === "Web" && url === HTML_URL) { + if (!activated) { + // hook up to event bridge + tablet.webEventReceived.connect(onWebEventReceived); + print("after connect web event"); + MyAvatar.hmdLeanRecenterEnabled = true; + } + activated = true; + } else { + if (activated) { + // disconnect from event bridge + tablet.webEventReceived.disconnect(onWebEventReceived); + documentLoaded = false; + } + activated = false; + } +} + +function limitAngle(angle) { + return (angle + 180) % 360 - 180; +} + +function computeHandAzimuths() { + var leftHand = currentStateReadings.lhandPose.translation; + var rightHand = currentStateReadings.rhandPose.translation; + var head = currentStateReadings.headPose.translation; + var lHandMinusHead = Vec3.subtract(leftHand, head); + lHandMinusHead.y = 0.0; + var rHandMinusHead = Vec3.subtract(rightHand, head); + rHandMinusHead.y = 0.0; + print(JSON.stringify(leftHand)); + print(JSON.stringify(head)); + var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; + var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); + var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); + hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, 0.99); + hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, 0.99); + + // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); + // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); + var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; + print(leftRightMidpoint); + + return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); + + +} + +function update(dt) { + //update state readings + currentStateReadings.head = Controller.getPoseValue(Controller.Standard.Head); + currentStateReadings.rhandPose = Controller.getPoseValue(Controller.Standard.RightHand); + currentStateReadings.lhandPose = Controller.getPoseValue(Controller.Standard.LeftHand); + + print(JSON.stringify(currentStateReadings.head)); + + var latestSpineRotation = computeHandAzimuths(); + var zAxisSpineRotation = Vec3.multiplyQbyV(latestSpineRotation, { x: 0, y: 0, z: 1 }); + var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.rotation, zAxisSpineRotation); + DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisSpineRotation), { x: 1, y: 0, z: 0 }); + spine2Rotation = latestSpineRotation; + /* + if (HMD.active && !scriptLoaded) { + //Script.load("rotateApp.js"); + scriptLoaded = true; + } + + if (!HMD.active) { + scriptLoaded = false; + } + */ + + // handle the azimuth of the arms + +} + +function shutdownTabletApp() { + tablet.removeButton(tabletButton); + if (activated) { + tablet.webEventReceived.disconnect(onWebEventReceived); + tablet.gotoHomeScreen(); + } + tablet.screenChanged.disconnect(onScreenChanged); +} + +Script.setTimeout(function () { + tabletButton.clicked.connect(manageClick); + tablet.screenChanged.connect(onScreenChanged); + Script.update.connect(update); + Controller.keyPressEvent.connect(onKeyPress); +}, (LOADING_DELAY)); + +Script.scriptEnding.connect(function () { + // if (handlerId) { + // print("removing animation state handler"); + // handlerId = MyAvatar.removeAnimationStateHandler(handlerId); + // } + MyAvatar.hmdLeanRecenterEnabled = true; + Script.update.disconnect(update); + shutdownTabletApp(); +}); \ No newline at end of file From d74fef2ea4c68611ca25070fab22458d00219f6f Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 21 Aug 2018 16:27:47 -0700 Subject: [PATCH 021/106] fixed spine 2 position updating --- scripts/developer/rotateApp.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index d65eedfbbe..ea616b1697 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -60,7 +60,7 @@ var handlerId = MyAvatar.addAnimationStateHandler(function (props) { //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // result.spine2Type = ikTypes.Spline; - result.spine2Position = { x: 0, y: 1.3, z: 0 }; + result.spine2Position = spine2PositionRigSpace;// { x: 0, y: 1.3, z: 0 }; result.spine2Rotation = spine2Rotation; return result; @@ -298,6 +298,8 @@ function update(dt) { var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.rotation, zAxisSpineRotation); DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisSpineRotation), { x: 1, y: 0, z: 0 }); spine2Rotation = latestSpineRotation; + var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); + spine2PositionRigSpace = Vec3.multiplyQbyV(CHANGE_OF_BASIS_ROTATION, spine2Pos); /* if (HMD.active && !scriptLoaded) { //Script.load("rotateApp.js"); From 456b8bf2a927c40499cc420b44ae6a18e18070a4 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 09:36:52 -0700 Subject: [PATCH 022/106] tweaked the animvars callback to update the spine2 position --- interface/src/avatar/MyAvatar.cpp | 5 ++++ scripts/developer/rotateApp.js | 38 +++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c3d8b8df4f..04a6d58aae 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3937,6 +3937,11 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } + if (!isActive(Rotation) && getForceActivateRotation()) { + activate(Rotation); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + setForceActivateRotation(false); + } } else { if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index ea616b1697..7794232196 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -44,7 +44,7 @@ var ANIM_VARS = [ "spine2Type", //"hipsType", "spine2Position", - "spine2Rotation", + "spine2Rotation" //"hipsPosition", //"hipsRotation" ]; @@ -60,7 +60,7 @@ var handlerId = MyAvatar.addAnimationStateHandler(function (props) { //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // result.spine2Type = ikTypes.Spline; - result.spine2Position = spine2PositionRigSpace;// { x: 0, y: 1.3, z: 0 }; + result.spine2Position = spine2PositionRigSpace; // { x: 0, y: 1.3, z: 0 }; result.spine2Rotation = spine2Rotation; return result; @@ -267,18 +267,34 @@ function computeHandAzimuths() { lHandMinusHead.y = 0.0; var rHandMinusHead = Vec3.subtract(rightHand, head); rHandMinusHead.y = 0.0; - print(JSON.stringify(leftHand)); - print(JSON.stringify(head)); + //print(JSON.stringify(leftHand)); + //print(JSON.stringify(head)); var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); - hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, 0.99); - hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, 0.99); + hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, AVERAGING_RATE); + hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, AVERAGING_RATE); // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); - var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; - print(leftRightMidpoint); + var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHand).y + Quat.safeEulerAngles(hipToRHand).y) / 2.0; + var leftRightMidpointAverage = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; + + // limit the angle because we are flipped by 180, fix this tomorrow. + // print(leftRightMidpointAverage/180.0); + // print("threshold value " + angleThresholdProperty.value); + // get it into radians too!! + if ((Math.abs(leftRightMidpointAverage/180.0) * Math.PI) > angleThresholdProperty.value) { + print("recenter the feet under the head"); + MyAvatar.triggerRotationRecenter(); + } + + var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); + var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); + var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.orientation, zAxisSpineRotation); + DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); + + //print(leftRightMidpoint); return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); @@ -291,12 +307,10 @@ function update(dt) { currentStateReadings.rhandPose = Controller.getPoseValue(Controller.Standard.RightHand); currentStateReadings.lhandPose = Controller.getPoseValue(Controller.Standard.LeftHand); - print(JSON.stringify(currentStateReadings.head)); + //print(JSON.stringify(currentStateReadings.head)); var latestSpineRotation = computeHandAzimuths(); - var zAxisSpineRotation = Vec3.multiplyQbyV(latestSpineRotation, { x: 0, y: 0, z: 1 }); - var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.rotation, zAxisSpineRotation); - DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisSpineRotation), { x: 1, y: 0, z: 0 }); + spine2Rotation = latestSpineRotation; var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); spine2PositionRigSpace = Vec3.multiplyQbyV(CHANGE_OF_BASIS_ROTATION, spine2Pos); From 1e3ca6f902fc9df3cab552c5ebb9a260ddb685df Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 12:57:06 -0700 Subject: [PATCH 023/106] added quicker follow code and added head to rotate calculation --- interface/src/avatar/MyAvatar.cpp | 21 +++++++++------- libraries/shared/src/AvatarConstants.h | 2 +- scripts/developer/rotateApp.js | 33 +++++++++++++++++++------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 04a6d58aae..c79f589b15 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3797,7 +3797,7 @@ void MyAvatar::lateUpdatePalms() { } -static const float FOLLOW_TIME = 0.5f; +static const float FOLLOW_TIME = 0.1f; MyAvatar::FollowHelper::FollowHelper() { deactivate(); @@ -3902,8 +3902,13 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head")); glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); + //if (!isActive(Horizontal) && + // (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + DEFAULT_AVATAR_SPINE_STRETCH_LIMIT))) { + // myAvatar.setResetMode(true); + // stepDetected = true; + //} if (!isActive(Horizontal) && - (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + DEFAULT_AVATAR_SPINE_STRETCH_LIMIT))) { + (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance * myAvatar.getAvatarScale())))) { myAvatar.setResetMode(true); stepDetected = true; } @@ -3925,10 +3930,15 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { - if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + if (!isActive(Rotation) && getForceActivateRotation()) { activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + setForceActivateRotation(false); } + //if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + // activate(Rotation); + // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + //} if (myAvatar.getCenterOfGravityModelEnabled()) { if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); @@ -3937,11 +3947,6 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } - if (!isActive(Rotation) && getForceActivateRotation()) { - activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); - setForceActivateRotation(false); - } } else { if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 39117de7ef..b97f586d32 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -34,7 +34,7 @@ const float DEFAULT_HANDS_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 3.3f; const float DEFAULT_HEAD_VELOCITY_STEPPING_THRESHOLD = 0.18f; const float DEFAULT_HEAD_PITCH_STEPPING_TOLERANCE = 7.0f; const float DEFAULT_HEAD_ROLL_STEPPING_TOLERANCE = 7.0f; -const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.07f; +const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.05f; const float DEFAULT_AVATAR_FORWARD_DAMPENING_FACTOR = 0.5f; const float DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR = 2.0f; const float DEFAULT_AVATAR_HIPS_MASS = 40.0f; diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index 7794232196..4d18910335 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -259,7 +259,7 @@ function limitAngle(angle) { return (angle + 180) % 360 - 180; } -function computeHandAzimuths() { +function computeHandAzimuths(timeElapsed) { var leftHand = currentStateReadings.lhandPose.translation; var rightHand = currentStateReadings.rhandPose.translation; var head = currentStateReadings.headPose.translation; @@ -272,8 +272,9 @@ function computeHandAzimuths() { var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); - hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, AVERAGING_RATE); - hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, AVERAGING_RATE); + var tau = timeElapsed / filterLengthProperty.value; + hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, tau); + hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, tau); // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); @@ -283,11 +284,7 @@ function computeHandAzimuths() { // limit the angle because we are flipped by 180, fix this tomorrow. // print(leftRightMidpointAverage/180.0); // print("threshold value " + angleThresholdProperty.value); - // get it into radians too!! - if ((Math.abs(leftRightMidpointAverage/180.0) * Math.PI) > angleThresholdProperty.value) { - print("recenter the feet under the head"); - MyAvatar.triggerRotationRecenter(); - } + var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); @@ -295,6 +292,22 @@ function computeHandAzimuths() { DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); //print(leftRightMidpoint); + + var headPoseRigSpace = Quat.multiply(CHANGE_OF_BASIS_ROTATION, currentStateReadings.head.rotation); + headPoseAverageOrientation = Quat.slerp(headPoseAverageOrientation, headPoseRigSpace, tau); + var headPoseAverageEulers = Quat.safeEulerAngles(headPoseAverageOrientation); + + // get it into radians too!! + // average head and hands 50/50 + print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y) + var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; + if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { + print("recenter the feet under the head"); + MyAvatar.triggerRotationRecenter(); + hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; + hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; + headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; + } return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); @@ -309,7 +322,9 @@ function update(dt) { //print(JSON.stringify(currentStateReadings.head)); - var latestSpineRotation = computeHandAzimuths(); + + + var latestSpineRotation = computeHandAzimuths(dt); spine2Rotation = latestSpineRotation; var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); From 9dfaf32b7240f22e88ea611966e3a4373b2d552a Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 14:12:19 -0700 Subject: [PATCH 024/106] fixed the step animation but it still needs work --- interface/src/avatar/MyAvatar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 46a5cbe414..44ee5e30c6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3933,6 +3933,9 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { + const float VELOCITY_THRESHHOLD = 1.0f; + float currentVelocity = glm::length(myAvatar.getLocalVelocity() / myAvatar.getSensorToWorldScale()); + if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { @@ -3946,7 +3949,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); //} if (myAvatar.getCenterOfGravityModelEnabled()) { - if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { + if ((!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) || (isActive(Horizontal) && (currentVelocity > VELOCITY_THRESHHOLD))) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); From 5c0b12abf6a7d1983dc5a080cf31422c41ee36d3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 22 Aug 2018 12:07:32 -0700 Subject: [PATCH 025/106] ordered ray/parabola intersection code --- interface/src/avatar/AvatarManager.cpp | 8 +- interface/src/raypick/LaserPointer.cpp | 3 + interface/src/raypick/ParabolaPointer.cpp | 3 + .../src/RenderablePolyVoxEntityItem.cpp | 13 +- libraries/entities/src/EntityTree.cpp | 54 +- libraries/entities/src/EntityTreeElement.cpp | 33 +- libraries/entities/src/EntityTreeElement.h | 9 +- libraries/entities/src/ShapeEntityItem.cpp | 28 +- libraries/octree/src/Octree.cpp | 127 ++--- libraries/octree/src/Octree.h | 22 +- libraries/render-utils/src/Model.cpp | 177 ++++-- libraries/render-utils/src/Model.h | 10 + libraries/shared/src/GeometryUtil.cpp | 7 +- libraries/shared/src/TriangleSet.cpp | 518 +++++++++++------- libraries/shared/src/TriangleSet.h | 25 +- 15 files changed, 589 insertions(+), 448 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0d180bc40d..139f44d58b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -553,8 +553,6 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic return result; } - glm::vec3 normDirection = glm::normalize(ray.direction); - auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -587,14 +585,14 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findRayCapsuleIntersection(ray.origin, normDirection, start, end, radius, distance); + bool intersects = findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); if (!intersects) { // ray doesn't intersect avatar's capsule continue; } QVariantMap extraInfo; - intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, normDirection, + intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true); if (intersects && (!result.intersects || distance < result.distance)) { @@ -608,7 +606,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic } if (result.intersects) { - result.intersection = ray.origin + normDirection * result.distance; + result.intersection = ray.origin + ray.direction * result.distance; } return result; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 2382a95105..40a5e5cb69 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -42,6 +42,9 @@ glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const glm::vec3 LaserPointer::getPickEnd(const PickResultPointer& pickResult, float distance) const { auto rayPickResult = std::static_pointer_cast(pickResult); + if (!rayPickResult) { + return glm::vec3(0.0f); + } if (distance > 0.0f) { PickRay pick = PickRay(rayPickResult->pickVariant); return pick.origin + distance * pick.direction; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 097c98340c..57d57e11c4 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -67,6 +67,9 @@ glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) co glm::vec3 ParabolaPointer::getPickEnd(const PickResultPointer& pickResult, float distance) const { auto parabolaPickResult = std::static_pointer_cast(pickResult); + if (!parabolaPickResult) { + return glm::vec3(0.0f); + } if (distance > 0.0f) { PickParabola pick = PickParabola(parabolaPickResult->pickVariant); return pick.origin + pick.velocity * distance + 0.5f * pick.acceleration * distance * distance; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index c11ccb70a0..63f27dd170 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -584,8 +584,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); - glm::vec4 directionInVoxel = glm::normalize(farInVoxel - originInVoxel); - glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); PolyVox::RaycastResult raycastResult = doRayCast(originInVoxel, farInVoxel, result); if (raycastResult == PolyVox::RaycastResults::Completed) { @@ -599,14 +597,9 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o voxelBox += result3 - Vectors::HALF; voxelBox += result3 + Vectors::HALF; - float voxelDistance; - bool hit = voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), - voxelDistance, face, surfaceNormal); - - glm::vec4 voxelIntersectionPoint = glm::vec4(glm::vec3(originInVoxel) + glm::vec3(directionInVoxel) * voxelDistance, 1.0); - glm::vec4 intersectionPoint = vtwMatrix * voxelIntersectionPoint; - distance = glm::distance(origin, glm::vec3(intersectionPoint)); - return hit; + glm::vec4 directionInVoxel = wtvMatrix * glm::vec4(direction, 0.0f); + return voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), + distance, face, surfaceNormal); } bool RenderablePolyVoxEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 377e192bb1..1cf6e45d5b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -825,15 +825,38 @@ bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) RayArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, keepSearching, + EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; + // We recurse OctreeElements in order, so if we hit something, we can stop immediately + keepSearching = false; } return keepSearching; } +float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { + RayArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); + float distance = FLT_MAX; + // If origin is inside the cube, always check this element first + if (entityTreeElementPointer->getAACube().contains(args->origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, boundDistance, face, surfaceNormal)) { + // Don't add this cell if it's already farther than our best distance so far + if (boundDistance < args->distance) { + distance = boundDistance; + } + } + } + return distance; +} + EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, bool visibleOnly, bool collidableOnly, bool precisionPicking, @@ -846,7 +869,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm: bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&]{ - recurseTreeWithOperation(findRayIntersectionOp, &args); + recurseTreeWithOperationSorted(findRayIntersectionOp, findRayIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { @@ -860,15 +883,38 @@ bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extra ParabolaArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, keepSearching, + EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, args->element, args->parabolicDistance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; + // We recurse OctreeElements in order, so if we hit something, we can stop immediately + keepSearching = false; } return keepSearching; } +float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { + ParabolaArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); + float distance = FLT_MAX; + // If origin is inside the cube, always check this element first + if (entityTreeElementPointer->getAACube().contains(args->origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (entityTreeElementPointer->getAACube().findParabolaIntersection(args->origin, args->velocity, args->acceleration, boundDistance, face, surfaceNormal)) { + // Don't add this cell if it's already farther than our best distance so far + if (boundDistance < args->parabolicDistance) { + distance = boundDistance; + } + } + } + return distance; +} + EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, QVector entityIdsToInclude, QVector entityIdsToDiscard, bool visibleOnly, bool collidableOnly, bool precisionPicking, @@ -882,7 +928,7 @@ EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&] { - recurseTreeWithOperation(findParabolaIntersectionOp, &args); + recurseTreeWithOperationSorted(findParabolaIntersectionOp, findParabolaIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 5974fce6c5..e8e11c0ee1 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -140,27 +140,17 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 } EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking) { + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = std::numeric_limits::max(); + float distanceToElementCube = FLT_MAX; BoxFace localFace; glm::vec3 localSurfaceNormal; - // if the ray doesn't intersect with our cube OR the distance to element is less than current best distance - // we can stop searching! - bool hit = _cube.findRayIntersection(origin, direction, distanceToElementCube, localFace, localSurfaceNormal); - if (!hit || (!_cube.contains(origin) && distanceToElementCube > distance)) { - keepSearching = false; // no point in continuing to search - return result; // we did not intersect - } - - // by default, we only allow intersections with leaves with content if (!canPickIntersect()) { - return result; // we don't intersect with non-leaves, and we keep searching + return result; } // if the distance to the element cube is not less than the current best distance, then it's not possible @@ -289,7 +279,7 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad } EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { @@ -299,17 +289,8 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin BoxFace localFace; glm::vec3 localSurfaceNormal; - // if the parabola doesn't intersect with our cube OR the distance to element is less than current best distance - // we can stop searching! - bool hit = _cube.findParabolaIntersection(origin, velocity, acceleration, distanceToElementCube, localFace, localSurfaceNormal); - if (!hit || (!_cube.contains(origin) && distanceToElementCube > parabolicDistance)) { - keepSearching = false; // no point in continuing to search - return result; // we did not intersect - } - - // by default, we only allow intersections with leaves with content if (!canPickIntersect()) { - return result; // we don't intersect with non-leaves, and we keep searching + return result; } // if the distance to the element cube is not less than the current best distance, then it's not possible diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d6f9db08d6..793340c9a4 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -136,10 +136,9 @@ public: virtual bool canPickIntersect() const override { return hasEntities(); } virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking = false); + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); virtual EntityItemID findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, @@ -149,7 +148,7 @@ public: glm::vec3& penetration, void** penetratedObject) const override; virtual EntityItemID findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index e4ea1470c1..773a7059dc 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -262,20 +262,18 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix(); glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); - glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); + glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); - float localDistance; // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 - if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) { - // determine where on the unit sphere the hit point occured - glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); - // then translate back to work coordinates - glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); - distance = glm::distance(origin, hitAt); + if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, distance)) { bool success; - // FIXME: this is only correct for uniformly scaled spheres - surfaceNormal = glm::normalize(hitAt - getCenterPosition(success)); - if (!success) { + glm::vec3 center = getCenterPosition(success); + if (success) { + // FIXME: this is only correct for uniformly scaled spheres + // determine where on the unit sphere the hit point occured + glm::vec3 hitAt = origin + (direction * distance); + surfaceNormal = glm::normalize(hitAt - center); + } else { return false; } return true; @@ -297,9 +295,11 @@ bool ShapeEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 if (findParabolaSphereIntersection(entityFrameOrigin, entityFrameVelocity, entityFrameAcceleration, glm::vec3(0.0f), 0.5f, parabolicDistance)) { bool success; - // FIXME: this is only correct for uniformly scaled spheres - surfaceNormal = glm::normalize((origin + velocity * parabolicDistance + 0.5f * acceleration * parabolicDistance * parabolicDistance) - getCenterPosition(success)); - if (!success) { + glm::vec3 center = getCenterPosition(success); + if (success) { + // FIXME: this is only correct for uniformly scaled spheres + surfaceNormal = glm::normalize((origin + velocity * parabolicDistance + 0.5f * acceleration * parabolicDistance * parabolicDistance) - center); + } else { return false; } return true; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 9bb0e25982..c6474c1fe4 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -68,55 +68,12 @@ Octree::~Octree() { eraseAllOctreeElements(false); } - -// Inserts the value and key into three arrays sorted by the key array, the first array is the value, -// the second array is a sorted key for the value, the third array is the index for the value in it original -// non-sorted array -// returns -1 if size exceeded -// originalIndexArray is optional -int insertOctreeElementIntoSortedArrays(const OctreeElementPointer& value, float key, int originalIndex, - OctreeElementPointer* valueArray, float* keyArray, int* originalIndexArray, - int currentCount, int maxCount) { - - if (currentCount < maxCount) { - int i = 0; - if (currentCount > 0) { - while (i < currentCount && key > keyArray[i]) { - i++; - } - // i is our desired location - // shift array elements to the right - if (i < currentCount && i+1 < maxCount) { - for (int j = currentCount - 1; j > i; j--) { - valueArray[j] = valueArray[j - 1]; - keyArray[j] = keyArray[j - 1]; - } - } - } - // place new element at i - valueArray[i] = value; - keyArray[i] = key; - if (originalIndexArray) { - originalIndexArray[i] = originalIndex; - } - return currentCount + 1; - } - return -1; // error case -} - - - // Recurses voxel tree calling the RecurseOctreeOperation function for each element. // stops recursion if operation function returns false. void Octree::recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData) { recurseElementWithOperation(_rootElement, operation, extraData); } -// Recurses voxel tree calling the RecurseOctreePostFixOperation function for each element in post-fix order. -void Octree::recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData) { - recurseElementWithPostOperation(_rootElement, operation, extraData); -} - // Recurses voxel element with an operation function void Octree::recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount) { @@ -129,71 +86,53 @@ void Octree::recurseElementWithOperation(const OctreeElementPointer& element, co for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer child = element->getChildAtIndex(i); if (child) { - recurseElementWithOperation(child, operation, extraData, recursionCount+1); + recurseElementWithOperation(child, operation, extraData, recursionCount + 1); } } } } -// Recurses voxel element with an operation function -void Octree::recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - void* extraData, int recursionCount) { +void Octree::recurseTreeWithOperationSorted(const RecurseOctreeOperation& operation, const RecurseOctreeSortingOperation& sortingOperation, void* extraData) { + recurseElementWithOperationSorted(_rootElement, operation, sortingOperation, extraData); +} + +// Recurses voxel element with an operation function, calling operation on its children in a specific order +bool Octree::recurseElementWithOperationSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, + const RecurseOctreeSortingOperation& sortingOperation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - HIFI_FCDEBUG(octree(), "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - return; + HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperationSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + // If we go too deep, we want to keep searching other paths + return true; } + bool keepSearching = operation(element, extraData); + + std::vector sortedChildren; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer child = element->getChildAtIndex(i); if (child) { - recurseElementWithPostOperation(child, operation, extraData, recursionCount+1); - } - } - operation(element, extraData); -} - -// Recurses voxel tree calling the RecurseOctreeOperation function for each element. -// stops recursion if operation function returns false. -void Octree::recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData) { - - recurseElementWithOperationDistanceSorted(_rootElement, operation, point, extraData); -} - -// Recurses voxel element with an operation function -void Octree::recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData, int recursionCount) { - - if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - return; - } - - if (operation(element, extraData)) { - // determine the distance sorted order of our children - OctreeElementPointer sortedChildren[NUMBER_OF_CHILDREN] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - float distancesToChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int indexOfChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int currentCount = 0; - - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - OctreeElementPointer childElement = element->getChildAtIndex(i); - if (childElement) { - // chance to optimize, doesn't need to be actual distance!! Could be distance squared - float distanceSquared = childElement->distanceSquareToPoint(point); - currentCount = insertOctreeElementIntoSortedArrays(childElement, distanceSquared, i, - sortedChildren, (float*)&distancesToChildren, - (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); - } - } - - for (int i = 0; i < currentCount; i++) { - OctreeElementPointer childElement = sortedChildren[i]; - if (childElement) { - recurseElementWithOperationDistanceSorted(childElement, operation, point, extraData); + float priority = sortingOperation(child, extraData); + if (priority < FLT_MAX) { + sortedChildren.emplace_back(priority, child); } } } + + if (sortedChildren.size() > 1) { + static auto comparator = [](const SortedChild& left, const SortedChild& right) { return left.first > right.first; }; + std::sort(sortedChildren.begin(), sortedChildren.end(), comparator); + } + + for (auto it = sortedChildren.begin(); it != sortedChildren.end(); ++it) { + const SortedChild& sortedChild = *it; + // Our children were sorted, so if one hits something, we don't need to check the others + if (!recurseElementWithOperationSorted(sortedChild.second, operation, sortingOperation, extraData, recursionCount + 1)) { + return false; + } + } + // We checked all our children and didn't find anything. + // Stop if we hit something in this element. Continue if we didn't. + return keepSearching; } void Octree::recurseTreeWithOperator(RecurseOctreeOperator* operatorObject) { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index b827ed7cd0..a2b2f227cb 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -49,6 +49,9 @@ public: // Callback function, for recuseTreeWithOperation using RecurseOctreeOperation = std::function; +// Function for sorting octree children during recursion. If return value == FLT_MAX, child is discarded +using RecurseOctreeSortingOperation = std::function; +using SortedChild = std::pair; typedef QHash CubeList; const bool NO_EXISTS_BITS = false; @@ -163,17 +166,10 @@ public: OctreeElementPointer getOrCreateChildElementContaining(const AACube& box); void recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); - void recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); - - /// \param operation type of operation - /// \param point point in world-frame (meters) - /// \param extraData hook for user data to be interpreted by special context - void recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData = NULL); + void recurseTreeWithOperationSorted(const RecurseOctreeOperation& operation, const RecurseOctreeSortingOperation& sortingOperation, void* extraData = NULL); void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject); - bool isDirty() const { return _isDirty; } void clearDirtyBit() { _isDirty = false; } void setDirtyBit() { _isDirty = true; } @@ -227,14 +223,8 @@ public: void recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount = 0); - - /// Traverse child nodes of node applying operation in post-fix order - /// - void recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - void* extraData, int recursionCount = 0); - - void recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData, int recursionCount = 0); + bool recurseElementWithOperationSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, + const RecurseOctreeSortingOperation& sortingOperation, void* extraData, int recursionCount = 0); bool recurseElementWithOperator(const OctreeElementPointer& element, RecurseOctreeOperator* operatorObject, int recursionCount = 0); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8f4eb57f8e..22bc6b6ca7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -379,14 +379,17 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { QMutexLocker locker(&_mutex); - float bestDistance = std::numeric_limits::max(); + float bestDistance = FLT_MAX; + BoxFace bestFace; Triangle bestModelTriangle; Triangle bestWorldTriangle; - int bestSubMeshIndex = 0; + glm::vec3 bestWorldIntersectionPoint; + glm::vec3 bestMeshIntersectionPoint; + int bestPartIndex; + int bestShapeID; + int bestSubMeshIndex; - int subMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); - if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -399,39 +402,75 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); int shapeID = 0; + int subMeshIndex = 0; + + std::vector sortedTriangleSets; for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { int partIndex = 0; - for (auto &partTriangleSet : meshTriangleSets) { - float triangleSetDistance; - BoxFace triangleSetFace; - Triangle triangleSetTriangle; - if (partTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { - glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); - glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); - float worldDistance = glm::distance(origin, worldIntersectionPoint); - - if (worldDistance < bestDistance) { - bestDistance = worldDistance; - intersectedSomething = true; - face = triangleSetFace; - bestModelTriangle = triangleSetTriangle; - bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; - extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); - extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); - extraInfo["partIndex"] = partIndex; - extraInfo["shapeID"] = shapeID; - bestSubMeshIndex = subMeshIndex; + for (auto& partTriangleSet : meshTriangleSets) { + float priority = FLT_MAX; + if (partTriangleSet.getBounds().contains(meshFrameOrigin)) { + priority = 0.0f; + } else { + float partBoundDistance = FLT_MAX; + BoxFace partBoundFace; + glm::vec3 partBoundNormal; + if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, partBoundDistance, + partBoundFace, partBoundNormal)) { + priority = partBoundDistance; } } + + if (priority < FLT_MAX) { + sortedTriangleSets.emplace_back(priority, &partTriangleSet, partIndex, shapeID, subMeshIndex); + } partIndex++; shapeID++; } subMeshIndex++; } + if (sortedTriangleSets.size() > 1) { + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); + } + + for (auto it = sortedTriangleSets.begin(); it != sortedTriangleSets.end(); ++it) { + const SortedTriangleSet& sortedTriangleSet = *it; + // We can exit once triangleSetDistance > bestDistance + if (sortedTriangleSet.distance > bestDistance) { + break; + } + float triangleSetDistance = FLT_MAX; + BoxFace triangleSetFace; + Triangle triangleSetTriangle; + if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, + triangleSetTriangle, pickAgainstTriangles, allowBackface)) { + if (triangleSetDistance < bestDistance) { + bestDistance = triangleSetDistance; + intersectedSomething = true; + bestFace = triangleSetFace; + bestModelTriangle = triangleSetTriangle; + bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; + glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); + glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); + bestWorldIntersectionPoint = worldIntersectionPoint; + bestMeshIntersectionPoint = meshIntersectionPoint; + bestPartIndex = sortedTriangleSet.partIndex; + bestShapeID = sortedTriangleSet.shapeID; + bestSubMeshIndex = sortedTriangleSet.subMeshIndex; + } + } + } + if (intersectedSomething) { distance = bestDistance; + face = bestFace; surfaceNormal = bestWorldTriangle.getNormal(); + extraInfo["worldIntersectionPoint"] = vec3toVariant(bestWorldIntersectionPoint); + extraInfo["meshIntersectionPoint"] = vec3toVariant(bestMeshIntersectionPoint); + extraInfo["partIndex"] = bestPartIndex; + extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); @@ -483,13 +522,16 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co QMutexLocker locker(&_mutex); float bestDistance = FLT_MAX; + BoxFace bestFace; Triangle bestModelTriangle; Triangle bestWorldTriangle; - int bestSubMeshIndex = 0; + glm::vec3 bestWorldIntersectionPoint; + glm::vec3 bestMeshIntersectionPoint; + int bestPartIndex; + int bestShapeID; + int bestSubMeshIndex; - int subMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); - if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -503,40 +545,79 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co glm::vec3 meshFrameAcceleration = glm::vec3(worldToMeshMatrix * glm::vec4(acceleration, 0.0f)); int shapeID = 0; + int subMeshIndex = 0; + + std::vector sortedTriangleSets; for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { int partIndex = 0; - for (auto &partTriangleSet : meshTriangleSets) { - float triangleSetDistance; - BoxFace triangleSetFace; - Triangle triangleSetTriangle; - if (partTriangleSet.findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, - triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { - if (triangleSetDistance < bestDistance) { - bestDistance = triangleSetDistance; - intersectedSomething = true; - face = triangleSetFace; - bestModelTriangle = triangleSetTriangle; - bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; - glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance + - 0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance; - glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance + - 0.5f * acceleration * triangleSetDistance * triangleSetDistance; - extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); - extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); - extraInfo["partIndex"] = partIndex; - extraInfo["shapeID"] = shapeID; - bestSubMeshIndex = subMeshIndex; + for (auto& partTriangleSet : meshTriangleSets) { + float priority = FLT_MAX; + if (partTriangleSet.getBounds().contains(meshFrameOrigin)) { + priority = 0.0f; + } else { + float partBoundDistance = FLT_MAX; + BoxFace partBoundFace; + glm::vec3 partBoundNormal; + if (partTriangleSet.getBounds().findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, + partBoundDistance, partBoundFace, partBoundNormal)) { + priority = partBoundDistance; } } + + if (priority < FLT_MAX) { + sortedTriangleSets.emplace_back(priority, &partTriangleSet, partIndex, shapeID, subMeshIndex); + } partIndex++; shapeID++; } subMeshIndex++; } + if (sortedTriangleSets.size() > 1) { + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); + } + + for (auto it = sortedTriangleSets.begin(); it != sortedTriangleSets.end(); ++it) { + const SortedTriangleSet& sortedTriangleSet = *it; + // We can exit once triangleSetDistance > bestDistance + if (sortedTriangleSet.distance > bestDistance) { + break; + } + float triangleSetDistance = FLT_MAX; + BoxFace triangleSetFace; + Triangle triangleSetTriangle; + if (sortedTriangleSet.triangleSet->findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, + triangleSetDistance, triangleSetFace, triangleSetTriangle, + pickAgainstTriangles, allowBackface)) { + if (triangleSetDistance < bestDistance) { + bestDistance = triangleSetDistance; + intersectedSomething = true; + bestFace = triangleSetFace; + bestModelTriangle = triangleSetTriangle; + bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; + glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance + + 0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance; + glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance + + 0.5f * acceleration * triangleSetDistance * triangleSetDistance; + bestWorldIntersectionPoint = worldIntersectionPoint; + bestMeshIntersectionPoint = meshIntersectionPoint; + bestPartIndex = sortedTriangleSet.partIndex; + bestShapeID = sortedTriangleSet.shapeID; + bestSubMeshIndex = sortedTriangleSet.subMeshIndex; + // These sets can overlap, so we can't exit early if we find something + } + } + } + if (intersectedSomething) { parabolicDistance = bestDistance; + face = bestFace; surfaceNormal = bestWorldTriangle.getNormal(); + extraInfo["worldIntersectionPoint"] = vec3toVariant(bestWorldIntersectionPoint); + extraInfo["meshIntersectionPoint"] = vec3toVariant(bestMeshIntersectionPoint); + extraInfo["partIndex"] = bestPartIndex; + extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 627e5fddab..adb2fcdd3d 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -64,6 +64,16 @@ class Model; using ModelPointer = std::shared_ptr; using ModelWeakPointer = std::weak_ptr; +struct SortedTriangleSet { + SortedTriangleSet(float distance, TriangleSet* triangleSet, int partIndex, int shapeID, int subMeshIndex) : + distance(distance), triangleSet(triangleSet), partIndex(partIndex), shapeID(shapeID), subMeshIndex(subMeshIndex) {} + + float distance; + TriangleSet* triangleSet; + int partIndex; + int shapeID; + int subMeshIndex; +}; /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public std::enable_shared_from_this, public scriptable::ModelProvider { diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 6fb06eb624..a93c2ec9f3 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -286,12 +286,13 @@ bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& directi distance = 0.0f; return true; // starts inside the sphere } - float b = glm::dot(direction, relativeOrigin); - float radicand = b * b - c; + float b = 2.0f * glm::dot(direction, relativeOrigin); + float a = glm::dot(direction, direction); + float radicand = b * b - 4.0f * a * c; if (radicand < 0.0f) { return false; // doesn't hit the sphere } - float t = -b - sqrtf(radicand); + float t = 0.5f * (-b - sqrtf(radicand)) / a; if (t < 0.0f) { return false; // doesn't hit the sphere } diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index cde9c20cab..f3c0ef5776 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -13,6 +13,8 @@ #include "GLMHelpers.h" +#include + void TriangleSet::insert(const Triangle& t) { _isBalanced = false; @@ -30,47 +32,6 @@ void TriangleSet::clear() { _triangleOctree.clear(); } -bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { - - // reset our distance to be the max possible, lower level tests will store best distance here - distance = std::numeric_limits::max(); - - if (!_isBalanced) { - balanceOctree(); - } - - int trianglesTouched = 0; - auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, triangle, precision, trianglesTouched, allowBackface); - - #if WANT_DEBUGGING - if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); - } - #endif - return result; -} - -bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { - // reset our distance to be the max possible, lower level tests will store best distance here - parabolicDistance = FLT_MAX; - - if (!_isBalanced) { - balanceOctree(); - } - - int trianglesTouched = 0; - auto result = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, parabolicDistance, face, triangle, precision, trianglesTouched, allowBackface); - -#if WANT_DEBUGGING - if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); - } -#endif - return result; -} - bool TriangleSet::convexHullContains(const glm::vec3& point) const { if (!_bounds.contains(point)) { return false; @@ -97,7 +58,7 @@ void TriangleSet::debugDump() { } void TriangleSet::balanceOctree() { - _triangleOctree.reset(_bounds, 0); + _triangleOctree.reset(_bounds); // insert all the triangles for (size_t i = 0; i < _triangles.size(); i++) { @@ -106,79 +67,15 @@ void TriangleSet::balanceOctree() { _isBalanced = true; - #if WANT_DEBUGGING +#if WANT_DEBUGGING debugDump(); - #endif +#endif } - -// Determine of the given ray (origin/direction) in model space intersects with any triangles -// in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, Triangle& triangle, bool precision, - int& trianglesTouched, bool allowBackface) { - bool intersectedSomething = false; - float bestDistance = FLT_MAX; - - if (precision) { - for (const auto& triangleIndex : _triangleIndices) { - const auto& thisTriangle = _allTriangles[triangleIndex]; - float thisTriangleDistance; - trianglesTouched++; - if (findRayTriangleIntersection(origin, direction, thisTriangle, thisTriangleDistance, allowBackface)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - triangle = thisTriangle; - } - } - } - } else { - intersectedSomething = true; - bestDistance = distance; - } - - if (intersectedSomething) { - distance = bestDistance; - } - - return intersectedSomething; -} - -bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, Triangle& triangle, bool precision, - int& trianglesTouched, bool allowBackface) { - bool intersectedSomething = false; - float bestDistance = FLT_MAX; - - if (precision) { - for (const auto& triangleIndex : _triangleIndices) { - const auto& thisTriangle = _allTriangles[triangleIndex]; - float thisTriangleDistance; - trianglesTouched++; - if (findParabolaTriangleIntersection(origin, velocity, acceleration, thisTriangle, thisTriangleDistance, allowBackface)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - triangle = thisTriangle; - } - } - } - } else { - intersectedSomething = true; - bestDistance = parabolicDistance; - } - - if (intersectedSomething) { - parabolicDistance = bestDistance; - } - - return intersectedSomething; -} - -static const int MAX_DEPTH = 4; // for now -static const int MAX_CHILDREN = 8; +// With an octree: 8 ^ MAX_DEPTH = 4096 leaves +//static const int MAX_DEPTH = 4; +// With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves +static const int MAX_DEPTH = 12; TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : _allTriangles(allTriangles) @@ -190,7 +87,8 @@ void TriangleSet::TriangleOctreeCell::clear() { _population = 0; _triangleIndices.clear(); _bounds.clear(); - _children.clear(); + _children.first.reset(); + _children.second.reset(); } void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { @@ -201,45 +99,76 @@ void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { void TriangleSet::TriangleOctreeCell::debugDump() { qDebug() << __FUNCTION__; - qDebug() << "bounds:" << getBounds(); - qDebug() << "depth:" << _depth; - qDebug() << "population:" << _population << "this level or below" + qDebug() << " bounds:" << getBounds(); + qDebug() << " depth:" << _depth; + qDebug() << " population:" << _population << "this level or below" << " ---- triangleIndices:" << _triangleIndices.size() << "in this cell"; - qDebug() << "child cells:" << _children.size(); + int numChildren = 0; + if (_children.first) { + numChildren++; + } else if (_children.second) { + numChildren++; + } + qDebug() << "child cells:" << numChildren; if (_depth < MAX_DEPTH) { - int childNum = 0; - for (auto& child : _children) { - qDebug() << "child:" << childNum; - child.second.debugDump(); - childNum++; + if (_children.first) { + qDebug() << "child: 0"; + _children.first->debugDump(); + } + if (_children.second) { + qDebug() << "child: 1"; + _children.second->debugDump(); } } } +std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellChildBounds() { + std::pair toReturn; + int axis = 0; + // find biggest axis + glm::vec3 dimensions = _bounds.getDimensions(); + for (int i = 0; i < 3; i++) { + if (dimensions[i] >= dimensions[(i + 1) % 3] && dimensions[i] >= dimensions[(i + 2) % 3]) { + axis = i; + break; + } + } + + // The new boxes are half the size in the largest dimension + glm::vec3 newDimensions = dimensions; + newDimensions[axis] *= 0.5f; + toReturn.first.setBox(_bounds.getCorner(), newDimensions); + glm::vec3 offset = glm::vec3(0.0f); + offset[axis] = newDimensions[axis]; + toReturn.second.setBox(_bounds.getCorner() + offset, newDimensions); + return toReturn; +} + void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { - const Triangle& triangle = _allTriangles[triangleIndex]; _population++; + // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { + const Triangle& triangle = _allTriangles[triangleIndex]; + auto childBounds = getTriangleOctreeCellChildBounds(); - for (int child = 0; child < MAX_CHILDREN; child++) { - AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); - - + auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { // if the child AABox would contain the triangle... - if (childBounds.contains(triangle)) { + if (childBound.contains(triangle)) { // if the child cell doesn't yet exist, create it... - if (_children.find((AABox::OctreeChild)child) == _children.end()) { - _children.insert( - std::pair - ((AABox::OctreeChild)child, TriangleOctreeCell(_allTriangles, childBounds, _depth + 1))); + if (!child) { + child = std::make_shared(_allTriangles, childBound, _depth + 1); } // insert the triangleIndex in the child cell - _children.at((AABox::OctreeChild)child).insert(triangleIndex); - return; + child->insert(triangleIndex); + return true; } + return false; + }; + if (insertOperator(childBounds.first, _children.first) || insertOperator(childBounds.second, _children.second)) { + return; } } // either we're at max depth, or the triangle doesn't fit in one of our @@ -247,6 +176,62 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { _triangleIndices.push_back(triangleIndex); } +bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, + BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { + if (!_isBalanced) { + balanceOctree(); + } + + float localDistance = distance; + int trianglesTouched = 0; + bool hit = _triangleOctree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + if (hit) { + distance = localDistance; + } + +#if WANT_DEBUGGING + if (precision) { + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + } +#endif + return hit; +} + +// Determine of the given ray (origin/direction) in model space intersects with any triangles +// in the set. If an intersection occurs, the distance and surface normal will be provided. +bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, Triangle& triangle, bool precision, + int& trianglesTouched, bool allowBackface) { + bool intersectedSomething = false; + float bestDistance = FLT_MAX; + Triangle bestTriangle; + + if (precision) { + for (const auto& triangleIndex : _triangleIndices) { + const auto& thisTriangle = _allTriangles[triangleIndex]; + float thisTriangleDistance; + trianglesTouched++; + if (findRayTriangleIntersection(origin, direction, thisTriangle, thisTriangleDistance, allowBackface)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + bestTriangle = thisTriangle; + intersectedSomething = true; + } + } + } + } else { + intersectedSomething = true; + bestDistance = distance; + } + + if (intersectedSomething) { + distance = bestDistance; + triangle = bestTriangle; + } + + return intersectedSomething; +} + bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -257,52 +242,81 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi float bestLocalDistance = FLT_MAX; BoxFace bestLocalFace; Triangle bestLocalTriangle; - glm::vec3 bestLocalNormal; bool intersects = false; - float boxDistance = FLT_MAX; - // if the pick intersects our bounding box, then continue - if (getBounds().findRayIntersection(origin, direction, boxDistance, bestLocalFace, bestLocalNormal)) { - // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) - // then we know that none of our triangles can represent a better intersection and we can return - if (boxDistance > distance) { - return false; - } - - // if we're not yet at the max depth, then check which child the triangle fits in - if (_depth < MAX_DEPTH) { - float bestChildDistance = FLT_MAX; - for (auto& child : _children) { - // check each child, if there's an intersection, it will return some distance that we need - // to compare against the other results, because there might be multiple intersections and - // we will always choose the best (shortest) intersection - float childDistance = bestChildDistance; - BoxFace childFace; - Triangle childTriangle; - if (child.second.findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { - if (childDistance < bestLocalDistance) { - bestLocalDistance = childDistance; - bestChildDistance = childDistance; - bestLocalFace = childFace; - bestLocalTriangle = childTriangle; - intersects = true; - } - } - } - } - // also check our local triangle set - float internalDistance = boxDistance; + // Check our local triangle set first + // The distance passed in here is the distance to our bounding box. If !precision, that distance is used + { + float internalDistance = distance; BoxFace internalFace; Triangle internalTriangle; if (findRayIntersectionInternal(origin, direction, internalDistance, internalFace, internalTriangle, precision, trianglesTouched, allowBackface)) { - if (internalDistance < bestLocalDistance) { - bestLocalDistance = internalDistance; - bestLocalFace = internalFace; - bestLocalTriangle = internalTriangle; - intersects = true; + bestLocalDistance = internalDistance; + bestLocalFace = internalFace; + bestLocalTriangle = internalTriangle; + intersects = true; + } + } + + // if we're not yet at the max depth, then check our children + if (_depth < MAX_DEPTH) { + std::list sortedTriangleCells; + auto sortingOperator = [&](std::shared_ptr& child) { + if (child) { + float priority = FLT_MAX; + if (child->getBounds().contains(origin)) { + priority = 0.0f; + } else { + float childBoundDistance = FLT_MAX; + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + if (child->getBounds().findRayIntersection(origin, direction, childBoundDistance, childBoundFace, childBoundNormal)) { + // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) + if (childBoundDistance < bestLocalDistance) { + priority = childBoundDistance; + } + } + } + + if (priority < FLT_MAX) { + if (sortedTriangleCells.size() > 0 && priority < sortedTriangleCells.front().first) { + sortedTriangleCells.emplace_front(priority, child); + } else { + sortedTriangleCells.emplace_back(priority, child); + } + } + } + }; + sortingOperator(_children.first); + sortingOperator(_children.second); + + for (auto it = sortedTriangleCells.begin(); it != sortedTriangleCells.end(); ++it) { + const SortedTriangleCell& sortedTriangleCell = *it; + float childDistance = sortedTriangleCell.first; + // We can exit once childDistance > bestLocalDistance + if (childDistance > bestLocalDistance) { + break; + } + // If we're inside the child cell and !precision, we need the actual distance to the cell bounds + if (!precision && childDistance < EPSILON) { + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, childDistance, childBoundFace, childBoundNormal); + } + BoxFace childFace; + Triangle childTriangle; + if (sortedTriangleCell.second->findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalTriangle = childTriangle; + intersects = true; + break; + } } } } + if (intersects) { distance = bestLocalDistance; face = bestLocalFace; @@ -311,6 +325,61 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi return intersects; } +bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, + float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { + if (!_isBalanced) { + balanceOctree(); + } + + float localDistance = parabolicDistance; + int trianglesTouched = 0; + bool hit = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + if (hit) { + parabolicDistance = localDistance; + } + +#if WANT_DEBUGGING + if (precision) { + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + } +#endif + return hit; +} + +bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, + const glm::vec3& acceleration, float& parabolicDistance, + BoxFace& face, Triangle& triangle, bool precision, + int& trianglesTouched, bool allowBackface) { + bool intersectedSomething = false; + float bestDistance = FLT_MAX; + Triangle bestTriangle; + + if (precision) { + for (const auto& triangleIndex : _triangleIndices) { + const auto& thisTriangle = _allTriangles[triangleIndex]; + float thisTriangleDistance; + trianglesTouched++; + if (findParabolaTriangleIntersection(origin, velocity, acceleration, thisTriangle, thisTriangleDistance, allowBackface)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + bestTriangle = thisTriangle; + intersectedSomething = true; + } + } + } + } else { + intersectedSomething = true; + bestDistance = parabolicDistance; + } + + if (intersectedSomething) { + parabolicDistance = bestDistance; + triangle = bestTriangle; + } + + return intersectedSomething; +} + bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, @@ -322,52 +391,81 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& float bestLocalDistance = FLT_MAX; BoxFace bestLocalFace; Triangle bestLocalTriangle; - glm::vec3 bestLocalNormal; bool intersects = false; - float boxDistance = FLT_MAX; - // if the pick intersects our bounding box, then continue - if (getBounds().findParabolaIntersection(origin, velocity, acceleration, boxDistance, bestLocalFace, bestLocalNormal)) { - // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) - // then we know that none of our triangles can represent a better intersection and we can return - if (boxDistance > parabolicDistance) { - return false; - } - - // if we're not yet at the max depth, then check which child the triangle fits in - if (_depth < MAX_DEPTH) { - float bestChildDistance = FLT_MAX; - for (auto& child : _children) { - // check each child, if there's an intersection, it will return some distance that we need - // to compare against the other results, because there might be multiple intersections and - // we will always choose the best (shortest) intersection - float childDistance = bestChildDistance; - BoxFace childFace; - Triangle childTriangle; - if (child.second.findParabolaIntersection(origin, velocity, acceleration, childDistance, childFace, childTriangle, precision, trianglesTouched)) { - if (childDistance < bestLocalDistance) { - bestLocalDistance = childDistance; - bestChildDistance = childDistance; - bestLocalFace = childFace; - bestLocalTriangle = childTriangle; - intersects = true; - } - } - } - } - // also check our local triangle set - float internalDistance = boxDistance; + // Check our local triangle set first + // The distance passed in here is the distance to our bounding box. If !precision, that distance is used + { + float internalDistance = parabolicDistance; BoxFace internalFace; Triangle internalTriangle; if (findParabolaIntersectionInternal(origin, velocity, acceleration, internalDistance, internalFace, internalTriangle, precision, trianglesTouched, allowBackface)) { - if (internalDistance < bestLocalDistance) { - bestLocalDistance = internalDistance; - bestLocalFace = internalFace; - bestLocalTriangle = internalTriangle; - intersects = true; + bestLocalDistance = internalDistance; + bestLocalFace = internalFace; + bestLocalTriangle = internalTriangle; + intersects = true; + } + } + + // if we're not yet at the max depth, then check our children + if (_depth < MAX_DEPTH) { + std::list sortedTriangleCells; + auto sortingOperator = [&](std::shared_ptr& child) { + if (child) { + float priority = FLT_MAX; + if (child->getBounds().contains(origin)) { + priority = 0.0f; + } else { + float childBoundDistance = FLT_MAX; + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + if (child->getBounds().findParabolaIntersection(origin, velocity, acceleration, childBoundDistance, childBoundFace, childBoundNormal)) { + // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) + if (childBoundDistance < bestLocalDistance) { + priority = childBoundDistance; + } + } + } + + if (priority < FLT_MAX) { + if (sortedTriangleCells.size() > 0 && priority < sortedTriangleCells.front().first) { + sortedTriangleCells.emplace_front(priority, child); + } else { + sortedTriangleCells.emplace_back(priority, child); + } + } + } + }; + sortingOperator(_children.first); + sortingOperator(_children.second); + + for (auto it = sortedTriangleCells.begin(); it != sortedTriangleCells.end(); ++it) { + const SortedTriangleCell& sortedTriangleCell = *it; + float childDistance = sortedTriangleCell.first; + // We can exit once childDistance > bestLocalDistance + if (childDistance > bestLocalDistance) { + break; + } + // If we're inside the child cell and !precision, we need the actual distance to the cell bounds + if (!precision && childDistance < EPSILON) { + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + sortedTriangleCell.second->getBounds().findParabolaIntersection(origin, velocity, acceleration, childDistance, childBoundFace, childBoundNormal); + } + BoxFace childFace; + Triangle childTriangle; + if (sortedTriangleCell.second->findParabolaIntersection(origin, velocity, acceleration, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalTriangle = childTriangle; + intersects = true; + break; + } } } } + if (intersects) { parabolicDistance = bestLocalDistance; face = bestLocalFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 0b0d0a9ac5..a838e2d9e2 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include "AABox.h" #include "GeometryUtil.h" @@ -20,9 +21,8 @@ class TriangleSet { class TriangleOctreeCell { public: - TriangleOctreeCell(std::vector& allTriangles) : - _allTriangles(allTriangles) - { } + TriangleOctreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} + TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); void insert(size_t triangleIndex); void reset(const AABox& bounds, int depth = 0); @@ -40,8 +40,6 @@ class TriangleSet { void debugDump(); protected: - TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); - // checks our internal list of triangles bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, @@ -50,20 +48,22 @@ class TriangleSet { float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); + std::pair getTriangleOctreeCellChildBounds(); + std::vector& _allTriangles; - std::map _children; - int _depth{ 0 }; - int _population{ 0 }; + std::pair, std::shared_ptr> _children; + int _depth { 0 }; + int _population { 0 }; AABox _bounds; std::vector _triangleIndices; friend class TriangleSet; }; + using SortedTriangleCell = std::pair>; + public: - TriangleSet() : - _triangleOctree(_triangles) - {} + TriangleSet() : _triangleOctree(_triangles) {} void debugDump(); @@ -87,8 +87,7 @@ public: const AABox& getBounds() const { return _bounds; } protected: - - bool _isBalanced{ false }; + bool _isBalanced { false }; std::vector _triangles; TriangleOctreeCell _triangleOctree; AABox _bounds; From 874d9251e76996f1371c4d755db598bb66972906 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 17:32:46 -0700 Subject: [PATCH 026/106] demo version, next to implement in c++ --- scripts/developer/rotateApp.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index 4d18910335..60d78d66d9 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -260,6 +260,15 @@ function limitAngle(angle) { } function computeHandAzimuths(timeElapsed) { + + // var leftHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.lhandPose.translation); + // var rightHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.rhandPose.translation); + + + // var hipToLeftHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: leftHandPositionRigSpace.x, y: 0, z: leftHandPositionRigSpace.z }); + // var hipToRightHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: rightHandPositionRigSpace.x, y: 0, z: rightHandPositionRigSpace.z }); + // var hipToHandHalfway = Quat.slerp(hipToLeftHand, hipToRightHand, 0.5); + var leftHand = currentStateReadings.lhandPose.translation; var rightHand = currentStateReadings.rhandPose.translation; var head = currentStateReadings.headPose.translation; @@ -299,17 +308,21 @@ function computeHandAzimuths(timeElapsed) { // get it into radians too!! // average head and hands 50/50 - print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y) + // print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y); var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { - print("recenter the feet under the head"); + //print("recenter the feet under the head"); MyAvatar.triggerRotationRecenter(); hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; } - return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); + // put a hard max on this easing function. + var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0)/3.0) * leftRightMidpoint; + print("rotate angle " + rotateAngle); + + return Quat.fromVec3Degrees({ x: 0, y: rotateAngle, z: 0 }); } From c474f38860afef8694d4736016c549dbd6bb3d9e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 22 Aug 2018 17:49:02 -0700 Subject: [PATCH 027/106] force coarse picking, sort avatars --- interface/src/Menu.cpp | 6 + interface/src/Menu.h | 1 + interface/src/avatar/AvatarManager.cpp | 174 +++++++++++++++---------- interface/src/avatar/AvatarManager.h | 2 + interface/src/raypick/ParabolaPick.cpp | 7 +- interface/src/raypick/RayPick.cpp | 7 +- libraries/avatars/src/AvatarData.h | 2 +- libraries/pointers/src/Pick.cpp | 4 + libraries/pointers/src/PickManager.h | 11 +- libraries/shared/src/TriangleSet.cpp | 54 ++++---- libraries/shared/src/TriangleSet.h | 18 +-- 11 files changed, 178 insertions(+), 108 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index fe7083ea30..d4c74127c8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -46,6 +46,7 @@ #include "InterfaceLogging.h" #include "LocationBookmarks.h" #include "DeferredLightingEffect.h" +#include "PickManager.h" #include "AmbientOcclusionEffect.h" #include "RenderShadowTask.h" @@ -688,6 +689,11 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraints, 0, false, qApp, SLOT(setShowBulletConstraints(bool))); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraintLimits, 0, false, qApp, SLOT(setShowBulletConstraintLimits(bool))); + // Developer > Picking >>> + MenuWrapper* pickingOptionsMenu = developerMenu->addMenu("Picking"); + addCheckableActionToQMenuAndActionHash(pickingOptionsMenu, MenuOption::ForceCoarsePicking, 0, false, + DependencyManager::get().data(), SLOT(setForceCoarsePicking(bool))); + // Developer > Display Crash Options addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true); // Developer > Crash >>> diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c489a3f0b0..4386bec5ae 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -221,6 +221,7 @@ namespace MenuOption { const QString NotificationSounds = "play_notification_sounds"; const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot"; const QString NotificationSoundsTablet = "play_notification_sounds_tablet"; + const QString ForceCoarsePicking = "Force Coarse Picking"; } #endif // hifi_Menu_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 139f44d58b..6fb35ec1da 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -553,6 +553,14 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic return result; } + // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to + // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code + // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking + // against the avatar is sort-of right, but you likely wont be able to pick against the arms. + + // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. + + std::vector sortedAvatars; auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -561,47 +569,60 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic continue; } - float distance; - BoxFace face; - glm::vec3 surfaceNormal; - - SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); - - // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to - // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code - // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking - // against the avatar is sort-of right, but you likely wont be able to pick against the arms. - - // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. - + float distance = FLT_MAX; +#if 0 // if we weren't picking against the capsule, we would want to pick against the avatarBounds... - // AABox avatarBounds = avatarModel->getRenderableMeshBound(); - // if (!avatarBounds.findRayIntersection(ray.origin, normDirection, distance, face, surfaceNormal)) { - // // ray doesn't intersect avatar's bounding-box - // continue; - // } - + SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); + AABox avatarBounds = avatarModel->getRenderableMeshBound(); + if (avatarBounds.contains(ray.origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (avatarBounds.findRayIntersection(ray.origin, ray.direction, boundDistance, face, surfaceNormal)) { + distance = boundDistance; + } + } +#else glm::vec3 start; glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); - if (!intersects) { - // ray doesn't intersect avatar's capsule - continue; + findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); +#endif + + if (distance < FLT_MAX) { + sortedAvatars.emplace_back(distance, avatar); + } + } + + if (sortedAvatars.size() > 1) { + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); + } + + for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) { + const SortedAvatar& sortedAvatar = *it; + // We can exit once avatarCapsuleDistance > bestDistance + if (sortedAvatar.first > result.distance) { + break; } + float distance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; QVariantMap extraInfo; - intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, - distance, face, surfaceNormal, extraInfo, true); - - if (intersects && (!result.intersects || distance < result.distance)) { - result.intersects = true; - result.avatarID = avatar->getID(); - result.distance = distance; - result.face = face; - result.surfaceNormal = surfaceNormal; - result.extraInfo = extraInfo; + SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel(); + if (avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true)) { + if (distance < result.distance) { + result.intersects = true; + result.avatarID = sortedAvatar.second->getID(); + result.distance = distance; + result.face = face; + result.surfaceNormal = surfaceNormal; + result.extraInfo = extraInfo; + } } } @@ -625,6 +646,14 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector return result; } + // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to + // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code + // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking + // against the avatar is sort-of right, but you likely wont be able to pick against the arms. + + // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. + + std::vector sortedAvatars; auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -633,47 +662,60 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector continue; } - float parabolicDistance; - BoxFace face; - glm::vec3 surfaceNormal; - - SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); - - // It's better to intersect the parabola against the avatar's actual mesh, but this is currently difficult to - // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code - // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking - // against the avatar is sort-of right, but you likely wont be able to pick against the arms. - - // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. - + float distance = FLT_MAX; +#if 0 // if we weren't picking against the capsule, we would want to pick against the avatarBounds... - // AABox avatarBounds = avatarModel->getRenderableMeshBound(); - // if (!avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal)) { - // // parabola doesn't intersect avatar's bounding-box - // continue; - // } - + SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); + AABox avatarBounds = avatarModel->getRenderableMeshBound(); + if (avatarBounds.contains(pick.origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, boundDistance, face, surfaceNormal)) { + distance = boundDistance; + } + } +#else glm::vec3 start; glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), parabolicDistance); - if (!intersects) { - // ray doesn't intersect avatar's capsule - continue; + findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), distance); +#endif + + if (distance < FLT_MAX) { + sortedAvatars.emplace_back(distance, avatar); + } + } + + if (sortedAvatars.size() > 1) { + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); + } + + for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) { + const SortedAvatar& sortedAvatar = *it; + // We can exit once avatarCapsuleDistance > bestDistance + if (sortedAvatar.first > result.parabolicDistance) { + break; } + float parabolicDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; QVariantMap extraInfo; - intersects = avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, - parabolicDistance, face, surfaceNormal, extraInfo, true); - - if (intersects && (!result.intersects || parabolicDistance < result.parabolicDistance)) { - result.intersects = true; - result.avatarID = avatar->getID(); - result.parabolicDistance = parabolicDistance; - result.face = face; - result.surfaceNormal = surfaceNormal; - result.extraInfo = extraInfo; + SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel(); + if (avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal, extraInfo, true)) { + if (parabolicDistance < result.parabolicDistance) { + result.intersects = true; + result.avatarID = sortedAvatar.second->getID(); + result.parabolicDistance = parabolicDistance; + result.face = face; + result.surfaceNormal = surfaceNormal; + result.extraInfo = extraInfo; + } } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ecf9a2d735..a9c04e8473 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -27,6 +27,8 @@ #include "AvatarMotionState.h" #include "MyAvatar.h" +using SortedAvatar = std::pair>; + /**jsdoc * The AvatarManager API has properties and methods which manage Avatars within the same domain. * diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index b3e3f16345..f4db1df25a 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -13,11 +13,13 @@ #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" +#include "PickManager.h" PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToEntityIntersectionResult entityRes = - DependencyManager::get()->findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), + DependencyManager::get()->findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); @@ -28,8 +30,9 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), + qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 96b41dcc72..fde1da3f87 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -13,10 +13,12 @@ #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" +#include "PickManager.h" PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToEntityIntersectionResult entityRes = - DependencyManager::get()->findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), + DependencyManager::get()->findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); @@ -26,8 +28,9 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { } PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), + qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 53a2a69119..9d4ba3902d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1559,7 +1559,7 @@ class RayToAvatarIntersectionResult { public: bool intersects { false }; QUuid avatarID; - float distance { 0.0f }; + float distance { FLT_MAX }; BoxFace face; glm::vec3 intersection; glm::vec3 surfaceNormal; diff --git a/libraries/pointers/src/Pick.cpp b/libraries/pointers/src/Pick.cpp index 4315409bdb..7ac53a2643 100644 --- a/libraries/pointers/src/Pick.cpp +++ b/libraries/pointers/src/Pick.cpp @@ -72,11 +72,15 @@ PickResultPointer PickQuery::getPrevPickResult() const { void PickQuery::setIgnoreItems(const QVector& ignoreItems) { withWriteLock([&] { _ignoreItems = ignoreItems; + // We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same ignoreItems in a different order + std::sort(_ignoreItems.begin(), _ignoreItems.end(), std::less()); }); } void PickQuery::setIncludeItems(const QVector& includeItems) { withWriteLock([&] { _includeItems = includeItems; + // We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same includeItems in a different order + std::sort(_includeItems.begin(), _includeItems.end(), std::less()); }); } \ No newline at end of file diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 242550d837..595c43e71d 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -16,7 +16,10 @@ #include -class PickManager : public Dependency, protected ReadWriteLockable { +#include + +class PickManager : public QObject, public Dependency, protected ReadWriteLockable { + Q_OBJECT SINGLETON_DEPENDENCY public: @@ -53,7 +56,13 @@ public: unsigned int getPerFrameTimeBudget() const { return _perFrameTimeBudget; } void setPerFrameTimeBudget(unsigned int numUsecs) { _perFrameTimeBudget = numUsecs; } + bool getForceCoarsePicking() { return _forceCoarsePicking; } + +public slots: + void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } + protected: + bool _forceCoarsePicking { false }; std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index f3c0ef5776..1dc8c5657a 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -29,7 +29,7 @@ void TriangleSet::clear() { _bounds.clear(); _isBalanced = false; - _triangleOctree.clear(); + _triangleTree.clear(); } bool TriangleSet::convexHullContains(const glm::vec3& point) const { @@ -53,16 +53,16 @@ void TriangleSet::debugDump() { qDebug() << __FUNCTION__; qDebug() << "bounds:" << getBounds(); qDebug() << "triangles:" << size() << "at top level...."; - qDebug() << "----- _triangleOctree -----"; - _triangleOctree.debugDump(); + qDebug() << "----- _triangleTree -----"; + _triangleTree.debugDump(); } -void TriangleSet::balanceOctree() { - _triangleOctree.reset(_bounds); +void TriangleSet::balanceTree() { + _triangleTree.reset(_bounds); // insert all the triangles for (size_t i = 0; i < _triangles.size(); i++) { - _triangleOctree.insert(i); + _triangleTree.insert(i); } _isBalanced = true; @@ -77,13 +77,13 @@ void TriangleSet::balanceOctree() { // With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves static const int MAX_DEPTH = 12; -TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : +TriangleSet::TriangleTreeCell::TriangleTreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : _allTriangles(allTriangles) { reset(bounds, depth); } -void TriangleSet::TriangleOctreeCell::clear() { +void TriangleSet::TriangleTreeCell::clear() { _population = 0; _triangleIndices.clear(); _bounds.clear(); @@ -91,13 +91,13 @@ void TriangleSet::TriangleOctreeCell::clear() { _children.second.reset(); } -void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { +void TriangleSet::TriangleTreeCell::reset(const AABox& bounds, int depth) { clear(); _bounds = bounds; _depth = depth; } -void TriangleSet::TriangleOctreeCell::debugDump() { +void TriangleSet::TriangleTreeCell::debugDump() { qDebug() << __FUNCTION__; qDebug() << " bounds:" << getBounds(); qDebug() << " depth:" << _depth; @@ -123,7 +123,7 @@ void TriangleSet::TriangleOctreeCell::debugDump() { } } -std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellChildBounds() { +std::pair TriangleSet::TriangleTreeCell::getTriangleTreeCellChildBounds() { std::pair toReturn; int axis = 0; // find biggest axis @@ -145,20 +145,20 @@ std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellCh return toReturn; } -void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { +void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) { _population++; // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { const Triangle& triangle = _allTriangles[triangleIndex]; - auto childBounds = getTriangleOctreeCellChildBounds(); + auto childBounds = getTriangleTreeCellChildBounds(); - auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { + auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { // if the child AABox would contain the triangle... if (childBound.contains(triangle)) { // if the child cell doesn't yet exist, create it... if (!child) { - child = std::make_shared(_allTriangles, childBound, _depth + 1); + child = std::make_shared(_allTriangles, childBound, _depth + 1); } // insert the triangleIndex in the child cell @@ -179,19 +179,19 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { - balanceOctree(); + balanceTree(); } float localDistance = distance; int trianglesTouched = 0; - bool hit = _triangleOctree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { distance = localDistance; } #if WANT_DEBUGGING if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size(); } #endif return hit; @@ -199,7 +199,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& // Determine of the given ray (origin/direction) in model space intersects with any triangles // in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, +bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { bool intersectedSomething = false; @@ -232,7 +232,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec return intersectedSomething; } -bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { if (_population < 1) { @@ -261,7 +261,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi // if we're not yet at the max depth, then check our children if (_depth < MAX_DEPTH) { std::list sortedTriangleCells; - auto sortingOperator = [&](std::shared_ptr& child) { + auto sortingOperator = [&](std::shared_ptr& child) { if (child) { float priority = FLT_MAX; if (child->getBounds().contains(origin)) { @@ -328,25 +328,25 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { - balanceOctree(); + balanceTree(); } float localDistance = parabolicDistance; int trianglesTouched = 0; - bool hit = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { parabolicDistance = localDistance; } #if WANT_DEBUGGING if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size(); } #endif return hit; } -bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, +bool TriangleSet::TriangleTreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -380,7 +380,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm return intersectedSomething; } -bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, +bool TriangleSet::TriangleTreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -410,7 +410,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& // if we're not yet at the max depth, then check our children if (_depth < MAX_DEPTH) { std::list sortedTriangleCells; - auto sortingOperator = [&](std::shared_ptr& child) { + auto sortingOperator = [&](std::shared_ptr& child) { if (child) { float priority = FLT_MAX; if (child->getBounds().contains(origin)) { diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index a838e2d9e2..84e362f0d0 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -19,10 +19,10 @@ class TriangleSet { - class TriangleOctreeCell { + class TriangleTreeCell { public: - TriangleOctreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} - TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); + TriangleTreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} + TriangleTreeCell(std::vector& allTriangles, const AABox& bounds, int depth); void insert(size_t triangleIndex); void reset(const AABox& bounds, int depth = 0); @@ -48,10 +48,10 @@ class TriangleSet { float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); - std::pair getTriangleOctreeCellChildBounds(); + std::pair getTriangleTreeCellChildBounds(); std::vector& _allTriangles; - std::pair, std::shared_ptr> _children; + std::pair, std::shared_ptr> _children; int _depth { 0 }; int _population { 0 }; AABox _bounds; @@ -60,10 +60,10 @@ class TriangleSet { friend class TriangleSet; }; - using SortedTriangleCell = std::pair>; + using SortedTriangleCell = std::pair>; public: - TriangleSet() : _triangleOctree(_triangles) {} + TriangleSet() : _triangleTree(_triangles) {} void debugDump(); @@ -74,7 +74,7 @@ public: bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); - void balanceOctree(); + void balanceTree(); void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles size_t size() const { return _triangles.size(); } @@ -89,6 +89,6 @@ public: protected: bool _isBalanced { false }; std::vector _triangles; - TriangleOctreeCell _triangleOctree; + TriangleTreeCell _triangleTree; AABox _bounds; }; From e3ad5adcaec07108360e340302cd643c61e4af31 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 06:49:35 -0700 Subject: [PATCH 028/106] first approach vertical alignment --- libraries/avatars/src/AvatarData.cpp | 2 +- libraries/avatars/src/AvatarData.h | 4 ++ libraries/avatars/src/AvatarHashMap.cpp | 72 +++++++++++++++++++++++-- libraries/avatars/src/AvatarHashMap.h | 2 + 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c1ec19b307..63a905aa6e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -918,7 +918,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]); + auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + glm::vec3(0, 2 * _surrogateIndex, 0); if (_globalPosition != newValue) { _globalPosition = newValue; _globalPositionChanged = usecTimestampNow(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 53a2a69119..b3443dbd8a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1186,6 +1186,9 @@ public: virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {} virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {} + void setSurrogateIndex(int surrogateIndex) { _surrogateIndex = surrogateIndex; } + int getSurrogateIndex() { return _surrogateIndex; } + signals: /**jsdoc @@ -1443,6 +1446,7 @@ protected: udt::SequenceNumber _identitySequenceNumber { 0 }; bool _hasProcessedFirstIdentity { false }; float _density; + int _surrogateIndex{ 0 }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 64b26131be..a84d83e663 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -21,6 +21,8 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" +const int SURROGATE_COUNT = 2; + AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); @@ -139,14 +141,37 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointerisIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); + + if (isNewAvatar) { QWriteLocker locker(&_hashLock); _pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar }); + std::vector surrogateIDs; + for (int i = 0; i < SURROGATE_COUNT; i++) { + QUuid surrogateID = QUuid::createUuid(); + surrogateIDs.push_back(surrogateID); + auto surrogateAvatar = addAvatar(surrogateID, sendingNode); + surrogateAvatar->setSurrogateIndex(i + 1); + surrogateAvatar->parseDataFromBuffer(byteArray); + _pendingAvatars.insert(surrogateID, { std::chrono::steady_clock::now(), 0, surrogateAvatar }); + } + _surrogates.insert(std::pair>(sessionUUID, surrogateIDs)); + } else { + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); + if (!isNewAvatar) { + surrogateAvatar->parseDataFromBuffer(byteArray); + } + } + } // have the matching (or new) avatar parse the data from the packet int bytesRead = avatar->parseDataFromBuffer(byteArray); message->seek(positionBeforeRead + bytesRead); + + avatar->parseDataFromBuffer(byteArray); return avatar; } else { // create a dummy AvatarData class to throw this data on the ground @@ -191,13 +216,22 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer bool displayNameChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + auto surrogateIDs = _surrogates[identityUUID]; + for (auto id : surrogateIDs) { + auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); + if (!isNewAvatar) { + surrogateAvatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + } + } } } -void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { +void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { + message->seek(0); while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + avatarID = sessionUUID; // grab the avatar so we can ask it to process trait data bool isNewAvatar; @@ -225,10 +259,12 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (packetTraitVersion > lastProcessedVersions[traitType]) { avatar->processTrait(traitType, message->read(traitBinarySize)); lastProcessedVersions[traitType] = packetTraitVersion; - } else { + } + else { skipBinaryTrait = true; } - } else { + } + else { AvatarTraits::TraitInstanceID traitInstanceID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -238,11 +274,13 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (packetTraitVersion > processedInstanceVersion) { if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } else { + } + else { avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); } processedInstanceVersion = packetTraitVersion; - } else { + } + else { skipBinaryTrait = true; } } @@ -258,6 +296,18 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess } } +void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + processAvatarTraits(avatarID, message, sendingNode); + auto surrogateIDs = _surrogates[avatarID]; + for (auto id : surrogateIDs) { + processAvatarTraits(id, message, sendingNode); + } + } +} + void AvatarHashMap::processKillAvatar(QSharedPointer message, SharedNodePointer sendingNode) { // read the node id QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -265,6 +315,10 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S KillAvatarReason reason; message->readPrimitive(&reason); removeAvatar(sessionUUID, reason); + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + removeAvatar(id, reason); + } } void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { @@ -276,6 +330,14 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo if (removedAvatar) { handleRemovedAvatar(removedAvatar, removalReason); } + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + _pendingAvatars.remove(id); + auto removedSurrogate = _avatarHash.take(id); + if (removedSurrogate) { + handleRemovedAvatar(removedSurrogate, removalReason); + } + } } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index ba16fa9568..41981df693 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -134,6 +134,7 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); + void processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc @@ -167,6 +168,7 @@ protected: mutable QReadWriteLock _hashLock; std::unordered_map _processedTraitVersions; + std::map> _surrogates; private: QUuid _lastOwnerSessionUUID; }; From 30ef544c6debac94cf1df5a0f5ee714c029143a9 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 23 Aug 2018 15:57:14 +0100 Subject: [PATCH 029/106] starting the c++ version of shoulder rotation --- interface/src/avatar/MyAvatar.cpp | 11 +++++++++++ interface/src/avatar/MyAvatar.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 44ee5e30c6..6211e6f0d8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -801,6 +801,17 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } } +void MyAvatar::computeHandAzimuth() { + controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); + controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); + glm::vec3 localLookat(0.0f, 0.0f, 1.0f); + glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.rotation * localLookat; + glm::vec3 lefttHandRigSpace = leftHandPoseAvatarSpace.rotation * localLookat; + + _hipToHandController = glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z); + +} + void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache) { assert(QThread::currentThread() == thread()); auto userInputMapper = DependencyManager::get(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 10b5884d5a..6b7c4e83a1 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -320,6 +320,9 @@ public: // as it moves through the world. void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); + // compute the hip to hand average azimuth. + void computeHandAzimuth(); + // read the location of a hand controller and save the transform void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache); @@ -1658,6 +1661,8 @@ private: glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; + glm::vec2 _hipToHandController; // facing vector in xz plane (sensor space) + float _currentStandingHeight { 0.0f }; bool _resetMode { true }; RingBufferHistory _recentModeReadings; From 3d959ca7ee6c59496f30dc15f6da02f666376166 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 15:52:42 -0700 Subject: [PATCH 030/106] Refactoring and cleaning --- .../src/avatars-renderer/Avatar.h | 1 - libraries/avatars/src/AvatarData.cpp | 21 +- libraries/avatars/src/AvatarData.h | 8 +- libraries/avatars/src/AvatarHashMap.cpp | 223 ++++++++++++++---- libraries/avatars/src/AvatarHashMap.h | 23 +- 5 files changed, 223 insertions(+), 53 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c6e8ac59f1..63aa6d9b6f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -464,7 +464,6 @@ protected: glm::vec3 _lastAngularVelocity; glm::vec3 _angularAcceleration; glm::quat _lastOrientation; - glm::vec3 _worldUpDirection { Vectors::UP }; bool _moving { false }; ///< set when position is changing diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 63a905aa6e..ddb53f8acb 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -918,7 +918,26 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + glm::vec3(0, 2 * _surrogateIndex, 0); + + const float SPACE_BETWEEN_AVATARS = 2.0f; + const int RANDOM_RADIUS = 100; + const int AVATARS_PER_ROW = 3; + + glm::vec3 offset; + + if (false) { + qsrand(static_cast(getID().toByteArray().toInt())); + float xrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); + float yrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); + offset = glm::vec3(xrand, 0.0f, yrand); + } + else { + int row = _replicaIndex % AVATARS_PER_ROW; + int col = floor(_replicaIndex / AVATARS_PER_ROW); + offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); + } + + auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; if (_globalPosition != newValue) { _globalPosition = newValue; _globalPositionChanged = usecTimestampNow(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b3443dbd8a..36cc0f936d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -337,6 +337,7 @@ enum KillAvatarReason : uint8_t { TheirAvatarEnteredYourBubble, YourAvatarEnteredTheirBubble }; + Q_DECLARE_METATYPE(KillAvatarReason); class QDataStream; @@ -1185,9 +1186,8 @@ public: virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {} virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {} - - void setSurrogateIndex(int surrogateIndex) { _surrogateIndex = surrogateIndex; } - int getSurrogateIndex() { return _surrogateIndex; } + void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; } + int getReplicaIndex() { return _replicaIndex; } signals: @@ -1446,7 +1446,7 @@ protected: udt::SequenceNumber _identitySequenceNumber { 0 }; bool _hasProcessedFirstIdentity { false }; float _density; - int _surrogateIndex{ 0 }; + int _replicaIndex { 0 }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index a84d83e663..800825f574 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -21,7 +21,80 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" -const int SURROGATE_COUNT = 2; + +void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) { + if (_replicasMap.find(parentID) == _replicasMap.end()) { + std::vector emptyReplicas = std::vector(); + _replicasMap.insert(std::pair>(parentID, emptyReplicas)); + } + auto &replicas = _replicasMap[parentID]; + replica->setReplicaIndex((int)replicas.size() + 1); + replicas.push_back(replica); +} + +std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID, int count) { + std::vector ids; + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (int i = 0; i < replicas.size(); i++) { + ids.push_back(replicas[i]->getID()); + } + } else if (count > 0) { + for (int i = 0; i < count; i++) { + ids.push_back(QUuid::createUuid()); + } + } + return ids; +} + +void AvatarReplicas::parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->parseDataFromBuffer(buffer); + } + } +} + +void AvatarReplicas::removeReplicas(const QUuid& parentID) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + _replicasMap.erase(parentID); + } +} + +void AvatarReplicas::processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processAvatarIdentity(identityData, identityChanged, displayNameChanged); + } + } +} +void AvatarReplicas::processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processTrait(traitType, traitBinaryData); + } + } +} +void AvatarReplicas::processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processDeletedTraitInstance(traitType, instanceID); + } + } +} +void AvatarReplicas::processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processTraitInstance(traitType, instanceID, traitBinaryData); + } + } +} AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); @@ -138,40 +211,28 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer(); + const int REPLICAS_COUNT = 8; + bool isNewAvatar; if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); - - + if (isNewAvatar) { QWriteLocker locker(&_hashLock); _pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar }); - std::vector surrogateIDs; - for (int i = 0; i < SURROGATE_COUNT; i++) { - QUuid surrogateID = QUuid::createUuid(); - surrogateIDs.push_back(surrogateID); - auto surrogateAvatar = addAvatar(surrogateID, sendingNode); - surrogateAvatar->setSurrogateIndex(i + 1); - surrogateAvatar->parseDataFromBuffer(byteArray); - _pendingAvatars.insert(surrogateID, { std::chrono::steady_clock::now(), 0, surrogateAvatar }); + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID, REPLICAS_COUNT); + for (auto replicaID : replicaIDs) { + auto replicaAvatar = addAvatar(replicaID, sendingNode); + _replicas.addReplica(sessionUUID, replicaAvatar); + _pendingAvatars.insert(replicaID, { std::chrono::steady_clock::now(), 0, replicaAvatar }); } - _surrogates.insert(std::pair>(sessionUUID, surrogateIDs)); - } else { - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { - auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); - if (!isNewAvatar) { - surrogateAvatar->parseDataFromBuffer(byteArray); - } - } - - } + } // have the matching (or new) avatar parse the data from the packet int bytesRead = avatar->parseDataFromBuffer(byteArray); message->seek(positionBeforeRead + bytesRead); + _replicas.parseDataFromBuffer(sessionUUID, byteArray); - avatar->parseDataFromBuffer(byteArray); return avatar; } else { // create a dummy AvatarData class to throw this data on the ground @@ -216,17 +277,11 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer bool displayNameChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); - auto surrogateIDs = _surrogates[identityUUID]; - for (auto id : surrogateIDs) { - auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); - if (!isNewAvatar) { - surrogateAvatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); - } - } + _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); } } -void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { +void AvatarHashMap::processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { message->seek(0); while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for @@ -297,13 +352,88 @@ void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { + /* while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - processAvatarTraits(avatarID, message, sendingNode); - auto surrogateIDs = _surrogates[avatarID]; - for (auto id : surrogateIDs) { - processAvatarTraits(id, message, sendingNode); + processBulkAvatarTraitsForID(avatarID, message, sendingNode); + auto replicaIDs = _replicas.getReplicaIDs(avatarID); + for (auto id : replicaIDs) { + processBulkAvatarTraitsForID(id, message, sendingNode); + } + } + */ + int position = 0; + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + // grab the avatar so we can ask it to process trait data + bool isNewAvatar; + auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); + + // read the first trait type for this avatar + AvatarTraits::TraitType traitType; + message->readPrimitive(&traitType); + + // grab the last trait versions for this avatar + auto& lastProcessedVersions = _processedTraitVersions[avatarID]; + + while (traitType != AvatarTraits::NullTrait) { + AvatarTraits::TraitVersion packetTraitVersion; + message->readPrimitive(&packetTraitVersion); + + AvatarTraits::TraitWireSize traitBinarySize; + bool skipBinaryTrait = false; + + + if (AvatarTraits::isSimpleTrait(traitType)) { + message->readPrimitive(&traitBinarySize); + + // check if this trait version is newer than what we already have for this avatar + if (packetTraitVersion > lastProcessedVersions[traitType]) { + position = message->getPosition(); + avatar->processTrait(traitType, message->read(traitBinarySize)); + message->seek(position); + _replicas.processTrait(avatarID, traitType, message->read(traitBinarySize)); + lastProcessedVersions[traitType] = packetTraitVersion; + } + else { + skipBinaryTrait = true; + } + } + else { + AvatarTraits::TraitInstanceID traitInstanceID = + QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + message->readPrimitive(&traitBinarySize); + + auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); + if (packetTraitVersion > processedInstanceVersion) { + if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { + avatar->processDeletedTraitInstance(traitType, traitInstanceID); + _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); + } + else { + position = message->getPosition(); + avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); + message->seek(position); + _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, message->read(traitBinarySize)); + } + processedInstanceVersion = packetTraitVersion; + } + else { + skipBinaryTrait = true; + } + } + + if (skipBinaryTrait) { + // we didn't read this trait because it was older or because we didn't have an avatar to process it for + message->seek(message->getPosition() + traitBinarySize); + } + + // read the next trait type, which is null if there are no more traits for this avatar + message->readPrimitive(&traitType); } } } @@ -315,8 +445,8 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S KillAvatarReason reason; message->readPrimitive(&reason); removeAvatar(sessionUUID, reason); - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); + for (auto id : replicaIDs) { removeAvatar(id, reason); } } @@ -324,20 +454,23 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { QWriteLocker locker(&_hashLock); + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); + _replicas.removeReplicas(sessionUUID); + for (auto id : replicaIDs) { + _pendingAvatars.remove(id); + auto removedReplica = _avatarHash.take(id); + if (removedReplica) { + handleRemovedAvatar(removedReplica, removalReason); + } + } + _pendingAvatars.remove(sessionUUID); auto removedAvatar = _avatarHash.take(sessionUUID); if (removedAvatar) { handleRemovedAvatar(removedAvatar, removalReason); } - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { - _pendingAvatars.remove(id); - auto removedSurrogate = _avatarHash.take(id); - if (removedSurrogate) { - handleRemovedAvatar(removedSurrogate, removalReason); - } - } + } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 41981df693..2d8d7f907f 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -41,6 +41,24 @@ * @hifi-assignment-client */ +class AvatarReplicas { +public: + AvatarReplicas() {}; + void addReplica(const QUuid& parentID, AvatarSharedPointer replica); + std::vector getReplicaIDs(const QUuid& parentID, int count = 0); + void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); + void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + void removeReplicas(const QUuid& parentID); + void processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData); + void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); + void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + +private: + std::map> _replicasMap; +}; + + class AvatarHashMap : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -134,7 +152,7 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); - void processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); + void processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc @@ -168,7 +186,8 @@ protected: mutable QReadWriteLock _hashLock; std::unordered_map _processedTraitVersions; - std::map> _surrogates; + AvatarReplicas _replicas; + private: QUuid _lastOwnerSessionUUID; }; From 220dbf586f6e6382d8dc8338c48ec9b42e210ce2 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 16:46:58 -0700 Subject: [PATCH 031/106] set the phase to 0.0 when it is negative --- libraries/animation/src/AnimBlendLinearMove.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 6313d4cbe9..27352fd546 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -155,6 +155,7 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn // integrate phase forward in time. _phase += omega * dt; + qCDebug(animation) << "the _phase is " << _phase; // detect loop trigger events if (_phase >= 1.0f) { @@ -172,4 +173,7 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { assert(clipNode); const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; _phase = fmodf(frame / NUM_FRAMES, 1.0f); + if (_phase < 0.0f) { + _phase = 0.0f; // 1.0f + _phase; + } } From 0cc302f68c9812325f19a55158503b5339ccd82b Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:28:38 -0700 Subject: [PATCH 032/106] set phase to 0.0 --- libraries/animation/src/AnimBlendLinearMove.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 27352fd546..d648b9966f 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -139,6 +139,8 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); assert(nextClipNode); + + float v0 = _characteristicSpeeds[prevPoseIndex]; float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float v1 = _characteristicSpeeds[nextPoseIndex]; @@ -153,9 +155,17 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn float f1 = nextClipNode->getStartFrame() + _phase * n1; nextClipNode->setCurrentFrame(f1); + + // integrate phase forward in time. _phase += omega * dt; - qCDebug(animation) << "the _phase is " << _phase; + + qCDebug(animation) << "the _phase is " << _phase << " and omega " << omega << _desiredSpeed; + + if (_phase < 0.0f) { + _phase = 0.0f; // 1.0f + _phase; + } + // detect loop trigger events if (_phase >= 1.0f) { @@ -173,7 +183,4 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { assert(clipNode); const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; _phase = fmodf(frame / NUM_FRAMES, 1.0f); - if (_phase < 0.0f) { - _phase = 0.0f; // 1.0f + _phase; - } } From 884be2f99d4a7ea417260902880e412264ad2d5a Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:30:03 -0700 Subject: [PATCH 033/106] removed comment and whitespace --- libraries/animation/src/AnimBlendLinearMove.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index d648b9966f..8879f3d9d4 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -155,18 +155,13 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn float f1 = nextClipNode->getStartFrame() + _phase * n1; nextClipNode->setCurrentFrame(f1); - - // integrate phase forward in time. _phase += omega * dt; - - qCDebug(animation) << "the _phase is " << _phase << " and omega " << omega << _desiredSpeed; if (_phase < 0.0f) { - _phase = 0.0f; // 1.0f + _phase; + _phase = 0.0f; } - // detect loop trigger events if (_phase >= 1.0f) { triggersOut.setTrigger(_id + "Loop"); From 671bf32d5813ef84837e7d22fd83933d4f9cba5e Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:36:35 -0700 Subject: [PATCH 034/106] removed whitespace --- libraries/animation/src/AnimBlendLinearMove.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 8879f3d9d4..42098eb072 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -139,8 +139,6 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); assert(nextClipNode); - - float v0 = _characteristicSpeeds[prevPoseIndex]; float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float v1 = _characteristicSpeeds[nextPoseIndex]; From b5b71676448c1805321831e6c363076d1a4c0f94 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 23 Aug 2018 17:47:25 -0700 Subject: [PATCH 035/106] add to stats --- interface/resources/qml/Stats.qml | 16 ++++ interface/src/ui/Stats.cpp | 17 ++++- interface/src/ui/Stats.h | 75 ++++++++++++++++++- .../src/RenderablePolyVoxEntityItem.cpp | 1 - libraries/entities/src/EntityTreeElement.cpp | 6 -- libraries/pointers/src/PickCacheOptimizer.h | 10 ++- libraries/pointers/src/PickManager.cpp | 14 ++-- libraries/pointers/src/PickManager.h | 6 ++ libraries/render-utils/src/Model.cpp | 12 +-- 9 files changed, 134 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2e6e909312..8b277d4c20 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -116,6 +116,22 @@ Item { visible: root.expanded text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount } + StatText { + visible: root.expanded + text: "Total picks:\n " + + root.stylusPicksCount + " styluses\n " + + root.rayPicksCount + " rays\n " + + root.parabolaPicksCount + " parabolas\n " + + root.collisionPicksCount + " colliders" + } + StatText { + visible: root.expanded + text: "Intersection calls: Entities/Overlays/Avatars/HUD\n " + + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "/" + root.stylusPicksUpdated.w + "\n " + + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "/" + root.rayPicksUpdated.w + "\n " + + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "/" + root.parabolaPicksUpdated.w + "\n " + + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z + "/" + root.collisionPicksUpdated.w + } } } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ce1cd51de1..16e2bb955f 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -146,6 +147,20 @@ void Stats::updateStats(bool force) { } STAT_UPDATE(gameLoopRate, (int)qApp->getGameLoopRate()); + auto pickManager = DependencyManager::get(); + if (pickManager && (_expanded || force)) { + std::vector totalPicks = pickManager->getTotalPickCounts(); + STAT_UPDATE(stylusPicksCount, totalPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksCount, totalPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksCount, totalPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksCount, totalPicks[PickQuery::Collision]); + std::vector updatedPicks = pickManager->getUpdatedPickCounts(); + STAT_UPDATE(stylusPicksUpdated, updatedPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksUpdated, updatedPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksUpdated, updatedPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksUpdated, updatedPicks[PickQuery::Collision]); + } + auto bandwidthRecorder = DependencyManager::get(); STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); STAT_UPDATE(packetOutCount, (int)bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); @@ -285,7 +300,7 @@ void Stats::updateStats(bool force) { // downloads << (int)(resource->getProgress() * 100.0f) << "% "; //} //downloads << "(" << << " pending)"; - } // expanded avatar column + } // Fourth column, octree stats int serverCount = 0; diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index cf624b54c3..7845a540de 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -22,7 +22,6 @@ public: \ private: \ type _##name{ initialValue }; - /**jsdoc * @namespace Stats * @@ -168,6 +167,15 @@ private: \ * @property {number} implicitHeight * * @property {object} layer - Read-only. + + * @property {number} stylusPicksCount - Read-only. + * @property {number} rayPicksCount - Read-only. + * @property {number} parabolaPicksCount - Read-only. + * @property {number} collisionPicksCount - Read-only. + * @property {Vec4} stylusPicksUpdated - Read-only. + * @property {Vec4} rayPicksUpdated - Read-only. + * @property {Vec4} parabolaPicksUpdated - Read-only. + * @property {Vec4} collisionPicksUpdated - Read-only. */ // Properties from x onwards are QQuickItem properties. @@ -285,6 +293,15 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, avatarSimulationTime, 0) Q_PROPERTY(QStringList animStackNames READ animStackNames NOTIFY animStackNamesChanged) + STATS_PROPERTY(int, stylusPicksCount, 0) + STATS_PROPERTY(int, rayPicksCount, 0) + STATS_PROPERTY(int, parabolaPicksCount, 0) + STATS_PROPERTY(int, collisionPicksCount, 0) + STATS_PROPERTY(QVector4D, stylusPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, rayPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, parabolaPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, collisionPicksUpdated, QVector4D(0, 0, 0, 0)) + public: static Stats* getInstance(); @@ -1245,6 +1262,62 @@ signals: * @function Stats.update */ + /**jsdoc + * Triggered when the value of the stylusPicksCount property changes. + * @function Stats.stylusPicksCountChanged + * @returns {Signal} + */ + void stylusPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksCount property changes. + * @function Stats.rayPicksCountChanged + * @returns {Signal} + */ + void rayPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksCount property changes. + * @function Stats.parabolaPicksCountChanged + * @returns {Signal} + */ + void parabolaPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksCount property changes. + * @function Stats.collisionPicksCountChanged + * @returns {Signal} + */ + void collisionPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the stylusPicksUpdated property changes. + * @function Stats.stylusPicksUpdatedChanged + * @returns {Signal} + */ + void stylusPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksUpdated property changes. + * @function Stats.rayPicksUpdatedChanged + * @returns {Signal} + */ + void rayPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksUpdated property changes. + * @function Stats.parabolaPicksUpdatedChanged + * @returns {Signal} + */ + void parabolaPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksUpdated property changes. + * @function Stats.collisionPicksUpdatedChanged + * @returns {Signal} + */ + void collisionPicksUpdatedChanged(); + private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon{ true }; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 63f27dd170..af8d4fac86 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -571,7 +571,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o } glm::mat4 wtvMatrix = worldToVoxelMatrix(); - glm::mat4 vtwMatrix = voxelToWorldMatrix(); glm::vec3 normDirection = glm::normalize(direction); // the PolyVox ray intersection code requires a near and far point. diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index e8e11c0ee1..b7288755a6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -145,7 +145,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = FLT_MAX; BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -153,8 +152,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = distance; EntityItemID entityID = findDetailedRayIntersection(origin, direction, element, distanceToElementDetails, @@ -285,7 +282,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = std::numeric_limits::max(); BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -293,8 +289,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = parabolicDistance; // We can precompute the world-space parabola normal and reuse it for the parabola plane intersects AABox sphere check diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index 49a039935c..7a52cfc410 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -37,7 +37,7 @@ template class PickCacheOptimizer { public: - void update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); + QVector4D update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); protected: typedef std::unordered_map> PickCache; @@ -67,8 +67,9 @@ void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultP } template -void PickCacheOptimizer::update(std::unordered_map>& picks, +QVector4D PickCacheOptimizer::update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD) { + QVector4D numIntersectionsComputed; PickCache results; const uint32_t INVALID_PICK_ID = 0; auto itr = picks.begin(); @@ -91,6 +92,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick); + numIntersectionsComputed[0]++; if (entityRes) { cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick); } @@ -101,6 +103,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) { PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick); + numIntersectionsComputed[1]++; if (overlayRes) { cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick); } @@ -111,6 +114,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) { PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick); + numIntersectionsComputed[2]++; if (avatarRes) { cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick); } @@ -122,6 +126,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getHUDFlags(), QVector(), QVector() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) { PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick); + numIntersectionsComputed[3]++; if (hudRes) { cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick); } @@ -145,6 +150,7 @@ void PickCacheOptimizer::update(std::unordered_mapsecond].erase(uid); _typeMap.erase(uid); + _totalPickCounts[type->second]--; } }); } @@ -96,12 +98,12 @@ void PickManager::update() { }); bool shouldPickHUD = _shouldPickHUDOperator(); - // we pass the same expiry to both updates, but the stylus updates are relatively cheap - // and the rayPicks updae will ALWAYS update at least one ray even when there is no budget - _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); - _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); - _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); - _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); + // FIXME: give each type its own expiry + // Each type will update at least one pick, regardless of the expiry + _updatedPickCounts[PickQuery::Stylus] = _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); + _updatedPickCounts[PickQuery::Ray] = _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Parabola] = _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Collision] = _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); } bool PickManager::isLeftHand(unsigned int uid) { diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 595c43e71d..e8d32771e0 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -58,10 +58,16 @@ public: bool getForceCoarsePicking() { return _forceCoarsePicking; } + const std::vector& getUpdatedPickCounts() { return _updatedPickCounts; } + const std::vector& getTotalPickCounts() { return _totalPickCounts; } + public slots: void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } protected: + std::vector _updatedPickCounts { PickQuery::NUM_PICK_TYPES }; + std::vector _totalPickCounts { 0, 0, 0, 0 }; + bool _forceCoarsePicking { false }; std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 22bc6b6ca7..ae3ee6e9a2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -385,9 +385,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { @@ -527,9 +527,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { From 0e0561bb24bfbf93f62b71ef18276e15d8c415fa Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:59:18 -0700 Subject: [PATCH 036/106] latest rotate code --- interface/src/avatar/MyAvatar.cpp | 20 +++++++++++++++----- scripts/developer/rotateApp.js | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6211e6f0d8..8d9c503e5e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -462,7 +462,7 @@ void MyAvatar::update(float deltaTime) { setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - + computeHandAzimuth(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE @@ -473,6 +473,14 @@ void MyAvatar::update(float deltaTime) { glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + + // draw hand azimuth vector + + glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); + glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); + qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); + DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); + DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); } #endif @@ -802,11 +810,13 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } void MyAvatar::computeHandAzimuth() { - controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); - controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); + auto leftHandPoseAvatarSpace = getLeftHandPose(); + auto rightHandPoseAvatarSpace = getRightHandPose(); glm::vec3 localLookat(0.0f, 0.0f, 1.0f); - glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.rotation * localLookat; - glm::vec3 lefttHandRigSpace = leftHandPoseAvatarSpace.rotation * localLookat; + glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); + glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); + + //qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; _hipToHandController = glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z); diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index 60d78d66d9..e4080c9bb0 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -320,7 +320,7 @@ function computeHandAzimuths(timeElapsed) { // put a hard max on this easing function. var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0)/3.0) * leftRightMidpoint; - print("rotate angle " + rotateAngle); + //print("rotate angle " + rotateAngle); return Quat.fromVec3Degrees({ x: 0, y: rotateAngle, z: 0 }); From 1fd0d7da94c3cca2f4dc367cc41f8e84eed1f072 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 18:03:27 -0700 Subject: [PATCH 037/106] Create API call --- libraries/avatars/src/AvatarHashMap.cpp | 24 +++++++++++++++++------- libraries/avatars/src/AvatarHashMap.h | 12 ++++++++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 800825f574..6949efc19c 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -32,15 +32,15 @@ void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer repli replicas.push_back(replica); } -std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID, int count) { +std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID) { std::vector ids; if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; for (int i = 0; i < replicas.size(); i++) { ids.push_back(replicas[i]->getID()); } - } else if (count > 0) { - for (int i = 0; i < count; i++) { + } else if (_replicaCount > 0) { + for (int i = 0; i < _replicaCount; i++) { ids.push_back(QUuid::createUuid()); } } @@ -139,6 +139,19 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range return false; } +void AvatarHashMap::setReplicaCount(int count) { + _replicas.setReplicaCount(count); + auto avatars = getAvatarIdentifiers(); + for (int i = 0; i < avatars.size(); i++) { + KillAvatarReason reason = KillAvatarReason::NoReason; + removeAvatar(avatars[i], reason); + auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); + for (auto id : replicaIDs) { + removeAvatar(id, reason); + } + } +} + int AvatarHashMap::numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters) { auto hashCopy = getHashCopy(); auto rangeMeters2 = rangeMeters * rangeMeters; @@ -210,9 +223,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer(); - - const int REPLICAS_COUNT = 8; - bool isNewAvatar; if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); @@ -220,7 +230,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer getReplicaIDs(const QUuid& parentID, int count = 0); + std::vector getReplicaIDs(const QUuid& parentID); void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); void removeReplicas(const QUuid& parentID); @@ -53,9 +53,11 @@ public: void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + void setReplicaCount(int count) { _replicaCount = count; } private: std::map> _replicasMap; + int _replicaCount; }; @@ -92,6 +94,12 @@ public: // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } + /**jsdoc + * @function AvatarList.setReplicaCount + * @param {number} count // The times an avatar will get replicated + */ + Q_INVOKABLE void setReplicaCount(int count); + virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); From 44a6c7df5f1b648796d8f45fc419acea8c774873 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 24 Aug 2018 07:17:17 +0100 Subject: [PATCH 038/106] added hook to myskeleton model to update spine2, need to get the rotation from myavatar then we have spine rot --- interface/src/avatar/MyAvatar.cpp | 4 +++- interface/src/avatar/MySkeletonModel.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8d9c503e5e..2771f567b3 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -476,11 +476,13 @@ void MyAvatar::update(float deltaTime) { // draw hand azimuth vector + glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); + DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } #endif @@ -818,7 +820,7 @@ void MyAvatar::computeHandAzimuth() { //qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; - _hipToHandController = glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z); + _hipToHandController = lerp(glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z), glm::vec2(leftHandRigSpace.x, leftHandRigSpace.z),0.5f); } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0fc5e7521e..491fab466d 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -238,6 +238,14 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; + + // spine 2 hack to be improved. + AnimPose newSpinePose; + bool fred = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), newSpinePose); + newSpinePose.rot() = glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f)*newSpinePose.rot(); + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = newSpinePose; + params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; + } else { _prevHipsValid = false; } From 25610c75876ba7db1de50f26855582d05a4a9ac2 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 24 Aug 2018 11:04:36 +0100 Subject: [PATCH 039/106] got the hands turning the shoulders, needs error checking --- interface/src/avatar/MyAvatar.cpp | 15 +++++++++++++++ interface/src/avatar/MyAvatar.h | 2 ++ interface/src/avatar/MySkeletonModel.cpp | 5 ++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2771f567b3..97179f5d22 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3349,6 +3349,21 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { return createMatFromQuatAndPos(headOrientationYawOnly, bodyPos); } +glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { + + static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; + glm::vec3 hipToHandRigSpace = RIG_CHANGE_OF_BASIS * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + + //to do: check for zero before normalizing. + glm::vec3 u, v, w; + generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); + glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); + + //glm::quat spine2RigSpace = glm::quat_cast(glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::normalize(hipToHandRigSpace), glm::vec3(0.0f, 1.0f, 0.0f))); + + return spine2RigSpace; +} + // ease in function for dampening cg movement static float slope(float num) { const float CURVE_CONSTANT = 1.0f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 6b7c4e83a1..3c951264e8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1050,6 +1050,8 @@ public: // results are in sensor frame (-z forward) glm::mat4 deriveBodyFromHMDSensor() const; + glm::mat4 getSpine2RotationRigSpace() const; + glm::vec3 computeCounterBalance(); // derive avatar body position and orientation from using the current HMD Sensor location in relation to the previous diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 491fab466d..5272b1499d 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -240,9 +240,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // spine 2 hack to be improved. - AnimPose newSpinePose; - bool fred = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), newSpinePose); - newSpinePose.rot() = glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f)*newSpinePose.rot(); + AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); + //newSpinePose.rot() = myAvatar->getSpine2RotationRigSpace();// *newSpinePose.rot(); params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = newSpinePose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; From 5654acf5bf28c9476402a1bc6a2cb4ef3545bf07 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 24 Aug 2018 09:24:58 -0700 Subject: [PATCH 040/106] Fix warnings --- libraries/avatars/src/AvatarHashMap.cpp | 99 ++----------------------- libraries/avatars/src/AvatarHashMap.h | 8 +- 2 files changed, 15 insertions(+), 92 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 6949efc19c..67cc9f0563 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -36,7 +36,7 @@ std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID) { std::vector ids; if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; - for (int i = 0; i < replicas.size(); i++) { + for (int i = 0; i < (int)replicas.size(); i++) { ids.push_back(replicas[i]->getID()); } } else if (_replicaCount > 0) { @@ -288,92 +288,12 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); - } -} -void AvatarHashMap::processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { - message->seek(0); - while (message->getBytesLeftToRead()) { - // read the avatar ID to figure out which avatar this is for - auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - avatarID = sessionUUID; - - // grab the avatar so we can ask it to process trait data - bool isNewAvatar; - auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); - - // read the first trait type for this avatar - AvatarTraits::TraitType traitType; - message->readPrimitive(&traitType); - - // grab the last trait versions for this avatar - auto& lastProcessedVersions = _processedTraitVersions[avatarID]; - - while (traitType != AvatarTraits::NullTrait) { - AvatarTraits::TraitVersion packetTraitVersion; - message->readPrimitive(&packetTraitVersion); - - AvatarTraits::TraitWireSize traitBinarySize; - bool skipBinaryTrait = false; - - - if (AvatarTraits::isSimpleTrait(traitType)) { - message->readPrimitive(&traitBinarySize); - - // check if this trait version is newer than what we already have for this avatar - if (packetTraitVersion > lastProcessedVersions[traitType]) { - avatar->processTrait(traitType, message->read(traitBinarySize)); - lastProcessedVersions[traitType] = packetTraitVersion; - } - else { - skipBinaryTrait = true; - } - } - else { - AvatarTraits::TraitInstanceID traitInstanceID = - QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - - message->readPrimitive(&traitBinarySize); - - auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); - if (packetTraitVersion > processedInstanceVersion) { - if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { - avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } - else { - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - } - processedInstanceVersion = packetTraitVersion; - } - else { - skipBinaryTrait = true; - } - } - - if (skipBinaryTrait) { - // we didn't read this trait because it was older or because we didn't have an avatar to process it for - message->seek(message->getPosition() + traitBinarySize); - } - - // read the next trait type, which is null if there are no more traits for this avatar - message->readPrimitive(&traitType); - } } } void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { - /* - while (message->getBytesLeftToRead()) { - // read the avatar ID to figure out which avatar this is for - auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - processBulkAvatarTraitsForID(avatarID, message, sendingNode); - auto replicaIDs = _replicas.getReplicaIDs(avatarID); - for (auto id : replicaIDs) { - processBulkAvatarTraitsForID(id, message, sendingNode); - } - } - */ - int position = 0; + while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -381,7 +301,6 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess // grab the avatar so we can ask it to process trait data bool isNewAvatar; auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); - // read the first trait type for this avatar AvatarTraits::TraitType traitType; message->readPrimitive(&traitType); @@ -402,10 +321,9 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess // check if this trait version is newer than what we already have for this avatar if (packetTraitVersion > lastProcessedVersions[traitType]) { - position = message->getPosition(); - avatar->processTrait(traitType, message->read(traitBinarySize)); - message->seek(position); - _replicas.processTrait(avatarID, traitType, message->read(traitBinarySize)); + auto traitData = message->read(traitBinarySize); + avatar->processTrait(traitType, traitData); + _replicas.processTrait(avatarID, traitType, traitData); lastProcessedVersions[traitType] = packetTraitVersion; } else { @@ -425,10 +343,9 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); } else { - position = message->getPosition(); - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - message->seek(position); - _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, message->read(traitBinarySize)); + auto traitData = message->read(traitBinarySize); + avatar->processTraitInstance(traitType, traitInstanceID, traitData); + _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData); } processedInstanceVersion = packetTraitVersion; } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index b7199d25ac..97c54e1787 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -54,6 +54,7 @@ public: void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); void setReplicaCount(int count) { _replicaCount = count; } + int getReplicaCount() { return _replicaCount; } private: std::map> _replicasMap; @@ -99,6 +100,12 @@ public: * @param {number} count // The times an avatar will get replicated */ Q_INVOKABLE void setReplicaCount(int count); + + /**jsdoc + * @function AvatarList.setReplicaCount + * @param {number} count // The times an avatar will get replicated + */ + Q_INVOKABLE int getReplicaCount() { return _replicas.getReplicaCount(); }; virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); @@ -160,7 +167,6 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); - void processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc From a60ab863a39a78653a680b06d3886f2ca5d5c646 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 24 Aug 2018 11:06:36 -0700 Subject: [PATCH 041/106] demo version of the head and hands determining the rotate function --- interface/src/avatar/MyAvatar.cpp | 45 +++++++++++++++--------- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/MySkeletonModel.cpp | 5 ++- scripts/developer/rotateApp.js | 7 ++-- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 97179f5d22..a1e30144ee 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -449,7 +449,18 @@ void MyAvatar::update(float deltaTime) { const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); float tau = deltaTime / HMD_FACING_TIMESCALE; - _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau); + + // put the spine facing in sensor space. + // then mix it with head facing to determine rotation recenter + glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); + glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); + glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); + glm::vec2 normedHandHip = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHip, _headControllerFacing, 0.5f); + //qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); + qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { _rotationChanged = usecTimestampNow(); @@ -479,7 +490,7 @@ void MyAvatar::update(float deltaTime) { glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); - qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); + //qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); @@ -815,13 +826,17 @@ void MyAvatar::computeHandAzimuth() { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); glm::vec3 localLookat(0.0f, 0.0f, 1.0f); - glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); - glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); - - //qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; - - _hipToHandController = lerp(glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z), glm::vec2(leftHandRigSpace.x, leftHandRigSpace.z),0.5f); + // glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); + // glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); + // qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; + if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), 0.5f); + //need to use easing function here. + //var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0) / 3.0) * leftRightMidpoint; + } else { + _hipToHandController = glm::vec2(0.0f, 1.0f); + } } void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache) { @@ -3359,8 +3374,6 @@ glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); - //glm::quat spine2RigSpace = glm::quat_cast(glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::normalize(hipToHandRigSpace), glm::vec3(0.0f, 1.0f, 0.0f))); - return spine2RigSpace; } @@ -3977,15 +3990,15 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { - if (!isActive(Rotation) && getForceActivateRotation()) { - activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); - setForceActivateRotation(false); - } - //if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + //if (!isActive(Rotation) && getForceActivateRotation()) { // activate(Rotation); // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + // setForceActivateRotation(false); //} + if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + activate(Rotation); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + } if (myAvatar.getCenterOfGravityModelEnabled()) { if ((!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) || (isActive(Horizontal) && (currentVelocity > VELOCITY_THRESHHOLD))) { activate(Horizontal); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3c951264e8..177b453267 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1663,7 +1663,7 @@ private: glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; - glm::vec2 _hipToHandController; // facing vector in xz plane (sensor space) + glm::vec2 _hipToHandController; // spine2 facing vector in xz plane (avatar space) float _currentStandingHeight { 0.0f }; bool _resetMode { true }; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 5272b1499d..158f9bb0b6 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -240,9 +240,12 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // spine 2 hack to be improved. + AnimPose currentPose; + bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"),currentPose); AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); + currentPose.rot() = newSpinePose.rot(); //newSpinePose.rot() = myAvatar->getSpine2RotationRigSpace();// *newSpinePose.rot(); - params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = newSpinePose; + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentPose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } else { diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index e4080c9bb0..030385b6fb 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -29,6 +29,7 @@ var spine2Rotation = { x: 0, y: 0, z: 0, w: 1 }; var hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; var hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; +/* var ikTypes = { RotationAndPosition: 0, RotationOnly: 1, @@ -65,7 +66,7 @@ var handlerId = MyAvatar.addAnimationStateHandler(function (props) { return result; }, ANIM_VARS); - +*/ // define state readings constructor function StateReading(headPose, rhandPose, lhandPose, diffFromAverageEulers) { this.headPose = headPose; @@ -298,7 +299,7 @@ function computeHandAzimuths(timeElapsed) { var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.orientation, zAxisSpineRotation); - DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); + // DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); //print(leftRightMidpoint); @@ -312,7 +313,7 @@ function computeHandAzimuths(timeElapsed) { var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { //print("recenter the feet under the head"); - MyAvatar.triggerRotationRecenter(); + // MyAvatar.triggerRotationRecenter(); hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; From 1c0c17cecbb879901301feaaa73b0868a5f1335e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 11:20:10 -0700 Subject: [PATCH 042/106] faster triangle intersection --- libraries/shared/src/GeometryUtil.cpp | 38 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index a93c2ec9f3..c69b05db53 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -392,24 +392,34 @@ Triangle Triangle::operator*(const glm::mat4& transform) const { }; } +// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance, bool allowBackface) { - glm::vec3 firstSide = v0 - v1; - glm::vec3 secondSide = v2 - v1; - glm::vec3 normal = glm::cross(secondSide, firstSide); - float dividend = glm::dot(normal, v1) - glm::dot(origin, normal); - if (!allowBackface && dividend > 0.0f) { - return false; // origin below plane - } - float divisor = glm::dot(normal, direction); - if (divisor >= 0.0f) { + glm::vec3 firstSide = v1 - v0; + glm::vec3 secondSide = v2 - v0; + glm::vec3 P = glm::cross(direction, secondSide); + float det = glm::dot(firstSide, P); + if (!allowBackface && det < EPSILON) { + return false; + } else if (fabsf(det) < EPSILON) { return false; } - float t = dividend / divisor; - glm::vec3 point = origin + direction * t; - if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f && - glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f && - glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) { + + float invDet = 1.0f / det; + glm::vec3 T = origin - v0; + float u = glm::dot(T, P) * invDet; + if (u < 0.0f || u > 1.0f) { + return false; + } + + glm::vec3 Q = glm::cross(T, firstSide); + float v = glm::dot(direction, Q) * invDet; + if (v < 0.0f || u + v > 1.0f) { + return false; + } + + float t = glm::dot(secondSide, Q) * invDet; + if (t > EPSILON) { distance = t; return true; } From ddbadf5a6936e29a426bd862ffca06129cfd1fac Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 24 Aug 2018 11:59:46 -0700 Subject: [PATCH 043/106] change to Test API --- interface/src/Application.h | 3 +++ .../src/scripting/TestScriptingInterface.cpp | 8 ++++++++ interface/src/scripting/TestScriptingInterface.h | 14 ++++++++++++++ libraries/avatars/src/AvatarHashMap.h | 15 +++------------ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 742cf075f6..0d4417cd91 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -311,6 +311,9 @@ public: Q_INVOKABLE void copyToClipboard(const QString& text); + int getOtherAvatarsReplicaCount() { return DependencyManager::get()->getReplicaCount(); } + void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); } + #if defined(Q_OS_ANDROID) void beforeEnterBackground(); void enterBackground(); diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 430441226f..52f6a3ebc0 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -190,4 +190,12 @@ void TestScriptingInterface::saveObject(QVariant variant, const QString& filenam void TestScriptingInterface::showMaximized() { qApp->getWindow()->showMaximized(); +} + +void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) { + qApp->setOtherAvatarsReplicaCount(count); +} + +int TestScriptingInterface::getOtherAvatarsReplicaCount() { + return qApp->getOtherAvatarsReplicaCount(); } \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index c47e39d1f3..4a1d1a3eeb 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -149,6 +149,20 @@ public slots: */ void showMaximized(); + /**jsdoc + * Values higher than 0 will create replicas of other-avatars when entering a domain for testing purpouses + * @function Test.setOtherAvatarsReplicaCount + * @param {number} count - Number of replicas we want to create + */ + Q_INVOKABLE void setOtherAvatarsReplicaCount(int count); + + /**jsdoc + * Return the number of replicas that are being created of other-avatars when entering a domain + * @function Test.getOtherAvatarsReplicaCount + * @returns {number} Current number of replicas of other-avatars. + */ + Q_INVOKABLE int getOtherAvatarsReplicaCount(); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 97c54e1787..e4e197a080 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -95,21 +95,12 @@ public: // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } - /**jsdoc - * @function AvatarList.setReplicaCount - * @param {number} count // The times an avatar will get replicated - */ - Q_INVOKABLE void setReplicaCount(int count); - - /**jsdoc - * @function AvatarList.setReplicaCount - * @param {number} count // The times an avatar will get replicated - */ - Q_INVOKABLE int getReplicaCount() { return _replicas.getReplicaCount(); }; - virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); + void setReplicaCount(int count); + int getReplicaCount() { return _replicas.getReplicaCount(); }; + signals: /**jsdoc From 98d145dc2487508bcf1d4076892cb6f0cf14e3dc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 15:19:48 -0700 Subject: [PATCH 044/106] fix crash on shutdown in domain with material --- interface/src/Application.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e290531471..44bba6250b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1841,6 +1841,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) { + if (_aboutToQuit) { + return false; + } + // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); if (renderable) { @@ -1856,6 +1860,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return false; }); EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) { + if (_aboutToQuit) { + return false; + } + // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); if (renderable) { From 3167d3c0c726f791fff2d3c3041618e3032cd57a Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 23 Aug 2018 14:05:44 -0700 Subject: [PATCH 045/106] Create TransformNodes for SpatiallyNestables, the mouse, and MyAvatar head --- .../src/avatar/MyAvatarHeadTransformNode.cpp | 23 ++++++++++ .../src/avatar/MyAvatarHeadTransformNode.h | 19 ++++++++ interface/src/raypick/MouseTransformNode.cpp | 43 +++++++++++++++++++ interface/src/raypick/MouseTransformNode.h | 23 ++++++++++ .../shared/src/NestableTransformNode.cpp | 31 +++++++++++++ libraries/shared/src/NestableTransformNode.h | 25 +++++++++++ libraries/shared/src/TransformNode.h | 18 ++++++++ 7 files changed, 182 insertions(+) create mode 100644 interface/src/avatar/MyAvatarHeadTransformNode.cpp create mode 100644 interface/src/avatar/MyAvatarHeadTransformNode.h create mode 100644 interface/src/raypick/MouseTransformNode.cpp create mode 100644 interface/src/raypick/MouseTransformNode.h create mode 100644 libraries/shared/src/NestableTransformNode.cpp create mode 100644 libraries/shared/src/NestableTransformNode.h create mode 100644 libraries/shared/src/TransformNode.h diff --git a/interface/src/avatar/MyAvatarHeadTransformNode.cpp b/interface/src/avatar/MyAvatarHeadTransformNode.cpp new file mode 100644 index 0000000000..82c2f8b703 --- /dev/null +++ b/interface/src/avatar/MyAvatarHeadTransformNode.cpp @@ -0,0 +1,23 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MyAvatarHeadTransformNode.h" + +#include "DependencyManager.h" +#include "AvatarManager.h" +#include "MyAvatar.h" + +Transform MyAvatarHeadTransformNode::getTransform() { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + + glm::vec3 pos = myAvatar->getHeadPosition(); + glm::quat headOri = myAvatar->getHeadOrientation(); + glm::quat ori = headOri * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT); + + return Transform(ori, myAvatar->scaleForChildren(), pos); +} \ No newline at end of file diff --git a/interface/src/avatar/MyAvatarHeadTransformNode.h b/interface/src/avatar/MyAvatarHeadTransformNode.h new file mode 100644 index 0000000000..a7d7144521 --- /dev/null +++ b/interface/src/avatar/MyAvatarHeadTransformNode.h @@ -0,0 +1,19 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_MyAvatarHeadTransformNode_h +#define hifi_MyAvatarHeadTransformNode_h + +#include "TransformNode.h" + +class MyAvatarHeadTransformNode : public TransformNode { +public: + MyAvatarHeadTransformNode() { } + Transform getTransform() override; +}; + +#endif // hifi_MyAvatarHeadTransformNode_h \ No newline at end of file diff --git a/interface/src/raypick/MouseTransformNode.cpp b/interface/src/raypick/MouseTransformNode.cpp new file mode 100644 index 0000000000..4bc85ae69a --- /dev/null +++ b/interface/src/raypick/MouseTransformNode.cpp @@ -0,0 +1,43 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MouseTransformNode.h" + +#include "DependencyManager.h" +#include "PickManager.h" +#include "MouseRayPick.h" + +const PickFilter MOUSE_TRANSFORM_NODE_PICK_FILTER( + 1 << PickFilter::PICK_ENTITIES | + 1 << PickFilter::PICK_AVATARS | + 1 << PickFilter::PICK_INCLUDE_NONCOLLIDABLE +); +const float MOUSE_TRANSFORM_NODE_MAX_DISTANCE = 1000.0f; + +MouseTransformNode::MouseTransformNode() { + _parentMouseRayPick = DependencyManager::get()->addPick(PickQuery::Ray, + std::make_shared(MOUSE_TRANSFORM_NODE_PICK_FILTER, MOUSE_TRANSFORM_NODE_MAX_DISTANCE, true)); +} + +MouseTransformNode::~MouseTransformNode() { + if (DependencyManager::isSet()) { + auto pickManager = DependencyManager::get(); + if (pickManager) { + pickManager->removePick(_parentMouseRayPick); + } + } +} + +Transform MouseTransformNode::getTransform() { + Transform transform; + std::shared_ptr rayPickResult = DependencyManager::get()->getPrevPickResultTyped(_parentMouseRayPick); + if (rayPickResult) { + transform.setTranslation(rayPickResult->intersection); + } + return transform; +} \ No newline at end of file diff --git a/interface/src/raypick/MouseTransformNode.h b/interface/src/raypick/MouseTransformNode.h new file mode 100644 index 0000000000..7a05370dd7 --- /dev/null +++ b/interface/src/raypick/MouseTransformNode.h @@ -0,0 +1,23 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_MouseTransformNode_h +#define hifi_MouseTransformNode_h + +#include "TransformNode.h" + +class MouseTransformNode : public TransformNode { +public: + MouseTransformNode(); + ~MouseTransformNode(); + Transform getTransform() override; + +protected: + unsigned int _parentMouseRayPick = 0; +}; + +#endif // hifi_MouseTransformNode_h \ No newline at end of file diff --git a/libraries/shared/src/NestableTransformNode.cpp b/libraries/shared/src/NestableTransformNode.cpp new file mode 100644 index 0000000000..7fb7187aee --- /dev/null +++ b/libraries/shared/src/NestableTransformNode.cpp @@ -0,0 +1,31 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "NestableTransformNode.h" + +NestableTransformNode::NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex) : + _spatiallyNestable(spatiallyNestable), + _jointIndex(jointIndex) +{ +} + +Transform NestableTransformNode::getTransform() { + auto nestable = _spatiallyNestable.lock(); + if (!nestable) { + return Transform(); + } + + bool success; + Transform jointWorldTransform = nestable->getTransform(_jointIndex, success, 30); + + if (success) { + return jointWorldTransform; + } else { + return Transform(); + } +} \ No newline at end of file diff --git a/libraries/shared/src/NestableTransformNode.h b/libraries/shared/src/NestableTransformNode.h new file mode 100644 index 0000000000..131de9e786 --- /dev/null +++ b/libraries/shared/src/NestableTransformNode.h @@ -0,0 +1,25 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_NestableTransformNode_h +#define hifi_NestableTransformNode_h + +#include "TransformNode.h" + +#include "SpatiallyNestable.h" + +class NestableTransformNode : public TransformNode { +public: + NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex); + Transform getTransform() override; + +protected: + SpatiallyNestableWeakPointer _spatiallyNestable; + int _jointIndex; +}; + +#endif // hifi_NestableTransformNode_h \ No newline at end of file diff --git a/libraries/shared/src/TransformNode.h b/libraries/shared/src/TransformNode.h new file mode 100644 index 0000000000..73223a8cee --- /dev/null +++ b/libraries/shared/src/TransformNode.h @@ -0,0 +1,18 @@ +// +// Created by Sabrina Shanman 8/14/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_TransformNode_h +#define hifi_TransformNode_h + +#include "Transform.h" + +class TransformNode { +public: + virtual Transform getTransform() = 0; +}; + +#endif // hifi_TransformNode_h \ No newline at end of file From b0f8d3e42727e77a79a3e8eee8da507701828d25 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 23 Aug 2018 14:06:39 -0700 Subject: [PATCH 046/106] Add PickTransformNode --- libraries/pointers/src/Pick.h | 1 + libraries/pointers/src/PickManager.cpp | 8 ++++++ libraries/pointers/src/PickManager.h | 2 ++ libraries/pointers/src/PickTransformNode.cpp | 26 ++++++++++++++++++++ libraries/pointers/src/PickTransformNode.h | 22 +++++++++++++++++ 5 files changed, 59 insertions(+) create mode 100644 libraries/pointers/src/PickTransformNode.cpp create mode 100644 libraries/pointers/src/PickTransformNode.h diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index fc09064bd1..1a48aac0ef 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -17,6 +17,7 @@ #include #include +#include enum IntersectionType { NONE = 0, diff --git a/libraries/pointers/src/PickManager.cpp b/libraries/pointers/src/PickManager.cpp index 470d88a46c..b7c57c6aba 100644 --- a/libraries/pointers/src/PickManager.cpp +++ b/libraries/pointers/src/PickManager.cpp @@ -88,6 +88,14 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector& includ } } +Transform PickManager::getResultTransform(unsigned int uid) const { + auto pick = findPick(uid); + if (pick) { + return pick->getResultTransform(); + } + return Transform(); +} + void PickManager::update() { uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget; std::unordered_map>> cachedPicks; diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 242550d837..3209a4c037 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -40,6 +40,8 @@ public: void setIgnoreItems(unsigned int uid, const QVector& ignore) const; void setIncludeItems(unsigned int uid, const QVector& include) const; + Transform getResultTransform(unsigned int uid) const; + bool isLeftHand(unsigned int uid); bool isRightHand(unsigned int uid); bool isMouse(unsigned int uid); diff --git a/libraries/pointers/src/PickTransformNode.cpp b/libraries/pointers/src/PickTransformNode.cpp new file mode 100644 index 0000000000..fe011b7fcd --- /dev/null +++ b/libraries/pointers/src/PickTransformNode.cpp @@ -0,0 +1,26 @@ +// +// Created by Sabrina Shanman 8/22/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PickTransformNode.h" + +#include "DependencyManager.h" +#include "PickManager.h" + +PickTransformNode::PickTransformNode(unsigned int uid) : + _uid(uid) +{ +} + +Transform PickTransformNode::getTransform() { + auto pickManager = DependencyManager::get(); + if (!pickManager) { + return Transform(); + } + + return pickManager->getResultTransform(_uid); +} \ No newline at end of file diff --git a/libraries/pointers/src/PickTransformNode.h b/libraries/pointers/src/PickTransformNode.h new file mode 100644 index 0000000000..7547d3f181 --- /dev/null +++ b/libraries/pointers/src/PickTransformNode.h @@ -0,0 +1,22 @@ +// +// Created by Sabrina Shanman 8/22/2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_PickTransformNode_h +#define hifi_PickTransformNode_h + +#include "TransformNode.h" + +class PickTransformNode : public TransformNode { +public: + PickTransformNode(unsigned int uid); + Transform getTransform() override; + +protected: + unsigned int _uid; +}; + +#endif // hifi_PickTransformNode_h \ No newline at end of file From 68b86963d4c616413279f21a5ec2adf2845f4580 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 22 Aug 2018 15:27:28 -0700 Subject: [PATCH 047/106] Add CollisionRegion copy constructor --- libraries/shared/src/RegisteredMetaTypes.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index e78dbafd75..3c8ee2ed7d 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -264,6 +264,18 @@ public: class CollisionRegion : public MathPick { public: CollisionRegion() { } + + CollisionRegion(const CollisionRegion& collisionRegion) : + modelURL(collisionRegion.modelURL), + shapeInfo(std::make_shared()), + transform(collisionRegion.transform), + parentID(collisionRegion.parentID), + parentJointIndex(collisionRegion.parentJointIndex), + joint(collisionRegion.joint) + { + shapeInfo->setParams(collisionRegion.shapeInfo->getType(), collisionRegion.shapeInfo->getHalfExtents(), collisionRegion.modelURL.toString()); + } + CollisionRegion(const QVariantMap& pickVariant) { if (pickVariant["shape"].isValid()) { auto shape = pickVariant["shape"].toMap(); From 6e160ad22f790a1b91e19cb1c6d7753c2cb40f48 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 16 Aug 2018 11:32:07 -0700 Subject: [PATCH 048/106] Add TransformNode-based parenting and implement for CollisionPicks --- interface/src/raypick/CollisionPick.cpp | 57 ++++++++++++++++--- interface/src/raypick/CollisionPick.h | 24 ++++---- interface/src/raypick/ParabolaPick.cpp | 12 ++++ interface/src/raypick/ParabolaPick.h | 1 + .../src/raypick/PickScriptingInterface.cpp | 50 +++++++++++++++- .../src/raypick/PickScriptingInterface.h | 3 + interface/src/raypick/RayPick.cpp | 12 ++++ interface/src/raypick/RayPick.h | 1 + interface/src/raypick/StylusPick.cpp | 12 ++++ interface/src/raypick/StylusPick.h | 1 + libraries/pointers/src/Pick.h | 6 +- libraries/shared/src/RegisteredMetaTypes.h | 29 +++++++++- 12 files changed, 184 insertions(+), 24 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 9f2e6da2e8..8c3d28fdfd 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -86,7 +86,7 @@ QVariantMap CollisionPickResult::toVariantMap() const { return variantMap; } -bool CollisionPick::isShapeInfoReady() { +bool CollisionPick::getShapeInfoReady() { if (_mathPick.shouldComputeShapeInfo()) { if (_cachedResource && _cachedResource->isLoaded()) { computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource); @@ -95,8 +95,23 @@ bool CollisionPick::isShapeInfoReady() { return false; } + else { + computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource); + return true; + } +} - return true; +void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { + ShapeType type = shapeInfo.getType(); + glm::vec3 dimensions = pick.transform.getScale(); + QString modelURL = (resource ? resource->getURL().toString() : ""); + if (type == SHAPE_TYPE_COMPOUND) { + shapeInfo.setParams(type, dimensions, modelURL); + } else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) { + shapeInfo.setParams(type, 0.5f * dimensions, modelURL); + } else { + shapeInfo.setParams(type, 0.5f * dimensions, modelURL); + } } void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { @@ -328,8 +343,23 @@ void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo } } +CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) : + Pick(filter, maxDistance, enabled), + _mathPick(collisionRegion), + _physicsEngine(physicsEngine) { + if (collisionRegion.shouldComputeShapeInfo()) { + _cachedResource = DependencyManager::get()->getCollisionGeometryResource(collisionRegion.modelURL); + } +} + CollisionRegion CollisionPick::getMathematicalPick() const { - return _mathPick; + if (!parentTransform) { + return _mathPick; + } else { + CollisionRegion transformedMathPick = _mathPick; + transformedMathPick.transform = parentTransform->getTransform().worldTransform(_mathPick.transform); + return transformedMathPick; + } } void CollisionPick::filterIntersections(std::vector& intersections) const { @@ -356,7 +386,7 @@ void CollisionPick::filterIntersections(std::vector& intersec } PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) { - if (!isShapeInfoReady()) { + if (!getShapeInfoReady()) { // Cannot compute result return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } @@ -367,13 +397,13 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi } PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick, getShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) { - if (!isShapeInfoReady()) { + if (!getShapeInfoReady()) { // Cannot compute result - return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick, CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); @@ -382,5 +412,16 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi } PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), getShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); +} + +Transform CollisionPick::getResultTransform() const { + PickResultPointer result = getPrevPickResult(); + if (!result) { + return Transform(); + } + + Transform transform; + transform.setTranslation(getMathematicalPick().transform.getTranslation()); + return transform; } \ No newline at end of file diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 6631238737..12d31111d1 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -11,6 +11,7 @@ #include #include #include +#include #include class CollisionPickResult : public PickResult { @@ -24,12 +25,17 @@ public: CollisionPickResult() {} CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {} - CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector& entityIntersections, const std::vector& avatarIntersections) : + CollisionPickResult(const CollisionRegion& searchRegion, + LoadState loadState, + const std::vector& entityIntersections, + const std::vector& avatarIntersections + ) : PickResult(searchRegion.toVariantMap()), loadState(loadState), intersects(entityIntersections.size() || avatarIntersections.size()), entityIntersections(entityIntersections), - avatarIntersections(avatarIntersections) { + avatarIntersections(avatarIntersections) + { } CollisionPickResult(const CollisionPickResult& collisionPickResult) : PickResult(collisionPickResult.pickVariant) { @@ -54,14 +60,7 @@ public: class CollisionPick : public Pick { public: - CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) : - Pick(filter, maxDistance, enabled), - _mathPick(collisionRegion), - _physicsEngine(physicsEngine) { - if (collisionRegion.shouldComputeShapeInfo()) { - _cachedResource = DependencyManager::get()->getCollisionGeometryResource(collisionRegion.modelURL); - } - } + CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine); CollisionRegion getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { @@ -71,11 +70,12 @@ public: PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override; PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override; PickResultPointer getHUDIntersection(const CollisionRegion& pick) override; - + Transform getResultTransform() const override; protected: // Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use. - bool isShapeInfoReady(); + bool getShapeInfoReady(); void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); + void computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); void filterIntersections(std::vector& intersections) const; CollisionRegion _mathPick; diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index b3e3f16345..1b37eee096 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -67,4 +67,16 @@ glm::vec3 ParabolaPick::getAcceleration() const { return scale * (DependencyManager::get()->getMyAvatar()->getWorldOrientation() * _accelerationAxis); } return scale * _accelerationAxis; +} + +Transform ParabolaPick::getResultTransform() const { + PickResultPointer result = getPrevPickResult(); + if (!result) { + return Transform(); + } + + auto parabolaResult = std::static_pointer_cast(result); + Transform transform; + transform.setTranslation(parabolaResult->intersection); + return transform; } \ No newline at end of file diff --git a/interface/src/raypick/ParabolaPick.h b/interface/src/raypick/ParabolaPick.h index 99a42a5380..01454390f9 100644 --- a/interface/src/raypick/ParabolaPick.h +++ b/interface/src/raypick/ParabolaPick.h @@ -83,6 +83,7 @@ public: PickResultPointer getOverlayIntersection(const PickParabola& pick) override; PickResultPointer getAvatarIntersection(const PickParabola& pick) override; PickResultPointer getHUDIntersection(const PickParabola& pick) override; + Transform getResultTransform() const override; protected: float _speed; diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index 0ed35e5589..f551c0ec90 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -23,6 +23,13 @@ #include "MouseParabolaPick.h" #include "CollisionPick.h" +#include "SpatialParentFinder.h" +#include "NestableTransformNode.h" +#include "PickTransformNode.h" +#include "MouseTransformNode.h" +#include "avatar/MyAvatarHeadTransformNode.h" +#include "avatar/AvatarManager.h" + #include unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { @@ -276,8 +283,10 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert } CollisionRegion collisionRegion(propMap); + auto collisionPick = std::make_shared(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine()); + collisionPick->parentTransform = createTransformNode(propMap); - return DependencyManager::get()->addPick(PickQuery::Collision, std::make_shared(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine())); + return DependencyManager::get()->addPick(PickQuery::Collision, collisionPick); } void PickScriptingInterface::enablePick(unsigned int uid) { @@ -351,3 +360,42 @@ unsigned int PickScriptingInterface::getPerFrameTimeBudget() const { void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) { DependencyManager::get()->setPerFrameTimeBudget(numUsecs); } + +std::shared_ptr PickScriptingInterface::createTransformNode(const QVariantMap& propMap) { + if (propMap["parentID"].isValid()) { + QUuid parentUuid = propMap["parentID"].toUuid(); + if (!parentUuid.isNull()) { + // Infer object type from parentID + // For now, assume a QUuuid is a SpatiallyNestable. This should change when picks are converted over to QUuids. + bool success; + std::weak_ptr nestablePointer = DependencyManager::get()->find(parentUuid, success, nullptr); + int parentJointIndex = 0; + if (propMap["parentJointIndex"].isValid()) { + parentJointIndex = propMap["parentJointIndex"].toInt(); + } + if (success && !nestablePointer.expired()) { + return std::make_shared(nestablePointer, parentJointIndex); + } + } + + unsigned int pickID = propMap["parentID"].toUInt(); + if (pickID != 0) { + return std::make_shared(pickID); + } + } + + if (propMap["joint"].isValid()) { + QString joint = propMap["joint"].toString(); + if (joint == "Mouse") { + return std::make_shared(); + } else if (joint == "Avatar") { + return std::make_shared(); + } else if (!joint.isNull()) { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + int jointIndex = myAvatar->getJointIndex(joint); + return std::make_shared(myAvatar, jointIndex); + } + } + + return std::shared_ptr(); +} \ No newline at end of file diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 1cad4abf7c..717436a13b 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -320,6 +320,9 @@ public slots: * @returns {number} */ static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; } + +protected: + static std::shared_ptr createTransformNode(const QVariantMap& propMap); }; #endif // hifi_PickScriptingInterface_h diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 96b41dcc72..736d3c1760 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -50,6 +50,18 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) { return std::make_shared(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick); } +Transform RayPick::getResultTransform() const { + PickResultPointer result = getPrevPickResult(); + if (!result) { + return Transform(); + } + + auto rayResult = std::static_pointer_cast(result); + Transform transform; + transform.setTranslation(rayResult->intersection); + return transform; +} + glm::vec3 RayPick::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat& rotation, const glm::vec3& registration) { // TODO: take into account registration glm::vec3 n = rotation * Vectors::FRONT; diff --git a/interface/src/raypick/RayPick.h b/interface/src/raypick/RayPick.h index 9410d39c1a..11f985cec2 100644 --- a/interface/src/raypick/RayPick.h +++ b/interface/src/raypick/RayPick.h @@ -77,6 +77,7 @@ public: PickResultPointer getOverlayIntersection(const PickRay& pick) override; PickResultPointer getAvatarIntersection(const PickRay& pick) override; PickResultPointer getHUDIntersection(const PickRay& pick) override; + Transform getResultTransform() const override; // These are helper functions for projecting and intersecting rays static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction); diff --git a/interface/src/raypick/StylusPick.cpp b/interface/src/raypick/StylusPick.cpp index 9021c922a6..69f605e7f9 100644 --- a/interface/src/raypick/StylusPick.cpp +++ b/interface/src/raypick/StylusPick.cpp @@ -225,4 +225,16 @@ PickResultPointer StylusPick::getAvatarIntersection(const StylusTip& pick) { PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) { return std::make_shared(pick.toVariantMap()); +} + +Transform StylusPick::getResultTransform() const { + PickResultPointer result = getPrevPickResult(); + if (!result) { + return Transform(); + } + + auto stylusResult = std::static_pointer_cast(result); + Transform transform; + transform.setTranslation(stylusResult->intersection); + return transform; } \ No newline at end of file diff --git a/interface/src/raypick/StylusPick.h b/interface/src/raypick/StylusPick.h index f19e343f8d..ca80e9fbea 100644 --- a/interface/src/raypick/StylusPick.h +++ b/interface/src/raypick/StylusPick.h @@ -66,6 +66,7 @@ public: PickResultPointer getOverlayIntersection(const StylusTip& pick) override; PickResultPointer getAvatarIntersection(const StylusTip& pick) override; PickResultPointer getHUDIntersection(const StylusTip& pick) override; + Transform getResultTransform() const override; bool isLeftHand() const override { return _side == Side::Left; } bool isRightHand() const override { return _side == Side::Right; } diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 1a48aac0ef..099a791407 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -17,7 +17,7 @@ #include #include -#include +#include enum IntersectionType { NONE = 0, @@ -214,6 +214,10 @@ public: virtual bool isRightHand() const { return false; } virtual bool isMouse() const { return false; } + virtual Transform getResultTransform() const = 0; + + std::shared_ptr parentTransform; + private: PickFilter _filter; const float _maxDistance; diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 3c8ee2ed7d..f1c62a8d16 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -258,8 +258,11 @@ public: * @typedef {object} CollisionPick * @property {Shape} shape - The information about the collision region's size and shape. -* @property {Vec3} position - The position of the collision region. -* @property {Quat} orientation - The orientation of the collision region. +* @property {Vec3} position - The position of the collision region, relative to a parent if defined. +* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. +* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay. +* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) +* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Head," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. */ class CollisionRegion : public MathPick { public: @@ -305,6 +308,15 @@ public: if (pickVariant["orientation"].isValid()) { transform.setRotation(quatFromVariant(pickVariant["orientation"])); } + if (pickVariant["parentID"].isValid()) { + parentID = pickVariant["parentID"].toString(); + } + if (pickVariant["parentJointIndex"].isValid()) { + parentJointIndex = pickVariant["parentJointIndex"].toInt(); + } + if (pickVariant["joint"].isValid()) { + joint = pickVariant["joint"].toString(); + } } QVariantMap toVariantMap() const override { @@ -320,6 +332,14 @@ public: collisionRegion["position"] = vec3toVariant(transform.getTranslation()); collisionRegion["orientation"] = quatToVariant(transform.getRotation()); + if (!parentID.isNull()) { + collisionRegion["parentID"] = parentID; + } + collisionRegion["parentJointIndex"] = parentJointIndex; + if (!joint.isNull()) { + collisionRegion["joint"] = joint; + } + return collisionRegion; } @@ -354,6 +374,11 @@ public: // We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick std::shared_ptr shapeInfo = std::make_shared(); Transform transform; + + // Parenting information + QUuid parentID; + int parentJointIndex = 0; + QString joint; }; namespace std { From cce62f4d928c703a506f53ca26a62ac85d74620f Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 20 Aug 2018 16:59:03 -0700 Subject: [PATCH 049/106] Do not assume picks with doesIntersect()==false have no results to compare. Fixes CollisionPickResult 'loaded' value always evaluating to false when not colliding --- interface/src/raypick/CollisionPick.h | 1 + libraries/pointers/src/Pick.h | 1 + libraries/pointers/src/PickCacheOptimizer.h | 10 +++++----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 12d31111d1..7ff3670e53 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -53,6 +53,7 @@ public: QVariantMap toVariantMap() const override; bool doesIntersect() const override { return intersects; } + bool needsToCompareResults() const override { return true; } bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return true; } PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override; diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 099a791407..9c4de6c46e 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -115,6 +115,7 @@ public: } virtual bool doesIntersect() const = 0; + virtual bool needsToCompareResults() const { return doesIntersect(); } // for example: if we want the closest result, compare based on distance // if we want all results, combine them diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index 49a039935c..ae515fc8d7 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -57,8 +57,8 @@ bool PickCacheOptimizer::checkAndCompareCachedResults(T& pick, PickCache& cac } template -void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr> pick) { - if (intersects) { +void PickCacheOptimizer::cacheResult(const bool needToCompareResults, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr> pick) { + if (needToCompareResults) { cache[mathPick][key] = resTemp; res = res->compareAndProcessNewResult(resTemp); } else { @@ -92,7 +92,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetEntityIntersection(mathematicalPick); if (entityRes) { - cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick); + cacheResult(entityRes->needsToCompareResults(), entityRes, entityKey, res, mathematicalPick, results, pick); } } } @@ -102,7 +102,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetOverlayIntersection(mathematicalPick); if (overlayRes) { - cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick); + cacheResult(overlayRes->needsToCompareResults(), overlayRes, overlayKey, res, mathematicalPick, results, pick); } } } @@ -112,7 +112,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetAvatarIntersection(mathematicalPick); if (avatarRes) { - cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick); + cacheResult(avatarRes->needsToCompareResults(), avatarRes, avatarKey, res, mathematicalPick, results, pick); } } } From 2bec8678639e2aff14f83063be6e2154c34ee983 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 24 Aug 2018 16:29:08 -0700 Subject: [PATCH 050/106] added the sanity check to see if the shoulders have flipped --- interface/src/avatar/MyAvatar.cpp | 19 ++++++++++++++++--- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a1e30144ee..ae02eacf75 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -458,9 +458,11 @@ void MyAvatar::update(float deltaTime) { glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); glm::vec2 normedHandHip = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHip, _headControllerFacing, 0.5f); - //qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; + // if head facing dot mid point hands facing is close to -1 and the hands midpoint is close to -1 you then flip + + // qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); - qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; + // qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { _rotationChanged = usecTimestampNow(); @@ -831,11 +833,22 @@ void MyAvatar::computeHandAzimuth() { // qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + vec2 oldAzimuthReading = _hipToHandController; _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), 0.5f); + + //if they are 180 apart + float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); + float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); + if (cosBackwardAngle > cosForwardAngle) { + // this means we have lost continuity with the current chest position + _hipToHandController = -_hipToHandController; + } + + //need to use easing function here. //var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0) / 3.0) * leftRightMidpoint; } else { - _hipToHandController = glm::vec2(0.0f, 1.0f); + // _hipToHandController = glm::vec2(0.0f, -1.0f); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 177b453267..ea5059bf39 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1663,7 +1663,7 @@ private: glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; - glm::vec2 _hipToHandController; // spine2 facing vector in xz plane (avatar space) + glm::vec2 _hipToHandController { 0.0f, -1.0f }; // spine2 facing vector in xz plane (avatar space) float _currentStandingHeight { 0.0f }; bool _resetMode { true }; From 8ede6f1cd09cf2ed33a006fa9629dae134fe0d5f Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 27 Aug 2018 06:55:52 -0700 Subject: [PATCH 051/106] Fix removing my avatar and code standards --- libraries/avatars/src/AvatarData.cpp | 16 ++++----------- libraries/avatars/src/AvatarHashMap.cpp | 26 ++++++++++++------------- libraries/avatars/src/AvatarHashMap.h | 4 ++-- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ddb53f8acb..5e1094276c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -919,19 +919,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - const float SPACE_BETWEEN_AVATARS = 2.0f; - const int RANDOM_RADIUS = 100; - const int AVATARS_PER_ROW = 3; + glm::vec3 offset = glm::vec3(0.0f, 0.0f, 0.0f); - glm::vec3 offset; - - if (false) { - qsrand(static_cast(getID().toByteArray().toInt())); - float xrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); - float yrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); - offset = glm::vec3(xrand, 0.0f, yrand); - } - else { + if (_replicaIndex > 0) { + const float SPACE_BETWEEN_AVATARS = 2.0f; + const int AVATARS_PER_ROW = 3; int row = _replicaIndex % AVATARS_PER_ROW; int col = floor(_replicaIndex / AVATARS_PER_ROW); offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 67cc9f0563..8dfee3551e 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -23,6 +23,9 @@ void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) { + if (parentID == QUuid()) { + return; + } if (_replicasMap.find(parentID) == _replicasMap.end()) { std::vector emptyReplicas = std::vector(); _replicasMap.insert(std::pair>(parentID, emptyReplicas)); @@ -144,10 +147,12 @@ void AvatarHashMap::setReplicaCount(int count) { auto avatars = getAvatarIdentifiers(); for (int i = 0; i < avatars.size(); i++) { KillAvatarReason reason = KillAvatarReason::NoReason; - removeAvatar(avatars[i], reason); - auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); - for (auto id : replicaIDs) { - removeAvatar(id, reason); + if (avatars[i] != QUuid()) { + removeAvatar(avatars[i], reason); + auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); + for (auto id : replicaIDs) { + removeAvatar(id, reason); + } } } } @@ -315,7 +320,6 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess AvatarTraits::TraitWireSize traitBinarySize; bool skipBinaryTrait = false; - if (AvatarTraits::isSimpleTrait(traitType)) { message->readPrimitive(&traitBinarySize); @@ -325,12 +329,10 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess avatar->processTrait(traitType, traitData); _replicas.processTrait(avatarID, traitType, traitData); lastProcessedVersions[traitType] = packetTraitVersion; - } - else { + } else { skipBinaryTrait = true; } - } - else { + } else { AvatarTraits::TraitInstanceID traitInstanceID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -341,15 +343,13 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { avatar->processDeletedTraitInstance(traitType, traitInstanceID); _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); - } - else { + } else { auto traitData = message->read(traitBinarySize); avatar->processTraitInstance(traitType, traitInstanceID, traitData); _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData); } processedInstanceVersion = packetTraitVersion; - } - else { + } else { skipBinaryTrait = true; } } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index e4e197a080..0f847b2a61 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -43,7 +43,7 @@ class AvatarReplicas { public: - AvatarReplicas() : _replicaCount(0) {} + AvatarReplicas() {} void addReplica(const QUuid& parentID, AvatarSharedPointer replica); std::vector getReplicaIDs(const QUuid& parentID); void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); @@ -58,7 +58,7 @@ public: private: std::map> _replicasMap; - int _replicaCount; + int _replicaCount { 0 }; }; From 62814deb328201ba6fe3a122514c9fbc5d61a730 Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Mon, 27 Aug 2018 08:34:01 -0700 Subject: [PATCH 052/106] Added read of map_d from MTL files --- libraries/fbx/src/OBJReader.cpp | 31 +++++++++++++++++++++---------- libraries/fbx/src/OBJReader.h | 2 ++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index caac08f777..501fb72130 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -263,15 +263,19 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { default: materials[matName] = currentMaterial; #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel << - " shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << - " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << - currentMaterial.specularColor << " emissive color:" << currentMaterial.emissiveColor << - " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << - currentMaterial.specularTextureFilename << " emissive texture:" << - currentMaterial.emissiveTextureFilename << " bump texture:" << - currentMaterial.bumpTextureFilename; - #endif + qCDebug(modelformat) << + "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel << + " shininess:" << currentMaterial.shininess << + " opacity:" << currentMaterial.opacity << + " diffuse color:" << currentMaterial.diffuseColor << + " specular color:" << currentMaterial.specularColor << + " emissive color:" << currentMaterial.emissiveColor << + " diffuse texture:" << currentMaterial.diffuseTextureFilename << + " specular texture:" << currentMaterial.specularTextureFilename << + " emissive texture:" << currentMaterial.emissiveTextureFilename << + " bump texture:" << currentMaterial.bumpTextureFilename; + " opacity texture:" << currentMaterial.opacityTextureFilename; +#endif return; } QByteArray token = tokenizer.getDatum(); @@ -289,6 +293,8 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.emissiveTextureFilename = ""; currentMaterial.specularTextureFilename = ""; currentMaterial.bumpTextureFilename = ""; + currentMaterial.opacityTextureFilename = ""; + } else if (token == "Ns") { currentMaterial.shininess = tokenizer.getFloat(); } else if (token == "Ni") { @@ -321,7 +327,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { currentMaterial.emissiveColor = tokenizer.getVec3(); } else if (token == "Ks") { currentMaterial.specularColor = tokenizer.getVec3(); - } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) { + } else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump") || (token == "map_d")) { const QByteArray textureLine = tokenizer.getLineAsDatum(); QByteArray filename; OBJMaterialTextureOptions textureOptions; @@ -341,6 +347,8 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } else if ((token == "map_bump") || (token == "bump")) { currentMaterial.bumpTextureFilename = filename; currentMaterial.bumpTextureOptions = textureOptions; + } else if (token == "map_d") { + currentMaterial.opacityTextureFilename = filename; } } } @@ -900,6 +908,9 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m fbxMaterial.normalTexture.isBumpmap = true; fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier; } + if (!objMaterial.opacityTextureFilename.isEmpty()) { + fbxMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename; + } modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 13ddc6e21c..e432a3ea51 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -66,6 +66,8 @@ public: QByteArray specularTextureFilename; QByteArray emissiveTextureFilename; QByteArray bumpTextureFilename; + QByteArray opacityTextureFilename; + OBJMaterialTextureOptions bumpTextureOptions; int illuminationModel; bool used { false }; From 51683b0b535f5e987c2a786a325c4e3640d99d41 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 27 Aug 2018 10:16:09 -0700 Subject: [PATCH 053/106] Remove storage of parenting information from CollisionRegion --- libraries/shared/src/RegisteredMetaTypes.h | 27 +--------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index f1c62a8d16..a7f9b72ed2 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -271,10 +271,7 @@ public: CollisionRegion(const CollisionRegion& collisionRegion) : modelURL(collisionRegion.modelURL), shapeInfo(std::make_shared()), - transform(collisionRegion.transform), - parentID(collisionRegion.parentID), - parentJointIndex(collisionRegion.parentJointIndex), - joint(collisionRegion.joint) + transform(collisionRegion.transform) { shapeInfo->setParams(collisionRegion.shapeInfo->getType(), collisionRegion.shapeInfo->getHalfExtents(), collisionRegion.modelURL.toString()); } @@ -308,15 +305,6 @@ public: if (pickVariant["orientation"].isValid()) { transform.setRotation(quatFromVariant(pickVariant["orientation"])); } - if (pickVariant["parentID"].isValid()) { - parentID = pickVariant["parentID"].toString(); - } - if (pickVariant["parentJointIndex"].isValid()) { - parentJointIndex = pickVariant["parentJointIndex"].toInt(); - } - if (pickVariant["joint"].isValid()) { - joint = pickVariant["joint"].toString(); - } } QVariantMap toVariantMap() const override { @@ -332,14 +320,6 @@ public: collisionRegion["position"] = vec3toVariant(transform.getTranslation()); collisionRegion["orientation"] = quatToVariant(transform.getRotation()); - if (!parentID.isNull()) { - collisionRegion["parentID"] = parentID; - } - collisionRegion["parentJointIndex"] = parentJointIndex; - if (!joint.isNull()) { - collisionRegion["joint"] = joint; - } - return collisionRegion; } @@ -374,11 +354,6 @@ public: // We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick std::shared_ptr shapeInfo = std::make_shared(); Transform transform; - - // Parenting information - QUuid parentID; - int parentJointIndex = 0; - QString joint; }; namespace std { From 945982b1bdfa576ab07a2e8cbb416ed1f34c44f3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 11 Jul 2018 14:05:07 -0700 Subject: [PATCH 054/106] Working on pauses in tomb domain --- libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp | 1 - libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 01424c2ac6..c1848d99b1 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -765,7 +765,6 @@ void GLBackend::recycle() const { } _textureManagement._transferEngine->manageMemory(); - Texture::KtxStorage::releaseOpenKtxFiles(); } void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp index eaee054b7d..f4e81448cc 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp @@ -405,6 +405,7 @@ bool GLTextureTransferEngineDefault::processActiveBufferQueue() { _activeTransferQueue.splice(_activeTransferQueue.end(), activeBufferQueue); } + Texture::KtxStorage::releaseOpenKtxFiles(); return true; } From 397b03d5d50fec11ed9a84c4e25d63090a317f28 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 27 Aug 2018 14:12:24 -0700 Subject: [PATCH 055/106] Add threshold parameter to collision pick with minimum of 0 --- interface/src/raypick/CollisionPick.cpp | 4 ++-- libraries/physics/src/PhysicsEngine.cpp | 14 ++++++++++---- libraries/physics/src/PhysicsEngine.h | 2 +- libraries/shared/src/RegisteredMetaTypes.h | 19 +++++++++++++++---- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 8c3d28fdfd..407595b86a 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -391,7 +391,7 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } - auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); + auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); filterIntersections(entityIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector()); } @@ -406,7 +406,7 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi return std::make_shared(pick, CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } - auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); + auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); filterIntersections(avatarIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector(), avatarIntersections); } diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index d5ce60bef9..58c197d6f4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -898,11 +898,12 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) { } struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { - AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) : + AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject, float threshold) : btCollisionWorld::ContactResultCallback(), collisionObject(), contacts(), - myAvatarCollisionObject(myAvatarCollisionObject) { + myAvatarCollisionObject(myAvatarCollisionObject), + threshold(threshold) { const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); collisionObject.setCollisionShape(const_cast(collisionShape)); @@ -924,8 +925,13 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { btCollisionObject collisionObject; std::vector contacts; btCollisionObject* myAvatarCollisionObject; + btScalar threshold; btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override { + if (cp.m_distance1 > -threshold) { + return 0; + } + const btCollisionObject* otherBody; btVector3 penetrationPoint; btVector3 otherPenetrationPoint; @@ -968,14 +974,14 @@ protected: } }; -std::vector PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const { +std::vector PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group, float threshold) const { // TODO: Give MyAvatar a motion state so we don't have to do this btCollisionObject* myAvatarCollisionObject = nullptr; if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) { myAvatarCollisionObject = _myAvatarController->getCollisionObject(); } - auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject); + auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject, threshold); _dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback); return contactCallback.contacts; diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 317f7ef312..09b68d9a8b 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -142,7 +142,7 @@ public: // Function for getting colliding objects in the world of specified type // See PhysicsCollisionGroups.h for mask flags. - std::vector contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; + std::vector contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC, float threshold = 0.0f) const; private: QList removeDynamicsForBody(btRigidBody* body); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index a7f9b72ed2..312e017dc8 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -254,12 +254,13 @@ public: }; /**jsdoc -* A CollisionPick defines a volume for checking collisions in the physics simulation. +* A CollisionRegion defines a volume for checking collisions in the physics simulation. -* @typedef {object} CollisionPick +* @typedef {object} CollisionRegion * @property {Shape} shape - The information about the collision region's size and shape. * @property {Vec3} position - The position of the collision region, relative to a parent if defined. * @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined. +* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region. * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay. * @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) * @property {string} joint - If "Mouse," parents the pick to the mouse. If "Head," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. @@ -269,6 +270,7 @@ public: CollisionRegion() { } CollisionRegion(const CollisionRegion& collisionRegion) : + threshold(collisionRegion.threshold), modelURL(collisionRegion.modelURL), shapeInfo(std::make_shared()), transform(collisionRegion.transform) @@ -299,6 +301,10 @@ public: } } + if (pickVariant["threshold"].isValid()) { + threshold = glm::max(0.0f, pickVariant["threshold"].toFloat()); + } + if (pickVariant["position"].isValid()) { transform.setTranslation(vec3FromVariant(pickVariant["position"])); } @@ -317,6 +323,8 @@ public: collisionRegion["shape"] = shape; + collisionRegion["threshold"] = threshold; + collisionRegion["position"] = vec3toVariant(transform.getTranslation()); collisionRegion["orientation"] = quatToVariant(transform.getRotation()); @@ -324,13 +332,15 @@ public: } operator bool() const override { - return !(glm::any(glm::isnan(transform.getTranslation())) || + return !std::isnan(threshold) && + !(glm::any(glm::isnan(transform.getTranslation())) || glm::any(glm::isnan(transform.getRotation())) || shapeInfo->getType() == SHAPE_TYPE_NONE); } bool operator==(const CollisionRegion& other) const { - return glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) && + return threshold == other.threshold && + glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) && glm::all(glm::equal(transform.getRotation(), other.transform.getRotation())) && glm::all(glm::equal(transform.getScale(), other.transform.getScale())) && shapeInfo->getType() == other.shapeInfo->getType() && @@ -354,6 +364,7 @@ public: // We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick std::shared_ptr shapeInfo = std::make_shared(); Transform transform; + float threshold; }; namespace std { From 055f03878e1879f0b461e909e54d1cb1877379df Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 27 Aug 2018 14:44:27 -0700 Subject: [PATCH 056/106] made changes to add roll to the spine2 rotation. also only the shoulders control the rotation recenter now --- interface/src/avatar/MyAvatar.cpp | 70 +++++++++--------------- interface/src/avatar/MyAvatar.h | 4 +- interface/src/avatar/MySkeletonModel.cpp | 17 ++++-- libraries/shared/src/AvatarConstants.h | 2 +- 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6012bc4419..c4d855ee77 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -447,6 +447,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { void MyAvatar::update(float deltaTime) { // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); + const float PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH = 0.0f; // 100 percent shoulders float tau = deltaTime / HMD_FACING_TIMESCALE; @@ -456,13 +457,13 @@ void MyAvatar::update(float deltaTime) { glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); - glm::vec2 normedHandHip = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHip, _headControllerFacing, 0.5f); - // if head facing dot mid point hands facing is close to -1 and the hands midpoint is close to -1 you then flip - - // qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; - _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); - // qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; + glm::vec2 normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); + if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); + } else { + _headControllerFacingMovingAverage = _headControllerFacing; + } if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { _rotationChanged = usecTimestampNow(); @@ -477,7 +478,6 @@ void MyAvatar::update(float deltaTime) { computeHandAzimuth(); - #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE if (_drawAverageFacingEnabled) { auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); @@ -488,13 +488,7 @@ void MyAvatar::update(float deltaTime) { DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); // draw hand azimuth vector - glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); - glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); - glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); - //qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); - DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); - DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } #endif @@ -502,7 +496,11 @@ void MyAvatar::update(float deltaTime) { if (_goToPending) { setWorldPosition(_goToPosition); setWorldOrientation(_goToOrientation); - _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average + if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { + _headControllerFacingMovingAverage = headFacingPlusHandHipAzimuthMix; + } else { + _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average + } _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). @@ -824,31 +822,29 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } } +// Find the vector halfway between the hip to hand azimuth vectors +// This midpoint hand azimuth is in Avatar space void MyAvatar::computeHandAzimuth() { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); glm::vec3 localLookat(0.0f, 0.0f, 1.0f); - // glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); - // glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); + const float HALFWAY = 0.50f; - // qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + // we need the old azimuth reading to prevent flipping the facing direction 180 + // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. vec2 oldAzimuthReading = _hipToHandController; - _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), 0.5f); - - //if they are 180 apart + _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); + + // check the angular distance from forward and back float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); + // if we are now closer to the 180 flip of the previous chest forward + // then we negate our computed _hipToHandController to keep the chest in the same direction. if (cosBackwardAngle > cosForwardAngle) { // this means we have lost continuity with the current chest position _hipToHandController = -_hipToHandController; } - - - //need to use easing function here. - //var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0) / 3.0) * leftRightMidpoint; - } else { - // _hipToHandController = glm::vec2(0.0f, -1.0f); } } @@ -3382,11 +3378,9 @@ glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; glm::vec3 hipToHandRigSpace = RIG_CHANGE_OF_BASIS * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); - //to do: check for zero before normalizing. glm::vec3 u, v, w; generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); - return spine2RigSpace; } @@ -3867,7 +3861,7 @@ void MyAvatar::lateUpdatePalms() { } -static const float FOLLOW_TIME = 0.1f; +static const float FOLLOW_TIME = 0.5f; MyAvatar::FollowHelper::FollowHelper() { deactivate(); @@ -3972,11 +3966,6 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head")); glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); - //if (!isActive(Horizontal) && - // (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + DEFAULT_AVATAR_SPINE_STRETCH_LIMIT))) { - // myAvatar.setResetMode(true); - // stepDetected = true; - //} if (!isActive(Horizontal) && (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance * myAvatar.getAvatarScale())))) { myAvatar.setResetMode(true); @@ -3997,23 +3986,15 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { - const float VELOCITY_THRESHHOLD = 1.0f; - float currentVelocity = glm::length(myAvatar.getLocalVelocity() / myAvatar.getSensorToWorldScale()); - if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { - //if (!isActive(Rotation) && getForceActivateRotation()) { - // activate(Rotation); - // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); - // setForceActivateRotation(false); - //} if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } if (myAvatar.getCenterOfGravityModelEnabled()) { - if ((!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) || (isActive(Horizontal) && (currentVelocity > VELOCITY_THRESHHOLD))) { + if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); @@ -4029,7 +4010,6 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } } } - if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Vertical); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ea5059bf39..f819c417de 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -285,7 +285,7 @@ public: Q_INVOKABLE void centerBody(); // thread-safe - /**jsdoc + /**jsdoc_hmdSensorMatrix * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent * contorted joints. * @function MyAvatar.clearIKJointLimitHistory @@ -914,6 +914,8 @@ public: virtual void rebuildCollisionShape() override; + const glm::vec2& getHeadControllerFacing() const { return _headControllerFacing; } + void setHeadControllerFacing(glm::vec2 currentHeadControllerFacing) { _headControllerFacing = currentHeadControllerFacing; } const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; } void setHeadControllerFacingMovingAverage(glm::vec2 currentHeadControllerFacing) { _headControllerFacingMovingAverage = currentHeadControllerFacing; } float getCurrentStandingHeight() const { return _currentStandingHeight; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 158f9bb0b6..f150379f0c 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -238,13 +238,20 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; - - // spine 2 hack to be improved. + // set spine2 if we have hand controllers AnimPose currentPose; bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"),currentPose); - AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); - currentPose.rot() = newSpinePose.rot(); - //newSpinePose.rot() = myAvatar->getSpine2RotationRigSpace();// *newSpinePose.rot(); + AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); + glm::vec3 u, v, w; + glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); + glm::vec3 up = currentPose.rot() * glm::vec3(0.0f, 1.0f, 0.0f); + generateBasisVectors(up, fwd, u, v, w); + AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); + + if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) { + AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); + currentPose.rot() = newSpinePose.rot(); + } params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentPose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index b97f586d32..c3e8a3f173 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -34,7 +34,7 @@ const float DEFAULT_HANDS_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 3.3f; const float DEFAULT_HEAD_VELOCITY_STEPPING_THRESHOLD = 0.18f; const float DEFAULT_HEAD_PITCH_STEPPING_TOLERANCE = 7.0f; const float DEFAULT_HEAD_ROLL_STEPPING_TOLERANCE = 7.0f; -const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.05f; +const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.04f; const float DEFAULT_AVATAR_FORWARD_DAMPENING_FACTOR = 0.5f; const float DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR = 2.0f; const float DEFAULT_AVATAR_HIPS_MASS = 40.0f; From b6edb2c9d6afcc07d266b45fa63b3536e35469eb Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 14:53:13 -0700 Subject: [PATCH 057/106] platform states, edit controller mapping, separate mac/win delete --- interface/src/Application.cpp | 27 ++++++++++++++++++- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 ++ scripts/system/edit.js | 17 +++++++++--- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e290531471..12aed213c9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -726,6 +726,9 @@ static const QString STATE_SNAP_TURN = "SnapTurn"; static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; +static const QString STATE_PLATFORM_WINDOWS = "PlatformWindows"; +static const QString STATE_PLATFORM_MAC = "PlatformMac"; +static const QString STATE_PLATFORM_ANDROID = "PlatformAndroid"; // Statically provided display and input plugins extern DisplayPluginList getDisplayPlugins(); @@ -909,7 +912,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, - STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED } }); + STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, + STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_WINDOWS } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1683,6 +1687,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_NAV_FOCUSED, []() -> float { return DependencyManager::get()->navigationFocused() ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_WINDOWS, []() -> float { +#if defined(Q_OS_WIN) + return 1; +#else + return 0; +#endif + }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_MAC, []() -> float { +#if defined(Q_OS_MAC) + return 1; +#else + return 0; +#endif + }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float { +#if defined(Q_OS_ANDROID) + return 1; +#else + return 0; +#endif + }); // Setup the _keyboardMouseDevice, _touchscreenDevice, _touchscreenVirtualPadDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 650c9675a7..ddd51e9b9b 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -302,6 +302,8 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index adee7c6236..776c09addd 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -20,6 +20,8 @@ var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton"; +var CONTROLLER_MAPPING_NAME = "com.highfidelity.editMode"; + Script.include([ "libraries/stringHelpers.js", "libraries/dataViewHelpers.js", @@ -860,6 +862,7 @@ var toolBar = (function () { cameraManager.disable(); selectionDisplay.triggerMapping.disable(); tablet.landscape = false; + Controller.disableMapping(CONTROLLER_MAPPING_NAME); } else { if (shouldUseEditTabletApp()) { tablet.loadQMLSource("hifi/tablet/Edit.qml", true); @@ -876,6 +879,7 @@ var toolBar = (function () { selectionDisplay.triggerMapping.enable(); print("starting tablet in landscape mode"); tablet.landscape = true; + Controller.enableMapping(CONTROLLER_MAPPING_NAME); // Not sure what the following was meant to accomplish, but it currently causes // everybody else to think that Interface has lost focus overall. fogbugzid:558 // Window.setFocus(); @@ -1859,9 +1863,7 @@ var keyReleaseEvent = function (event) { cameraManager.keyReleaseEvent(event); } // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.text === "DELETE") { - deleteSelectedEntities(); - } else if (event.text === 'd' && event.isControl) { + if (event.text === 'd' && event.isControl) { selectionManager.clearSelections(); } else if (event.text === "t") { selectionDisplay.toggleSpaceMode(); @@ -1893,6 +1895,15 @@ var keyReleaseEvent = function (event) { Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); +function deleteKey(value) { + if (value === 0) { // on release + deleteSelectedEntities(); + } +} +var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).peek().to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).peek().to(deleteKey); + function recursiveAdd(newParentID, parentData) { if (parentData.children !== undefined) { var children = parentData.children; From aae06e8f49d2c0ecf33ccd8eb343992cb5d6379f Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 27 Aug 2018 15:40:33 -0700 Subject: [PATCH 058/106] faster aabox ray intersection and pre-computed inverse direction --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Volume3DOverlay.cpp | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 4 +- libraries/entities/src/EntityTree.cpp | 5 +- libraries/entities/src/EntityTreeElement.cpp | 2 +- libraries/render-utils/src/Model.cpp | 9 +- libraries/render-utils/src/PickItemsJob.cpp | 3 +- libraries/shared/src/AABox.cpp | 6 +- libraries/shared/src/AABox.h | 2 +- libraries/shared/src/AACube.cpp | 6 +- libraries/shared/src/AACube.h | 6 +- libraries/shared/src/GeometryUtil.cpp | 89 +++++++------------ libraries/shared/src/GeometryUtil.h | 4 +- libraries/shared/src/TriangleSet.cpp | 16 ++-- libraries/shared/src/TriangleSet.h | 4 +- 15 files changed, 69 insertions(+), 91 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 789b1f9969..ba9a1f9fc9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -180,7 +180,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distance; BoxFace face; glm::vec3 normal; - boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + boundingBox.findRayIntersection(cameraPosition, direction, 1.0f / direction, distance, face, normal); float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; if (event.getID() == 1) { // "1" is left hand offsetAngle *= -1.0f; diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index c87650a77b..a307d445c0 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -88,7 +88,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve // we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame // and testing intersection there. - bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal); + bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, 1.0f / overlayFrameDirection, distance, face, surfaceNormal); if (hit) { surfaceNormal = transform.getRotation() * surfaceNormal; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index af8d4fac86..2da5c30dc0 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -596,8 +596,8 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o voxelBox += result3 - Vectors::HALF; voxelBox += result3 + Vectors::HALF; - glm::vec4 directionInVoxel = wtvMatrix * glm::vec4(direction, 0.0f); - return voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), + glm::vec3 directionInVoxel = vec3(wtvMatrix * glm::vec4(direction, 0.0f)); + return voxelBox.findRayIntersection(glm::vec3(originInVoxel), directionInVoxel, 1.0f / directionInVoxel, distance, face, surfaceNormal); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1cf6e45d5b..a7c88ddc7d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -48,6 +48,7 @@ public: // Inputs glm::vec3 origin; glm::vec3 direction; + glm::vec3 invDirection; const QVector& entityIdsToInclude; const QVector& entityIdsToDiscard; bool visibleOnly; @@ -847,7 +848,7 @@ float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* ex float boundDistance = FLT_MAX; BoxFace face; glm::vec3 surfaceNormal; - if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, boundDistance, face, surfaceNormal)) { + if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, args->invDirection, boundDistance, face, surfaceNormal)) { // Don't add this cell if it's already farther than our best distance so far if (boundDistance < args->distance) { distance = boundDistance; @@ -863,7 +864,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm: OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType, bool* accurateResult) { - RayArgs args = { origin, direction, entityIdsToInclude, entityIdsToDiscard, + RayArgs args = { origin, direction, 1.0f / direction, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() }; distance = FLT_MAX; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index b7288755a6..efe5dafccf 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -215,7 +215,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori float localDistance; BoxFace localFace; glm::vec3 localSurfaceNormal; - if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, + if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, 1.0f / entityFrameDirection, localDistance, localFace, localSurfaceNormal)) { if (entityFrameBox.contains(entityFrameOrigin) || localDistance < distance) { // now ask the entity if we actually intersect diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ae3ee6e9a2..18ee05ddd4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -376,7 +376,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g // we can use the AABox's intersection by mapping our origin and direction into the model frame // and testing intersection there. - if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { + if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, 1.0f / modelFrameDirection, distance, face, surfaceNormal)) { QMutexLocker locker(&_mutex); float bestDistance = FLT_MAX; @@ -400,6 +400,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f)); glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); + glm::vec3 meshFrameInvDirection = 1.0f / meshFrameDirection; int shapeID = 0; int subMeshIndex = 0; @@ -415,8 +416,8 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g float partBoundDistance = FLT_MAX; BoxFace partBoundFace; glm::vec3 partBoundNormal; - if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, partBoundDistance, - partBoundFace, partBoundNormal)) { + if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, + partBoundDistance, partBoundFace, partBoundNormal)) { priority = partBoundDistance; } } @@ -444,7 +445,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g float triangleSetDistance = FLT_MAX; BoxFace triangleSetFace; Triangle triangleSetTriangle; - if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, + if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { if (triangleSetDistance < bestDistance) { bestDistance = triangleSetDistance; diff --git a/libraries/render-utils/src/PickItemsJob.cpp b/libraries/render-utils/src/PickItemsJob.cpp index 860262a969..f41222d358 100644 --- a/libraries/render-utils/src/PickItemsJob.cpp +++ b/libraries/render-utils/src/PickItemsJob.cpp @@ -33,6 +33,7 @@ void PickItemsJob::run(const render::RenderContextPointer& renderContext, const render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const { const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); + const glm::vec3 rayInvDirection = 1.0f / rayDirection; BoxFace face; glm::vec3 normal; float isectDistance; @@ -42,7 +43,7 @@ render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPoint render::ItemKey itemKey; for (const auto& itemBound : inputs) { - if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { + if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, rayInvDirection, isectDistance, face, normal)) { auto& item = renderContext->_scene->getItem(itemBound.id); itemKey = item.getKey(); if (itemKey.isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistance= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE; - surfaceNormal = glm::vec3(direction.x > 0 ? 1.0f : -1.0f, 0.0f, 0.0f); - return true; - } - if ((findInsideOutIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE; - surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? 1.0f : -1.0f, 0.0f); - return true; - } - if ((findInsideOutIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) { - distance = axisDistance; - face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE; - surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? 1.0f : -1.0f); - return true; - } - // This case is unexpected, but mimics the previous behavior for inside out intersections - distance = 0; - return true; +// https://tavianator.com/fast-branchless-raybounding-box-intersections/ +bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float t1, t2, newTmin, newTmax, tmin = -INFINITY, tmax = INFINITY; + int minAxis = -1, maxAxis = -1; + + for (int i = 0; i < 3; ++i) { + t1 = (corner[i] - origin[i]) * invDirection[i]; + t2 = (corner[i] + scale[i] - origin[i]) * invDirection[i]; + + newTmin = glm::min(t1, t2); + newTmax = glm::max(t1, t2); + + minAxis = newTmin > tmin ? i : minAxis; + tmin = glm::max(tmin, newTmin); + maxAxis = newTmax < tmax ? i : maxAxis; + tmax = glm::min(tmax, newTmax); } - // check each axis - float axisDistance; - if ((findIntersection(origin.x, direction.x, corner.x, scale.x, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; - surfaceNormal = glm::vec3(direction.x > 0 ? -1.0f : 1.0f, 0.0f, 0.0f); - return true; - } - if ((findIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; - surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? -1.0f : 1.0f, 0.0f); - return true; - } - if ((findIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) { - distance = axisDistance; - face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; - surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? -1.0f : 1.0f); + bool inside = tmin < 0.0f; + if (tmax >= glm::max(tmin, 0.0f)) { + if (inside) { + distance = tmax; + bool positiveDirection = direction[maxAxis] > 0.0f; + surfaceNormal = glm::vec3(0.0f); + surfaceNormal[maxAxis] = positiveDirection ? -1.0f : 1.0f; + face = positiveDirection ? BoxFace(2 * maxAxis + 1) : BoxFace(2 * maxAxis); + } else { + distance = tmin; + bool positiveDirection = direction[minAxis] > 0.0f; + surfaceNormal = glm::vec3(0.0f); + surfaceNormal[minAxis] = positiveDirection ? -1.0f : 1.0f; + face = positiveDirection ? BoxFace(2 * minAxis) : BoxFace(2 * minAxis + 1); + } return true; } return false; diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 54f9062469..8ec75f71bd 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -76,8 +76,8 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& bool findIntersection(float origin, float direction, float corner, float size, float& distance); bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance); -bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& corner, const glm::vec3& scale, float& distance, - BoxFace& face, glm::vec3& surfaceNormal); +bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal); bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& center, float radius, float& distance); diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 1dc8c5657a..02a8d458a9 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -176,7 +176,7 @@ void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) { _triangleIndices.push_back(triangleIndex); } -bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { balanceTree(); @@ -184,7 +184,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& float localDistance = distance; int trianglesTouched = 0; - bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findRayIntersection(origin, direction, invDirection, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { distance = localDistance; } @@ -232,9 +232,9 @@ bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3& return intersectedSomething; } -bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, - bool allowBackface) { +bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, + bool allowBackface) { if (_population < 1) { return false; // no triangles below here, so we can't intersect } @@ -270,7 +270,7 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, float childBoundDistance = FLT_MAX; BoxFace childBoundFace; glm::vec3 childBoundNormal; - if (child->getBounds().findRayIntersection(origin, direction, childBoundDistance, childBoundFace, childBoundNormal)) { + if (child->getBounds().findRayIntersection(origin, direction, invDirection, childBoundDistance, childBoundFace, childBoundNormal)) { // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) if (childBoundDistance < bestLocalDistance) { priority = childBoundDistance; @@ -301,11 +301,11 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, if (!precision && childDistance < EPSILON) { BoxFace childBoundFace; glm::vec3 childBoundNormal; - sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, childDistance, childBoundFace, childBoundNormal); + sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, invDirection, childDistance, childBoundFace, childBoundNormal); } BoxFace childFace; Triangle childTriangle; - if (sortedTriangleCell.second->findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (sortedTriangleCell.second->findRayIntersection(origin, direction, invDirection, childDistance, childFace, childTriangle, precision, trianglesTouched)) { if (childDistance < bestLocalDistance) { bestLocalDistance = childDistance; bestLocalFace = childFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 84e362f0d0..3417b36b4a 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -28,7 +28,7 @@ class TriangleSet { void reset(const AABox& bounds, int depth = 0); void clear(); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, @@ -69,7 +69,7 @@ public: void insert(const Triangle& t); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); From 52bbb4d2abac51afe38fe5050a168464625d1ce3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Aug 2018 12:47:17 -0700 Subject: [PATCH 059/106] fix ray-pick results on entities with non-default registration point. clean up grab scripts to correctly use this fix. --- interface/src/raypick/PathPointer.cpp | 4 +-- .../controllerModules/farActionGrabEntity.js | 18 +++-------- .../farActionGrabEntityDynOnly.js | 17 +++------- .../controllerModules/farParentGrabEntity.js | 18 +++-------- .../libraries/controllerDispatcherUtils.js | 32 +++++++++++++++++-- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index 685611d77b..d434c667de 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -105,7 +105,7 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition()); glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat; pos = extractTranslation(finalPosAndRotMat); - rot = glmExtractRotation(finalPosAndRotMat); + rot = props.getRotation(); dim = props.getDimensions(); registrationPoint = props.getRegistrationPoint(); } @@ -350,4 +350,4 @@ glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::ve default: return glm::vec2(NAN); } -} \ No newline at end of file +} diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index e4563fda14..5e798ed680 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -14,7 +14,8 @@ PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST + Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -593,18 +594,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js index a080e75325..78abcb9b20 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js @@ -12,7 +12,7 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, - Uuid + Uuid, worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -572,18 +572,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/controllers/controllerModules/farParentGrabEntity.js b/scripts/system/controllers/controllerModules/farParentGrabEntity.js index 439b5e5f51..a9ec246a32 100644 --- a/scripts/system/controllers/controllerModules/farParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farParentGrabEntity.js @@ -11,7 +11,8 @@ Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, - getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent + getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent, + worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -615,18 +616,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index a386dcf5b4..c34fd76802 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, +/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4, Selection, Uuid, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, @@ -58,7 +58,8 @@ highlightTargetEntity:true, clearHighlightedEntities:true, unhighlightTargetEntity:true, - distanceBetweenEntityLocalPositionAndBoundingBox: true + distanceBetweenEntityLocalPositionAndBoundingBox: true, + worldPositionToRegistrationFrameMatrix: true */ MSECS_PER_SEC = 1000.0; @@ -487,6 +488,30 @@ entityIsFarGrabbedByOther = function(entityID) { return false; }; + +worldPositionToRegistrationFrameMatrix = function(wptrProps, pos) { + // get world matrix for intersection point + var intersectionMat = new Xform({ x: 0, y: 0, z:0, w: 1 }, pos); + + // calculate world matrix for registrationPoint addjusted entity + var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; + var regRatio = Vec3.subtract(DEFAULT_REGISTRATION_POINT, wptrProps.registrationPoint); + var regOffset = Vec3.multiplyVbyV(regRatio, wptrProps.dimensions); + var regOffsetRot = Vec3.multiplyQbyV(wptrProps.rotation, regOffset); + var modelMat = new Xform(wptrProps.rotation, Vec3.sum(wptrProps.position, regOffsetRot)); + + // get inverse of model matrix + var modelMatInv = modelMat.inv(); + + // transform world intersection point into object's registrationPoint frame + var xformMat = Xform.mul(modelMatInv, intersectionMat); + + // convert to Mat4 + var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); + return offsetMat; +}; + + if (typeof module !== 'undefined') { module.exports = { makeDispatcherModuleParameters: makeDispatcherModuleParameters, @@ -508,6 +533,7 @@ if (typeof module !== 'undefined') { projectOntoEntityXYPlane: projectOntoEntityXYPlane, TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE: TRIGGER_ON_VALUE, - DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST + DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix: worldPositionToRegistrationFrameMatrix }; } From f81fe6b29f3998c4dbb1a941b38b7d93d7f2c7c3 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 16:35:36 -0700 Subject: [PATCH 060/106] add more inputs to controller mapping --- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 + scripts/system/edit.js | 64 ++++++++++++------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index ddd51e9b9b..539546dd96 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -304,6 +304,8 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketLeft), "BracketLeft")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketRight), "BracketRight")); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 776c09addd..e145c1ab3d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1863,28 +1863,7 @@ var keyReleaseEvent = function (event) { cameraManager.keyReleaseEvent(event); } // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.text === 'd' && event.isControl) { - selectionManager.clearSelections(); - } else if (event.text === "t") { - selectionDisplay.toggleSpaceMode(); - } else if (event.text === "f") { - if (isActive) { - if (selectionManager.hasSelection()) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } - } else if (event.text === '[') { - if (isActive) { - cameraManager.enable(); - } - } else if (event.text === 'g') { - if (isActive && selectionManager.hasSelection()) { - grid.moveToSelection(); - } - } else if (event.key === KEY_P && event.isControl && !event.isAutoRepeat ) { + if (event.key === KEY_P && event.isControl && !event.isAutoRepeat) { if (event.isShifted) { unparentSelectedEntities(); } else { @@ -1900,9 +1879,46 @@ function deleteKey(value) { deleteSelectedEntities(); } } +function deselectKey(value) { + if (value === 0) { // on release + selectionManager.clearSelections(); + } +} +function toggleKey(value) { + if (value === 0) { // on release + selectionDisplay.toggleSpaceMode(); + } +} +function focusKey(value) { + if (value === 0) { // on release + if (selectionManager.hasSelection()) { + cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } + } +} +function cameraKey(value) { + if (value === 0) { // on release + cameraManager.enable(); + } +} +function gridKey(value) { + if (value === 0) { // on release + if (selectionManager.hasSelection()) { + grid.moveToSelection(); + } + } +} var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).peek().to(deleteKey); -mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).peek().to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); +mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); +mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); +mapping.from([Controller.Hardware.Keyboard.BracketLeft]).to(cameraKey); +mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); function recursiveAdd(newParentID, parentData) { if (parentData.children !== undefined) { From c19bd8968ad315df6abdfc336ba58fcafe47e041 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 27 Aug 2018 16:48:47 -0700 Subject: [PATCH 061/106] Fix mixer crash --- libraries/avatars/src/AvatarHashMap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 8dfee3551e..c437b56f32 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -239,7 +239,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer Date: Mon, 27 Aug 2018 15:38:36 -0700 Subject: [PATCH 062/106] Make MouseTransformNode actually parent the pick to a mouse rather than a MouseRayPick, and properly include rotation information --- interface/src/raypick/MouseTransformNode.cpp | 42 ++++++-------------- interface/src/raypick/MouseTransformNode.h | 5 --- 2 files changed, 13 insertions(+), 34 deletions(-) diff --git a/interface/src/raypick/MouseTransformNode.cpp b/interface/src/raypick/MouseTransformNode.cpp index 4bc85ae69a..9caa4865a2 100644 --- a/interface/src/raypick/MouseTransformNode.cpp +++ b/interface/src/raypick/MouseTransformNode.cpp @@ -8,36 +8,20 @@ #include "MouseTransformNode.h" -#include "DependencyManager.h" -#include "PickManager.h" -#include "MouseRayPick.h" - -const PickFilter MOUSE_TRANSFORM_NODE_PICK_FILTER( - 1 << PickFilter::PICK_ENTITIES | - 1 << PickFilter::PICK_AVATARS | - 1 << PickFilter::PICK_INCLUDE_NONCOLLIDABLE -); -const float MOUSE_TRANSFORM_NODE_MAX_DISTANCE = 1000.0f; - -MouseTransformNode::MouseTransformNode() { - _parentMouseRayPick = DependencyManager::get()->addPick(PickQuery::Ray, - std::make_shared(MOUSE_TRANSFORM_NODE_PICK_FILTER, MOUSE_TRANSFORM_NODE_MAX_DISTANCE, true)); -} - -MouseTransformNode::~MouseTransformNode() { - if (DependencyManager::isSet()) { - auto pickManager = DependencyManager::get(); - if (pickManager) { - pickManager->removePick(_parentMouseRayPick); - } - } -} +#include "Application.h" +#include "display-plugins/CompositorHelper.h" +#include "RayPick.h" Transform MouseTransformNode::getTransform() { - Transform transform; - std::shared_ptr rayPickResult = DependencyManager::get()->getPrevPickResultTyped(_parentMouseRayPick); - if (rayPickResult) { - transform.setTranslation(rayPickResult->intersection); + QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition(); + if (position.isValid()) { + Transform transform; + QVariantMap posMap = position.toMap(); + PickRay pickRay = qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat()); + transform.setTranslation(pickRay.origin); + transform.setRotation(rotationBetween(Vectors::UP, pickRay.direction)); + return transform; } - return transform; + + return Transform(); } \ No newline at end of file diff --git a/interface/src/raypick/MouseTransformNode.h b/interface/src/raypick/MouseTransformNode.h index 7a05370dd7..4f340339e4 100644 --- a/interface/src/raypick/MouseTransformNode.h +++ b/interface/src/raypick/MouseTransformNode.h @@ -12,12 +12,7 @@ class MouseTransformNode : public TransformNode { public: - MouseTransformNode(); - ~MouseTransformNode(); Transform getTransform() override; - -protected: - unsigned int _parentMouseRayPick = 0; }; #endif // hifi_MouseTransformNode_h \ No newline at end of file From 89b508251e94472518fa8c9a4d00d02540d66e86 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 27 Aug 2018 17:57:50 -0700 Subject: [PATCH 063/106] added some comment to explain rig to avatar transform --- interface/src/avatar/MyAvatar.cpp | 38 +++++++++++++++++-------------- interface/src/avatar/MyAvatar.h | 4 ++-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c4d855ee77..23feff88dd 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -453,13 +453,16 @@ void MyAvatar::update(float deltaTime) { // put the spine facing in sensor space. // then mix it with head facing to determine rotation recenter - glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); - glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); - glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); - glm::vec2 normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { + glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); + glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); + glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); + glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); + if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { + normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + } + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { _headControllerFacingMovingAverage = _headControllerFacing; @@ -475,7 +478,6 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - computeHandAzimuth(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE @@ -495,12 +497,8 @@ void MyAvatar::update(float deltaTime) { if (_goToPending) { setWorldPosition(_goToPosition); - setWorldOrientation(_goToOrientation); - if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { - _headControllerFacingMovingAverage = headFacingPlusHandHipAzimuthMix; - } else { - _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average - } + setWorldOrientation(_goToOrientation); + _headControllerFacingMovingAverage = _headControllerFacing; _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). @@ -827,15 +825,17 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { void MyAvatar::computeHandAzimuth() { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); - glm::vec3 localLookat(0.0f, 0.0f, 1.0f); const float HALFWAY = 0.50f; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. vec2 oldAzimuthReading = _hipToHandController; - _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); - + if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) { + _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); + } else { + _hipToHandController = glm::vec2(0.0f, 1.0f); + } // check the angular distance from forward and back float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); @@ -3376,7 +3376,11 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; - glm::vec3 hipToHandRigSpace = RIG_CHANGE_OF_BASIS * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + // RIG_CHANGE_OF_BASIS * INITIAL_RIG_ROTATION * Quaternions::IDENTITY(explicit rig rotation) * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180(avatar Space); + // INITIAL_RIG_ROTATION = Quaternions::Y_180; + static const glm::quat INITIAL_RIG_ROTATION = Quaternions::Y_180; + glm::quat avatarToRigSpace = INITIAL_RIG_ROTATION * Quaternions::IDENTITY; + glm::vec3 hipToHandRigSpace = avatarToRigSpace * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); glm::vec3 u, v, w; generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f819c417de..8cccb03ea5 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -285,7 +285,7 @@ public: Q_INVOKABLE void centerBody(); // thread-safe - /**jsdoc_hmdSensorMatrix + /**jsdoc * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent * contorted joints. * @function MyAvatar.clearIKJointLimitHistory @@ -1650,7 +1650,7 @@ private: std::atomic _hasScriptedBlendShapes { false }; std::atomic _rotationRecenterFilterLength { 4.0f }; std::atomic _rotationThreshold { 0.5235f }; // 30 degrees in radians - std::atomic _stepResetRotationEnabled { false }; + std::atomic _stepResetRotationEnabled { true }; std::atomic _drawAverageFacingEnabled { false }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access From 511478a563841ec944121c967bbc219409ea9874 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 18:11:19 -0700 Subject: [PATCH 064/106] use not Mac instead of Windows --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index e145c1ab3d..dd53e8c395 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1912,7 +1912,7 @@ function gridKey(value) { } } var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([!Controller.Hardware.Application.PlatformMac]).to(deleteKey); mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); From 6f6e70a470e10cb237a8392d23c1e242ccc6fff5 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Tue, 28 Aug 2018 08:45:47 -0700 Subject: [PATCH 065/106] Re-order variable initialization in CollisionRegion copy contructor --- libraries/shared/src/RegisteredMetaTypes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 312e017dc8..32ecfe8b6d 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -270,10 +270,10 @@ public: CollisionRegion() { } CollisionRegion(const CollisionRegion& collisionRegion) : - threshold(collisionRegion.threshold), modelURL(collisionRegion.modelURL), shapeInfo(std::make_shared()), - transform(collisionRegion.transform) + transform(collisionRegion.transform), + threshold(collisionRegion.threshold) { shapeInfo->setParams(collisionRegion.shapeInfo->getType(), collisionRegion.shapeInfo->getHalfExtents(), collisionRegion.modelURL.toString()); } From 3c792a04c1dfe8096ab789ed0b146d50d4f31c89 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 28 Aug 2018 11:25:42 -0700 Subject: [PATCH 066/106] fixed roll rotation added to azimuth in MySkeletonModel.cpp --- interface/src/avatar/MyAvatar.cpp | 35 +-- interface/src/avatar/MyAvatar.h | 2 + interface/src/avatar/MySkeletonModel.cpp | 33 +- scripts/developer/rotateApp.js | 385 ----------------------- 4 files changed, 38 insertions(+), 417 deletions(-) delete mode 100644 scripts/developer/rotateApp.js diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 23feff88dd..32822fc1b9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -447,12 +447,12 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { void MyAvatar::update(float deltaTime) { // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); - const float PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH = 0.0f; // 100 percent shoulders + const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders float tau = deltaTime / HMD_FACING_TIMESCALE; - // put the spine facing in sensor space. - // then mix it with head facing to determine rotation recenter + // put the average hand azimuth into sensor space. + // then mix it with head facing direction to determine rotation recenter if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); @@ -462,7 +462,7 @@ void MyAvatar::update(float deltaTime) { if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); } - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { _headControllerFacingMovingAverage = _headControllerFacing; @@ -497,7 +497,7 @@ void MyAvatar::update(float deltaTime) { if (_goToPending) { setWorldPosition(_goToPosition); - setWorldOrientation(_goToOrientation); + setWorldOrientation(_goToOrientation); _headControllerFacingMovingAverage = _headControllerFacing; _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes @@ -834,15 +834,14 @@ void MyAvatar::computeHandAzimuth() { if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) { _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); } else { - _hipToHandController = glm::vec2(0.0f, 1.0f); + _hipToHandController = glm::vec2(0.0f, -1.0f); } // check the angular distance from forward and back float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); // if we are now closer to the 180 flip of the previous chest forward - // then we negate our computed _hipToHandController to keep the chest in the same direction. + // then we negate our computed _hipToHandController to keep the chest from flipping. if (cosBackwardAngle > cosForwardAngle) { - // this means we have lost continuity with the current chest position _hipToHandController = -_hipToHandController; } } @@ -3375,12 +3374,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { - static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; - // RIG_CHANGE_OF_BASIS * INITIAL_RIG_ROTATION * Quaternions::IDENTITY(explicit rig rotation) * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180(avatar Space); - // INITIAL_RIG_ROTATION = Quaternions::Y_180; - static const glm::quat INITIAL_RIG_ROTATION = Quaternions::Y_180; - glm::quat avatarToRigSpace = INITIAL_RIG_ROTATION * Quaternions::IDENTITY; - glm::vec3 hipToHandRigSpace = avatarToRigSpace * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + // static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; + // RIG_CHANGE_OF_BASIS * AVATAR_TO_RIG_ROTATION * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180; //avatar Space; + const glm::quat AVATAR_TO_RIG_ROTATION = Quaternions::Y_180; + glm::vec3 hipToHandRigSpace = AVATAR_TO_RIG_ROTATION * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); glm::vec3 u, v, w; generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); @@ -3971,7 +3968,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); if (!isActive(Horizontal) && - (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance * myAvatar.getAvatarScale())))) { + (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) { myAvatar.setResetMode(true); stepDetected = true; } @@ -3995,14 +3992,14 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); } if (myAvatar.getCenterOfGravityModelEnabled()) { if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); } } } else { @@ -4010,7 +4007,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); } } } @@ -4020,7 +4017,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } else { if (!isActive(Rotation) && getForceActivateRotation()) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); setForceActivateRotation(false); } if (!isActive(Horizontal) && getForceActivateHorizontal()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8cccb03ea5..8b5d9d8c55 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -914,6 +914,8 @@ public: virtual void rebuildCollisionShape() override; + const glm::vec2& getHipToHandController() const { return _hipToHandController; } + void setHipToHandController(glm::vec2 currentHipToHandController) { _hipToHandController = currentHipToHandController; } const glm::vec2& getHeadControllerFacing() const { return _headControllerFacing; } void setHeadControllerFacing(glm::vec2 currentHeadControllerFacing) { _headControllerFacing = currentHeadControllerFacing; } const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f150379f0c..e06a06cf0e 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -239,21 +239,28 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - AnimPose currentPose; - bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"),currentPose); - AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); - glm::vec3 u, v, w; - glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); - glm::vec3 up = currentPose.rot() * glm::vec3(0.0f, 1.0f, 0.0f); - generateBasisVectors(up, fwd, u, v, w); - AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); - if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) { - AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); - currentPose.rot() = newSpinePose.rot(); + AnimPose currentSpine2Pose; + AnimPose currentHeadPose; + AnimPose currentHipsPose; + bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); + bool ret1 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); + bool ret2 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); + AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); + glm::vec3 u, v, w; + glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); + glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans(); + if (glm::length(up) > 0.0f) { + up = glm::normalize(up); + } else { + up = glm::vec3(0.0f, 1.0f, 0.0f); + } + generateBasisVectors(up, fwd, u, v, w); + AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); + currentSpine2Pose.rot() = newSpinePose.rot(); + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; + params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } - params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentPose; - params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } else { _prevHipsValid = false; diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js deleted file mode 100644 index 030385b6fb..0000000000 --- a/scripts/developer/rotateApp.js +++ /dev/null @@ -1,385 +0,0 @@ -/* global Script, Vec3, MyAvatar, Tablet, Messages, Quat, -DebugDraw, Mat4, Entities, Xform, Controller, Camera, console, document*/ - -Script.registerValue("ROTATEAPP", true); - -var TABLET_BUTTON_NAME = "ROTATE"; -var CHANGE_OF_BASIS_ROTATION = { x: 0, y: 1, z: 0, w: 0 }; -var DEFAULT_HEAD_TURN_THRESHOLD = 0.5333; -var DEFAULT_HEAD_TURN_FILTER_LENGTH = 4.0; -var LOADING_DELAY = 2000; -var AVERAGING_RATE = 0.03; -var INCREASING = 1.0; -var DECREASING = -1.0; -var DEGREES_PER_PI_RADIANS = 180.0; -var FILTER_FUDGE_RANGE = 0.9; - -var activated = false; -var documentLoaded = false; -var sciptLoaded = false; -var headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; -var hipToLeftHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; -var hipToRightHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; -var averageAzimuth = 0.0; -var hipsPositionRigSpace = { x: 0, y: 0, z: 0 }; -var spine2PositionRigSpace = { x: 0, y: 0, z: 0 }; -var hipsRotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; -var spine2RotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; -var spine2Rotation = { x: 0, y: 0, z: 0, w: 1 }; -var hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; -var hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; - -/* -var ikTypes = { - RotationAndPosition: 0, - RotationOnly: 1, - HmdHead: 2, - HipsRelativeRotationAndPosition: 3, - Spline: 4, - Unknown: 5 -}; - - -var ANIM_VARS = [ - //"headType", - "spine2Type", - //"hipsType", - "spine2Position", - "spine2Rotation" - //"hipsPosition", - //"hipsRotation" -]; - -var handlerId = MyAvatar.addAnimationStateHandler(function (props) { - //print("in callback"); - //print("props spine2 pos: " + props.spine2Position.x + " " + props.spine2Position.y + " " + props.spine2Position.z); - //print("props hip pos: " + props.hipsPosition.x + " " + props.hipsPosition.y + " " + props.hipsPosition.z); - var result = {}; - //{x:0,y:0,z:0} - //result.headType = ikTypes.HmdHead; - //result.hipsType = ikTypes.RotationAndPosition; - //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; - //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // - result.spine2Type = ikTypes.Spline; - result.spine2Position = spine2PositionRigSpace; // { x: 0, y: 1.3, z: 0 }; - result.spine2Rotation = spine2Rotation; - - return result; -}, ANIM_VARS); -*/ -// define state readings constructor -function StateReading(headPose, rhandPose, lhandPose, diffFromAverageEulers) { - this.headPose = headPose; - this.rhandPose = rhandPose; - this.lhandPose = lhandPose; - this.diffFromAverageEulers = diffFromAverageEulers; -} - -// define current state readings object for holding tracker readings and current differences from averages -var currentStateReadings = new StateReading(Controller.getPoseValue(Controller.Standard.Head), - Controller.getPoseValue(Controller.Standard.RightHand), - Controller.getPoseValue(Controller.Standard.LeftHand), - { x: 0, y: 0, z: 0 }); - -// declare the checkbox constructor -function AppCheckbox(type,id,eventType,isChecked) { - this.type = type; - this.id = id; - this.eventType = eventType; - this.data = {value: isChecked}; -} - -var usingStepResetRotationDirection = new AppCheckbox("checkboxtick", "stepReset", "onStepResetCheckBox", false); -var usingDrawAverageFacing = new AppCheckbox("checkboxtick", "drawAverage", "onDrawAverageFacingCheckBox", false); -var checkBoxArray = new Array(usingStepResetRotationDirection, usingDrawAverageFacing); - -// declare the html slider constructor -function AppProperty(name, type, eventType, signalType, setFunction, initValue, convertToThreshold, convertToSlider, signalOn) { - this.name = name; - this.type = type; - this.eventType = eventType; - this.signalType = signalType; - this.setValue = setFunction; - this.value = initValue; - this.get = function () { - return this.value; - }; - this.convertToThreshold = convertToThreshold; - this.convertToSlider = convertToSlider; -} - -// var HTML_URL = Script.resolvePath("file:///c:/dev/hifi_fork/hifi/scripts/developer/rotateRecenterApp.html"); -var HTML_URL = Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/rotateRecenterApp.html"); -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - -// define the sliders -var filterLengthProperty = new AppProperty("#filterLength-slider", "slider", "onFilterLengthSlider", "filterSignal", - setFilterLength, DEFAULT_HEAD_TURN_FILTER_LENGTH, function (num) { - var base = 5; - var shift = 0; - return convertExponential(base, num, INCREASING, shift); - }, function (num) { - var base = 5; - var shift = 0; - return convertLog(base, num, INCREASING, shift); - }, true); -var angleThresholdProperty = new AppProperty("#angleThreshold-slider", "slider", "onAngleThresholdSlider", "angleSignal", - setAngleThreshold, DEFAULT_HEAD_TURN_THRESHOLD, function (num) { - return convertToRadians(num); - }, function (num) { - return convertToDegrees(num); - }, true); - -var propArray = new Array(filterLengthProperty, angleThresholdProperty); - -function setFilterLength(num) { - filterLengthProperty.value = num; - MyAvatar.rotationRecenterFilterLength = filterLengthProperty.value; - -} - -function setAngleThreshold(num) { - angleThresholdProperty.value = num; - MyAvatar.rotationThreshold = angleThresholdProperty.value; -} - -function convertToRadians(num) { - return (num / DEGREES_PER_PI_RADIANS) * Math.PI; -} - -function convertToDegrees(num) { - return (num / Math.PI) * DEGREES_PER_PI_RADIANS; -} - -function getLog(x, y) { - return Math.log(y) / Math.log(x); -} - -function convertLog(base, num, direction, shift) { - return direction * getLog(base, (num + FILTER_FUDGE_RANGE)) + shift; -} - -function convertExponential(base, num, direction, shift) { - return Math.pow(base, (direction * num + shift)) - FILTER_FUDGE_RANGE; -} - -function manageClick() { - if (activated) { - tablet.gotoHomeScreen(); - } else { - tablet.gotoWebScreen(HTML_URL); - } -} - -var tabletButton = tablet.addButton({ - text: TABLET_BUTTON_NAME, - icon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg"), - activeIcon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg") -}); - -function onKeyPress(event) { - if (event.text === "'") { - // when the sensors are reset, then reset the mode. - } -} - -function onWebEventReceived(msg) { - var message = JSON.parse(msg); - print(" we have a message from html dialog " + message.type); - propArray.forEach(function (prop) { - if (prop.eventType === message.type) { - prop.setValue(prop.convertToThreshold(message.data.value)); - print("message from " + prop.name); - // break; - } - }); - checkBoxArray.forEach(function(cbox) { - if (cbox.eventType === message.type) { - cbox.data.value = message.data.value; - if (cbox.id === "stepReset") { - MyAvatar.enableStepResetRotation = cbox.data.value; - } - if (cbox.id === "drawAverage") { - MyAvatar.enableDrawAverageFacing = cbox.data.value; - } - // break; - } - - }); - if (message.type === "onCreateRotateApp") { - print("document loaded"); - documentLoaded = true; - Script.setTimeout(initAppForm, LOADING_DELAY); - } -} - -function initAppForm() { - print("step app is loaded: " + documentLoaded); - if (documentLoaded === true) { - propArray.forEach(function (prop) { - print(prop.name); - tablet.emitScriptEvent(JSON.stringify({ - "type": "slider", - "id": prop.name, - "data": { "value": prop.convertToSlider(prop.value) } - })); - }); - checkBoxArray.forEach(function (cbox) { - tablet.emitScriptEvent(JSON.stringify({ - "type": "checkboxtick", - "id": cbox.id, - "data": { value: cbox.data.value } - })); - }); - } - -} - - -function onScreenChanged(type, url) { - print("Screen changed"); - if (type === "Web" && url === HTML_URL) { - if (!activated) { - // hook up to event bridge - tablet.webEventReceived.connect(onWebEventReceived); - print("after connect web event"); - MyAvatar.hmdLeanRecenterEnabled = true; - } - activated = true; - } else { - if (activated) { - // disconnect from event bridge - tablet.webEventReceived.disconnect(onWebEventReceived); - documentLoaded = false; - } - activated = false; - } -} - -function limitAngle(angle) { - return (angle + 180) % 360 - 180; -} - -function computeHandAzimuths(timeElapsed) { - - // var leftHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.lhandPose.translation); - // var rightHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.rhandPose.translation); - - - // var hipToLeftHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: leftHandPositionRigSpace.x, y: 0, z: leftHandPositionRigSpace.z }); - // var hipToRightHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: rightHandPositionRigSpace.x, y: 0, z: rightHandPositionRigSpace.z }); - // var hipToHandHalfway = Quat.slerp(hipToLeftHand, hipToRightHand, 0.5); - - var leftHand = currentStateReadings.lhandPose.translation; - var rightHand = currentStateReadings.rhandPose.translation; - var head = currentStateReadings.headPose.translation; - var lHandMinusHead = Vec3.subtract(leftHand, head); - lHandMinusHead.y = 0.0; - var rHandMinusHead = Vec3.subtract(rightHand, head); - rHandMinusHead.y = 0.0; - //print(JSON.stringify(leftHand)); - //print(JSON.stringify(head)); - var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; - var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); - var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); - var tau = timeElapsed / filterLengthProperty.value; - hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, tau); - hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, tau); - - // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); - // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); - var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHand).y + Quat.safeEulerAngles(hipToRHand).y) / 2.0; - var leftRightMidpointAverage = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; - - // limit the angle because we are flipped by 180, fix this tomorrow. - // print(leftRightMidpointAverage/180.0); - // print("threshold value " + angleThresholdProperty.value); - - - var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); - var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); - var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.orientation, zAxisSpineRotation); - // DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); - - //print(leftRightMidpoint); - - var headPoseRigSpace = Quat.multiply(CHANGE_OF_BASIS_ROTATION, currentStateReadings.head.rotation); - headPoseAverageOrientation = Quat.slerp(headPoseAverageOrientation, headPoseRigSpace, tau); - var headPoseAverageEulers = Quat.safeEulerAngles(headPoseAverageOrientation); - - // get it into radians too!! - // average head and hands 50/50 - // print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y); - var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; - if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { - //print("recenter the feet under the head"); - // MyAvatar.triggerRotationRecenter(); - hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; - hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; - headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; - } - - // put a hard max on this easing function. - var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0)/3.0) * leftRightMidpoint; - //print("rotate angle " + rotateAngle); - - return Quat.fromVec3Degrees({ x: 0, y: rotateAngle, z: 0 }); - - -} - -function update(dt) { - //update state readings - currentStateReadings.head = Controller.getPoseValue(Controller.Standard.Head); - currentStateReadings.rhandPose = Controller.getPoseValue(Controller.Standard.RightHand); - currentStateReadings.lhandPose = Controller.getPoseValue(Controller.Standard.LeftHand); - - //print(JSON.stringify(currentStateReadings.head)); - - - - var latestSpineRotation = computeHandAzimuths(dt); - - spine2Rotation = latestSpineRotation; - var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); - spine2PositionRigSpace = Vec3.multiplyQbyV(CHANGE_OF_BASIS_ROTATION, spine2Pos); - /* - if (HMD.active && !scriptLoaded) { - //Script.load("rotateApp.js"); - scriptLoaded = true; - } - - if (!HMD.active) { - scriptLoaded = false; - } - */ - - // handle the azimuth of the arms - -} - -function shutdownTabletApp() { - tablet.removeButton(tabletButton); - if (activated) { - tablet.webEventReceived.disconnect(onWebEventReceived); - tablet.gotoHomeScreen(); - } - tablet.screenChanged.disconnect(onScreenChanged); -} - -Script.setTimeout(function () { - tabletButton.clicked.connect(manageClick); - tablet.screenChanged.connect(onScreenChanged); - Script.update.connect(update); - Controller.keyPressEvent.connect(onKeyPress); -}, (LOADING_DELAY)); - -Script.scriptEnding.connect(function () { - // if (handlerId) { - // print("removing animation state handler"); - // handlerId = MyAvatar.removeAnimationStateHandler(handlerId); - // } - MyAvatar.hmdLeanRecenterEnabled = true; - Script.update.disconnect(update); - shutdownTabletApp(); -}); \ No newline at end of file From ee33f5dc97610c96a8e29b89049bc9adc608311e Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Wed, 29 Aug 2018 01:57:49 +0100 Subject: [PATCH 067/106] normalize the hip hand vector in getSpine2RotationRigSpace, made computeHandAzimuth a const function that returns a vec2 --- interface/src/avatar/MyAvatar.cpp | 36 +++++++++++++----------- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/MySkeletonModel.cpp | 34 +++++++++++----------- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 32822fc1b9..75becd2c6d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -91,8 +91,6 @@ const float MIN_SCALE_CHANGED_DELTA = 0.001f; const int MODE_READINGS_RING_BUFFER_SIZE = 500; const float CENTIMETERS_PER_METER = 100.0f; -#define DEBUG_DRAW_HMD_MOVING_AVERAGE - MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), _yawSpeed(YAW_SPEED_DEFAULT), @@ -450,6 +448,7 @@ void MyAvatar::update(float deltaTime) { const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders float tau = deltaTime / HMD_FACING_TIMESCALE; + setHipToHandController(computeHandAzimuth()); // put the average hand azimuth into sensor space. // then mix it with head facing direction to determine rotation recenter @@ -465,7 +464,7 @@ void MyAvatar::update(float deltaTime) { glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { - _headControllerFacingMovingAverage = _headControllerFacing; + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau); } if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { @@ -478,10 +477,8 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - computeHandAzimuth(); -#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE - if (_drawAverageFacingEnabled) { + if (_drawAverageFacingEnabled) { auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); @@ -493,12 +490,11 @@ void MyAvatar::update(float deltaTime) { glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } -#endif if (_goToPending) { setWorldPosition(_goToPosition); setWorldOrientation(_goToOrientation); - _headControllerFacingMovingAverage = _headControllerFacing; + _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). @@ -822,29 +818,30 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // Find the vector halfway between the hip to hand azimuth vectors // This midpoint hand azimuth is in Avatar space -void MyAvatar::computeHandAzimuth() { +glm::vec2 MyAvatar::computeHandAzimuth() const { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); const float HALFWAY = 0.50f; + glm::vec2 latestHipToHandController = _hipToHandController; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. - vec2 oldAzimuthReading = _hipToHandController; + glm::vec2 oldAzimuthReading = _hipToHandController; if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) { - _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); + latestHipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); } else { - _hipToHandController = glm::vec2(0.0f, -1.0f); + latestHipToHandController = glm::vec2(0.0f, -1.0f); } // check the angular distance from forward and back - float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); - float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); + float cosForwardAngle = glm::dot(latestHipToHandController, oldAzimuthReading); // if we are now closer to the 180 flip of the previous chest forward - // then we negate our computed _hipToHandController to keep the chest from flipping. - if (cosBackwardAngle > cosForwardAngle) { - _hipToHandController = -_hipToHandController; + // then we negate our computed latestHipToHandController to keep the chest from flipping. + if (cosForwardAngle < 0.0f) { + latestHipToHandController = -latestHipToHandController; } } + return latestHipToHandController; } void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache) { @@ -3380,6 +3377,11 @@ glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { glm::vec3 hipToHandRigSpace = AVATAR_TO_RIG_ROTATION * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); glm::vec3 u, v, w; + if (glm::length(hipToHandRigSpace) > 0.0f) { + hipToHandRigSpace = glm::normalize(hipToHandRigSpace); + } else { + hipToHandRigSpace = glm::vec3(0.0f, 0.0f, 1.0f); + } generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); return spine2RigSpace; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8b5d9d8c55..f82c5b4018 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -321,7 +321,7 @@ public: void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); // compute the hip to hand average azimuth. - void computeHandAzimuth(); + glm::vec2 computeHandAzimuth() const; // read the location of a hand controller and save the transform void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index e06a06cf0e..8fd7d31956 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -243,23 +243,25 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { AnimPose currentSpine2Pose; AnimPose currentHeadPose; AnimPose currentHipsPose; - bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); - bool ret1 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); - bool ret2 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); - AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); - glm::vec3 u, v, w; - glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); - glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans(); - if (glm::length(up) > 0.0f) { - up = glm::normalize(up); - } else { - up = glm::vec3(0.0f, 1.0f, 0.0f); + bool spine2Exists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); + bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); + bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); + if (spine2Exists && headExists && hipsExists) { + AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); + glm::vec3 u, v, w; + glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); + glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans(); + if (glm::length(up) > 0.0f) { + up = glm::normalize(up); + } else { + up = glm::vec3(0.0f, 1.0f, 0.0f); + } + generateBasisVectors(up, fwd, u, v, w); + AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); + currentSpine2Pose.rot() = newSpinePose.rot(); + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; + params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } - generateBasisVectors(up, fwd, u, v, w); - AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); - currentSpine2Pose.rot() = newSpinePose.rot(); - params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; - params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } } else { From bf348f43aa55d790423f6e5a828e0372e9843f90 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 29 Aug 2018 09:55:54 -0700 Subject: [PATCH 068/106] Prepare for runtime CPU dispatch of all SIMD functions --- libraries/audio/src/AudioHRTF.cpp | 54 ++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 23adcde0c5..aef4ef3326 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -276,23 +276,8 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float } } -// -// Runtime CPU dispatch -// - -#include "CPUDetect.h" - -void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); -void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); - -static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) { - - static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE); - (*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch -} - // 4 channel planar to interleaved -static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) { +static void interleave_4x4_SSE(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) { assert(numFrames % 4 == 0); @@ -323,7 +308,7 @@ static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, f // process 2 cascaded biquads on 4 channels (interleaved) // biquads computed in parallel, by adding one sample of delay -static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { +static void biquad2_4x4_SSE(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { // enable flush-to-zero mode to prevent denormals unsigned int ftz = _MM_GET_FLUSH_ZERO_MODE(); @@ -388,7 +373,7 @@ static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3] } // crossfade 4 inputs into 2 outputs with accumulation (interleaved) -static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) { +static void crossfade_4x2_SSE(float* src, float* dst, const float* win, int numFrames) { assert(numFrames % 4 == 0); @@ -435,7 +420,7 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame } // linear interpolation with gain -static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) { +static void interpolate_SSE(float* dst, const float* src0, const float* src1, float frac, float gain) { __m128 f0 = _mm_set1_ps(gain * (1.0f - frac)); __m128 f1 = _mm_set1_ps(gain * frac); @@ -453,6 +438,37 @@ static void interpolate(float* dst, const float* src0, const float* src1, float } } +// +// Runtime CPU dispatch +// + +#include "CPUDetect.h" + +void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); +void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); + +static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) { + + static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE); + (*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch +} + +static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) { + interleave_4x4_SSE(src0, src1, src2, src3, dst, numFrames); +} + +static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { + biquad2_4x4_SSE(src, dst, coef, state, numFrames); +} + +static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) { + crossfade_4x2_SSE(src, dst, win, numFrames); +} + +static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) { + interpolate_SSE(dst, src0, src1, frac, gain); +} + #else // portable reference code // 1 channel input, 4 channel output From 6f151ad2837cb252dd8ea16c5baa3e415ca23363 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 29 Aug 2018 10:03:40 -0700 Subject: [PATCH 069/106] Consistent parameter order for SIMD functions --- libraries/audio/src/AudioHRTF.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index aef4ef3326..20d782a414 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -420,7 +420,7 @@ static void crossfade_4x2_SSE(float* src, float* dst, const float* win, int numF } // linear interpolation with gain -static void interpolate_SSE(float* dst, const float* src0, const float* src1, float frac, float gain) { +static void interpolate_SSE(const float* src0, const float* src1, float* dst, float frac, float gain) { __m128 f0 = _mm_set1_ps(gain * (1.0f - frac)); __m128 f1 = _mm_set1_ps(gain * frac); @@ -465,8 +465,8 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame crossfade_4x2_SSE(src, dst, win, numFrames); } -static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) { - interpolate_SSE(dst, src0, src1, frac, gain); +static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) { + interpolate_SSE(src0, src1, dst, frac, gain); } #else // portable reference code @@ -731,7 +731,7 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame } // linear interpolation with gain -static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) { +static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) { float f0 = gain * (1.0f - frac); float f1 = gain * frac; @@ -983,8 +983,8 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela azimuthToIndex(azimuth, az0, az1, frac); // interpolate FIR - interpolate(firCoef[channel+0], ir_table_table[index][azL0][0], ir_table_table[index][azL1][0], fracL, gain * gainL); - interpolate(firCoef[channel+1], ir_table_table[index][azR0][1], ir_table_table[index][azR1][1], fracR, gain * gainR); + interpolate(ir_table_table[index][azL0][0], ir_table_table[index][azL1][0], firCoef[channel+0], fracL, gain * gainL); + interpolate(ir_table_table[index][azR0][1], ir_table_table[index][azR1][1], firCoef[channel+1], fracR, gain * gainR); // interpolate ITD float itd = (1.0f - frac) * itd_table_table[index][az0] + frac * itd_table_table[index][az1]; From 3e17556cc1c14c7263d8f27cf440476c7be22451 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 29 Aug 2018 10:47:02 -0700 Subject: [PATCH 070/106] Prefer static_assert() over assert() --- libraries/audio/src/AudioHRTF.cpp | 6 +++--- libraries/audio/src/avx2/AudioHRTF_avx2.cpp | 2 +- libraries/audio/src/avx512/AudioHRTF_avx512.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 20d782a414..7895a70595 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -240,7 +240,7 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float float* ps = &src[i - HRTF_TAPS + 1]; // process forwards - assert(HRTF_TAPS % 4 == 0); + static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4"); for (int k = 0; k < HRTF_TAPS; k += 4) { @@ -425,7 +425,7 @@ static void interpolate_SSE(const float* src0, const float* src1, float* dst, fl __m128 f0 = _mm_set1_ps(gain * (1.0f - frac)); __m128 f1 = _mm_set1_ps(gain * frac); - assert(HRTF_TAPS % 4 == 0); + static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4"); for (int k = 0; k < HRTF_TAPS; k += 4) { @@ -505,7 +505,7 @@ static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* ds float* ps = &src[i - HRTF_TAPS + 1]; // process forwards - assert(HRTF_TAPS % 4 == 0); + static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4"); for (int k = 0; k < HRTF_TAPS; k += 4) { diff --git a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp index e89128b173..501f010236 100644 --- a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp +++ b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp @@ -44,7 +44,7 @@ void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3 float* ps = &src[i - HRTF_TAPS + 1]; // process forwards - assert(HRTF_TAPS % 4 == 0); + static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4"); for (int k = 0; k < HRTF_TAPS; k += 4) { diff --git a/libraries/audio/src/avx512/AudioHRTF_avx512.cpp b/libraries/audio/src/avx512/AudioHRTF_avx512.cpp index a8bb62be35..fcec81fa4c 100644 --- a/libraries/audio/src/avx512/AudioHRTF_avx512.cpp +++ b/libraries/audio/src/avx512/AudioHRTF_avx512.cpp @@ -44,7 +44,7 @@ void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* ds float* ps = &src[i - HRTF_TAPS + 1]; // process forwards - assert(HRTF_TAPS % 4 == 0); + static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4"); for (int k = 0; k < HRTF_TAPS; k += 4) { From f9d14e135149d977640f09308ec791cf6fb52b28 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 29 Aug 2018 11:17:11 -0700 Subject: [PATCH 071/106] filter time-cost drivers for region regulation --- interface/src/Application.cpp | 14 +++-- libraries/shared/src/NumericalConstants.h | 1 + libraries/workload/src/workload/Space.h | 3 +- libraries/workload/src/workload/ViewTask.cpp | 56 +++++++++++++++----- libraries/workload/src/workload/ViewTask.h | 29 ++++++---- 5 files changed, 73 insertions(+), 30 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 44bba6250b..0c25fd59a9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5795,15 +5795,13 @@ void Application::update(float deltaTime) { auto t5 = std::chrono::high_resolution_clock::now(); workload::Timings timings(6); - timings[0] = (t4 - t0); - timings[1] = (t5 - t4); - timings[2] = (t4 - t3); - timings[3] = (t3 - t2); - timings[4] = (t2 - t1); - timings[5] = (t1 - t0); - + timings[0] = t1 - t0; // prePhysics entities + timings[1] = t2 - t1; // prePhysics avatars + timings[2] = t3 - t2; // stepPhysics + timings[3] = t4 - t3; // postPhysics + timings[4] = t5 - t4; // non-physical kinematics + timings[5] = workload::Timing_ns((int32_t)(NSECS_PER_SECOND * deltaTime)); // game loop duration _gameWorkload.updateSimulationTimings(timings); - } } } else { diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index 4c24a73226..8377c48960 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -36,6 +36,7 @@ const float METERS_PER_MILLIMETER = 0.001f; const float MILLIMETERS_PER_METER = 1000.0f; const quint64 NSECS_PER_USEC = 1000; const quint64 NSECS_PER_MSEC = 1000000; +const quint64 NSECS_PER_SECOND = 1e9; const quint64 USECS_PER_MSEC = 1000; const quint64 MSECS_PER_SECOND = 1000; const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; diff --git a/libraries/workload/src/workload/Space.h b/libraries/workload/src/workload/Space.h index 310955f4c6..7dcb2217f7 100644 --- a/libraries/workload/src/workload/Space.h +++ b/libraries/workload/src/workload/Space.h @@ -70,7 +70,8 @@ private: using SpacePointer = std::shared_ptr; using Changes = std::vector; using IndexVectors = std::vector; -using Timings = std::vector; +using Timing_ns = std::chrono::nanoseconds; +using Timings = std::vector; } // namespace workload diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index 5d9953cdf4..b05a4c0c5f 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -102,9 +102,16 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const // Export the ranges and timings for debuging if (inTimings.size()) { - _dataExport.timings[workload::Region::R1] = std::chrono::duration(inTimings[0]).count(); + // NOTE for reference: + // inTimings[0] = prePhysics entities + // inTimings[1] = prePhysics avatars + // inTimings[2] = stepPhysics + // inTimings[3] = postPhysics + // inTimings[4] = non-physical kinematics + // inTimings[5] = game loop + _dataExport.timings[workload::Region::R1] = std::chrono::duration(inTimings[2] + inTimings[3]).count(); _dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1]; - _dataExport.timings[workload::Region::R3] = std::chrono::duration(inTimings[1]).count(); + _dataExport.timings[workload::Region::R3] = std::chrono::duration(inTimings[4]).count(); doExport = true; } @@ -115,11 +122,29 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const } } -glm::vec2 Regulator::run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current) { - // Regulate next value based on current moving toward the goal budget - float error_ms = std::chrono::duration(_budget - measured).count(); - float coef = glm::clamp(error_ms / std::chrono::duration(regulationDuration).count(), -1.0f, 1.0f); - return current * (1.0f + coef * (error_ms < 0.0f ? _relativeStepDown : _relativeStepUp)); +glm::vec2 Regulator::run(const Timing_ns& deltaTime, const Timing_ns& measuredTime, const glm::vec2& currentFrontBack) { + // measure signal: average and noise + const float FILTER_TIMESCALE = 0.5f * (float)NSECS_PER_SECOND; + float del = deltaTime.count() / FILTER_TIMESCALE; + if (del > 1.0f) { + del = 1.0f; // clamp for stability + } + _measuredTimeAverage = (1.0f - del) * _measuredTimeAverage + del * measuredTime.count(); + float diff = measuredTime.count() - _measuredTimeAverage; + _measuredTimeNoiseSquared = (1.0f - del) * _measuredTimeNoiseSquared + del * diff * diff; + float noise = sqrtf(_measuredTimeNoiseSquared); + + // check budget + float budgetDelta = _budget.count() - _measuredTimeAverage; + if (abs(budgetDelta) < noise) { + // budget is within the noise + return currentFrontBack; + } + + // clamp the step factor + del = budgetDelta < 0.0f ? -_relativeStepDown: _relativeStepUp; + + return currentFrontBack * (1.0f + del); } glm::vec2 Regulator::clamp(const glm::vec2& backFront) const { @@ -128,17 +153,24 @@ glm::vec2 Regulator::clamp(const glm::vec2& backFront) const { } void ControlViews::regulateViews(workload::Views& outViews, const workload::Timings& timings) { - for (auto& outView : outViews) { for (int32_t r = 0; r < workload::Region::NUM_VIEW_REGIONS; r++) { outView.regionBackFronts[r] = regionBackFronts[r]; } } - auto loopDuration = std::chrono::nanoseconds{ std::chrono::milliseconds(16) }; - regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[0], regionBackFronts[workload::Region::R1]); - regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[0], regionBackFronts[workload::Region::R2]); - regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[1], regionBackFronts[workload::Region::R3]); + // Note: for reference: + // timings[0] = prePhysics entities + // timings[1] = prePhysics avatars + // timings[2] = stepPhysics + // timings[3] = postPhysics + // timings[4] = non-physical kinematics + // timings[5] = game loop + + auto loopDuration = timings[5]; + regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[2] + timings[3], regionBackFronts[workload::Region::R1]); + regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[2] + timings[3], regionBackFronts[workload::Region::R2]); + regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[4], regionBackFronts[workload::Region::R3]); enforceRegionContainment(); for (auto& outView : outViews) { diff --git a/libraries/workload/src/workload/ViewTask.h b/libraries/workload/src/workload/ViewTask.h index 4fdd7e0f4c..7c1e4944f5 100644 --- a/libraries/workload/src/workload/ViewTask.h +++ b/libraries/workload/src/workload/ViewTask.h @@ -37,8 +37,8 @@ namespace workload { { 250.0f, 16000.0f } }; - const float RELATIVE_STEP_DOWN = 0.05f; - const float RELATIVE_STEP_UP = 0.04f; + const float RELATIVE_STEP_DOWN = 0.11f; + const float RELATIVE_STEP_UP = 0.09f; class SetupViewsConfig : public Job::Config{ Q_OBJECT @@ -212,20 +212,31 @@ namespace workload { }; struct Regulator { - using Timing_ns = std::chrono::nanoseconds; - Timing_ns _budget{ std::chrono::milliseconds(2) }; glm::vec2 _minRange{ MIN_VIEW_BACK_FRONTS[0] }; glm::vec2 _maxRange{ MAX_VIEW_BACK_FRONTS[0] }; - glm::vec2 _relativeStepDown{ RELATIVE_STEP_DOWN }; glm::vec2 _relativeStepUp{ RELATIVE_STEP_UP }; - + Timing_ns _budget{ std::chrono::milliseconds(2) }; + float _measuredTimeAverage { 0.0f }; + float _measuredTimeNoiseSquared { 0.0f }; Regulator() {} - Regulator(const Timing_ns& budget_ns, const glm::vec2& minRange, const glm::vec2& maxRange, const glm::vec2& relativeStepDown, const glm::vec2& relativeStepUp) : - _budget(budget_ns), _minRange(minRange), _maxRange(maxRange), _relativeStepDown(relativeStepDown), _relativeStepUp(relativeStepUp) {} + Regulator(const Timing_ns& budget_ns, + const glm::vec2& minRange, + const glm::vec2& maxRange, + const glm::vec2& relativeStepDown, + const glm::vec2& relativeStepUp) : + _minRange(minRange), + _maxRange(maxRange), + _relativeStepDown(relativeStepDown), + _relativeStepUp(relativeStepUp), + _budget(budget_ns), + _measuredTimeAverage(budget_ns.count()), + _measuredTimeNoiseSquared(0.0f) + {} - glm::vec2 run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current); + void setBudget(const Timing_ns& budget) { _budget = budget; } + glm::vec2 run(const Timing_ns& deltaTime, const Timing_ns& measuredTime, const glm::vec2& currentFrontBack); glm::vec2 clamp(const glm::vec2& backFront) const; }; From a134357489173f6c34d9d1a9ee51faf9ba30a0c8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 29 Aug 2018 11:37:19 -0700 Subject: [PATCH 072/106] fix compile typo --- libraries/workload/src/workload/ViewTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index b05a4c0c5f..27b136d64e 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -142,9 +142,9 @@ glm::vec2 Regulator::run(const Timing_ns& deltaTime, const Timing_ns& measuredTi } // clamp the step factor - del = budgetDelta < 0.0f ? -_relativeStepDown: _relativeStepUp; + glm::vec2 stepDelta = budgetDelta < 0.0f ? -_relativeStepDown : _relativeStepUp; - return currentFrontBack * (1.0f + del); + return currentFrontBack * (1.0f + stepDelta); } glm::vec2 Regulator::clamp(const glm::vec2& backFront) const { From dcf2074d7eb85bb35b60f89dbf97737f92b798cd Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 29 Aug 2018 11:56:37 -0700 Subject: [PATCH 073/106] remove [ camera key, allow F to enter camera without selection --- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 -- scripts/system/edit.js | 13 ++----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 539546dd96..ddd51e9b9b 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -304,8 +304,6 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); - availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketLeft), "BracketLeft")); - availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketRight), "BracketRight")); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index dd53e8c395..b086b5624c 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1890,18 +1890,10 @@ function toggleKey(value) { } } function focusKey(value) { - if (value === 0) { // on release - if (selectionManager.hasSelection()) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } -} -function cameraKey(value) { if (value === 0) { // on release cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } function gridKey(value) { @@ -1917,7 +1909,6 @@ mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); -mapping.from([Controller.Hardware.Keyboard.BracketLeft]).to(cameraKey); mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); function recursiveAdd(newParentID, parentData) { From 0e53fa3f8b8a2edb850652e8b461b967a7669fd2 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 29 Aug 2018 12:05:46 -0700 Subject: [PATCH 074/106] only focus with selection --- scripts/system/edit.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index b086b5624c..e340c75a8b 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1892,8 +1892,10 @@ function toggleKey(value) { function focusKey(value) { if (value === 0) { // on release cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + if (selectionManager.hasSelection()) { + cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } } } function gridKey(value) { From f7d0cdda025964ae4d8680d2fdbdc0870c97e4bc Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Wed, 29 Aug 2018 20:44:21 +0100 Subject: [PATCH 075/106] added a check for whether the head to shoulder azimuth is greater than 100 degrees. If so unflip the chest azimuth --- interface/src/avatar/MyAvatar.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 75becd2c6d..78affe2052 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -819,12 +819,13 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // Find the vector halfway between the hip to hand azimuth vectors // This midpoint hand azimuth is in Avatar space glm::vec2 MyAvatar::computeHandAzimuth() const { - auto leftHandPoseAvatarSpace = getLeftHandPose(); - auto rightHandPoseAvatarSpace = getRightHandPose(); + controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); + controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); + controller::Pose headPoseAvatarSpace = getControllerPoseInAvatarFrame(controller::Action::HEAD); const float HALFWAY = 0.50f; glm::vec2 latestHipToHandController = _hipToHandController; - if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid() && headPoseAvatarSpace.isValid()) { // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. glm::vec2 oldAzimuthReading = _hipToHandController; @@ -833,11 +834,23 @@ glm::vec2 MyAvatar::computeHandAzimuth() const { } else { latestHipToHandController = glm::vec2(0.0f, -1.0f); } + + glm::vec3 headLookAtAvatarSpace = transformVectorFast(headPoseAvatarSpace.getMatrix(), glm::vec3(0.0f, 0.0f, 1.0f)); + glm::vec2 headAzimuthAvatarSpace = glm::vec2(headLookAtAvatarSpace.x, headLookAtAvatarSpace.z); + if (glm::length(headAzimuthAvatarSpace) > 0.0f) { + headAzimuthAvatarSpace = glm::normalize(headAzimuthAvatarSpace); + } else { + headAzimuthAvatarSpace = -latestHipToHandController; + } + // check the angular distance from forward and back float cosForwardAngle = glm::dot(latestHipToHandController, oldAzimuthReading); + float cosHeadShoulder = glm::dot(-latestHipToHandController, headAzimuthAvatarSpace); // if we are now closer to the 180 flip of the previous chest forward // then we negate our computed latestHipToHandController to keep the chest from flipping. - if (cosForwardAngle < 0.0f) { + // also check the head to shoulder azimuth difference if we negate. + // don't negate the chest azimuth if this is greater than 100 degrees. + if ((cosForwardAngle < 0.0f) && !(cosHeadShoulder < -0.2f)) { latestHipToHandController = -latestHipToHandController; } } From 6cc31e7397c8de9361f44febc3aa91278f4730d2 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 29 Aug 2018 13:30:13 -0700 Subject: [PATCH 076/106] AVX2 implementation of biquad2_4x4() --- libraries/audio/src/AudioHRTF.cpp | 6 ++- libraries/audio/src/avx2/AudioHRTF_avx2.cpp | 45 +++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 7895a70595..0f87df2346 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -457,8 +457,12 @@ static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, f interleave_4x4_SSE(src0, src1, src2, src3, dst, numFrames); } +void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames); + static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { - biquad2_4x4_SSE(src, dst, coef, state, numFrames); + + static auto f = cpuSupportsAVX2() ? biquad2_4x4_AVX2 : biquad2_4x4_SSE; + (*f)(src, dst, coef, state, numFrames); // dispatch } static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) { diff --git a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp index 501f010236..0c3c6b5096 100644 --- a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp +++ b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp @@ -87,4 +87,49 @@ void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3 _mm256_zeroupper(); } +// process 2 cascaded biquads on 4 channels (interleaved) +// biquads are computed in parallel, by adding one sample of delay +void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { + + // enable flush-to-zero mode to prevent denormals + unsigned int ftz = _MM_GET_FLUSH_ZERO_MODE(); + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + + // restore state + __m256 x0 = _mm256_setzero_ps(); + __m256 y0 = _mm256_loadu_ps(state[0]); + __m256 w1 = _mm256_loadu_ps(state[1]); + __m256 w2 = _mm256_loadu_ps(state[2]); + + // biquad coefs + __m256 b0 = _mm256_loadu_ps(coef[0]); + __m256 b1 = _mm256_loadu_ps(coef[1]); + __m256 b2 = _mm256_loadu_ps(coef[2]); + __m256 a1 = _mm256_loadu_ps(coef[3]); + __m256 a2 = _mm256_loadu_ps(coef[4]); + + for (int i = 0; i < numFrames; i++) { + + // x0 = (first biquad output << 128) | input + x0 = _mm256_insertf128_ps(_mm256_permute2f128_ps(y0, y0, 0x01), _mm_loadu_ps(&src[4*i]), 0); + + // transposed Direct Form II + y0 = _mm256_fmadd_ps(x0, b0, w1); + w1 = _mm256_fmadd_ps(x0, b1, w2); + w2 = _mm256_mul_ps(x0, b2); + w1 = _mm256_fnmadd_ps(y0, a1, w1); + w2 = _mm256_fnmadd_ps(y0, a2, w2); + + _mm_storeu_ps(&dst[4*i], _mm256_extractf128_ps(y0, 1)); // second biquad output + } + + // save state + _mm256_storeu_ps(state[0], y0); + _mm256_storeu_ps(state[1], w1); + _mm256_storeu_ps(state[2], w2); + + _MM_SET_FLUSH_ZERO_MODE(ftz); + _mm256_zeroupper(); +} + #endif From cda8b82ead7a74b411e4219905f5ca29e8a0f8e3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 29 Aug 2018 13:32:53 -0700 Subject: [PATCH 077/106] improve pointer performance --- interface/src/raypick/PathPointer.cpp | 77 ++++++++++++++----------- interface/src/raypick/PathPointer.h | 4 ++ interface/src/raypick/StylusPointer.cpp | 6 +- interface/src/raypick/StylusPointer.h | 2 + 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index d434c667de..0301136bfa 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -54,11 +54,13 @@ PathPointer::~PathPointer() { void PathPointer::setRenderState(const std::string& state) { withWriteLock([&] { if (!_currentRenderState.empty() && state != _currentRenderState) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + auto renderState = _renderStates.find(_currentRenderState); + if (renderState != _renderStates.end()) { + renderState->second->disable(); } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + auto defaultRenderState = _defaultRenderStates.find(_currentRenderState); + if (defaultRenderState != _defaultRenderStates.end()) { + defaultRenderState->second.second->disable(); } } _currentRenderState = state; @@ -142,52 +144,57 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick void PathPointer::updateVisuals(const PickResultPointer& pickResult) { IntersectionType type = getPickedObjectType(pickResult); - if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && + auto renderState = _renderStates.find(_currentRenderState); + auto defaultRenderState = _defaultRenderStates.find(_currentRenderState); + if (_enabled && !_currentRenderState.empty() && renderState != _renderStates.end() && (type != IntersectionType::NONE || _pathLength > 0.0f)) { glm::vec3 origin = getPickOrigin(pickResult); glm::vec3 end = getPickEnd(pickResult, _pathLength); glm::vec3 surfaceNormal = getPickedObjectNormal(pickResult); - _renderStates[_currentRenderState]->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar, - _followNormal, _followNormalStrength, _pathLength, pickResult); - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + renderState->second->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar, + _followNormal, _followNormalStrength, _pathLength, pickResult); + if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) { + defaultRenderState->second.second->disable(); } - } else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + } else if (_enabled && !_currentRenderState.empty() && defaultRenderState != _defaultRenderStates.end()) { + if (renderState != _renderStates.end() && renderState->second->isEnabled()) { + renderState->second->disable(); } glm::vec3 origin = getPickOrigin(pickResult); - glm::vec3 end = getPickEnd(pickResult, _defaultRenderStates[_currentRenderState].first); - _defaultRenderStates[_currentRenderState].second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, - _faceAvatar, _followNormal, _followNormalStrength, _defaultRenderStates[_currentRenderState].first, pickResult); + glm::vec3 end = getPickEnd(pickResult, defaultRenderState->second.first); + defaultRenderState->second.second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, + _faceAvatar, _followNormal, _followNormalStrength, defaultRenderState->second.first, pickResult); } else if (!_currentRenderState.empty()) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + if (renderState != _renderStates.end() && renderState->second->isEnabled()) { + renderState->second->disable(); } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) { + defaultRenderState->second.second->disable(); } } } void PathPointer::editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) { withWriteLock([&] { - updateRenderStateOverlay(_renderStates[state]->getStartID(), startProps); - updateRenderStateOverlay(_renderStates[state]->getEndID(), endProps); - QVariant startDim = startProps.toMap()["dimensions"]; - if (startDim.isValid()) { - _renderStates[state]->setStartDim(vec3FromVariant(startDim)); - } - QVariant endDim = endProps.toMap()["dimensions"]; - if (endDim.isValid()) { - _renderStates[state]->setEndDim(vec3FromVariant(endDim)); - } - QVariant rotation = endProps.toMap()["rotation"]; - if (rotation.isValid()) { - _renderStates[state]->setEndRot(quatFromVariant(rotation)); - } + auto renderState = _renderStates.find(state); + if (renderState != _renderStates.end()) { + updateRenderStateOverlay(renderState->second->getStartID(), startProps); + updateRenderStateOverlay(renderState->second->getEndID(), endProps); + QVariant startDim = startProps.toMap()["dimensions"]; + if (startDim.isValid()) { + renderState->second->setStartDim(vec3FromVariant(startDim)); + } + QVariant endDim = endProps.toMap()["dimensions"]; + if (endDim.isValid()) { + renderState->second->setEndDim(vec3FromVariant(endDim)); + } + QVariant rotation = endProps.toMap()["rotation"]; + if (rotation.isValid()) { + renderState->second->setEndRot(quatFromVariant(rotation)); + } - editRenderStatePath(state, pathProps); + editRenderStatePath(state, pathProps); + } }); } @@ -271,6 +278,7 @@ void StartEndRenderState::disable() { endProps.insert("ignoreRayIntersection", true); qApp->getOverlays().editOverlay(getEndID(), endProps); } + _enabled = false; } void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY, @@ -337,6 +345,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, endProps.insert("ignoreRayIntersection", doesEndIgnoreRays()); qApp->getOverlays().editOverlay(getEndID(), endProps); } + _enabled = true; } glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::vec3& origin) { diff --git a/interface/src/raypick/PathPointer.h b/interface/src/raypick/PathPointer.h index 44c1b7f82b..b3638d1f7d 100644 --- a/interface/src/raypick/PathPointer.h +++ b/interface/src/raypick/PathPointer.h @@ -47,6 +47,8 @@ public: virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult); + bool isEnabled() const { return _enabled; } + protected: OverlayID _startID; OverlayID _endID; @@ -59,6 +61,8 @@ protected: glm::quat _avgEndRot; bool _avgEndRotInitialized { false }; + + bool _enabled { true }; }; typedef std::unordered_map> RenderStateMap; diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 8c0fb59106..c74b6d8509 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -64,7 +64,9 @@ void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { return; } } - hide(); + if (_showing) { + hide(); + } } void StylusPointer::show(const StylusTip& tip) { @@ -80,6 +82,7 @@ void StylusPointer::show(const StylusTip& tip) { props["visible"] = true; qApp->getOverlays().editOverlay(_stylusOverlay, props); } + _showing = true; } void StylusPointer::hide() { @@ -88,6 +91,7 @@ void StylusPointer::hide() { props.insert("visible", false); qApp->getOverlays().editOverlay(_stylusOverlay, props); } + _showing = false; } bool StylusPointer::shouldHover(const PickResultPointer& pickResult) { diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 950b03b7c9..b1903840b3 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -76,6 +76,8 @@ private: static glm::vec3 findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction); static glm::vec2 findPos2D(const PickedObject& pickedObject, const glm::vec3& origin); + bool _showing { true }; + }; #endif // hifi_StylusPointer_h From 0981f08b22c5f04094428a84d8faeddbdf8b3347 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 29 Aug 2018 14:48:27 -0700 Subject: [PATCH 078/106] Do not propagate avatar scale in AvatarHeadTransformNode --- interface/src/avatar/MyAvatarHeadTransformNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatarHeadTransformNode.cpp b/interface/src/avatar/MyAvatarHeadTransformNode.cpp index 82c2f8b703..9c202ba94a 100644 --- a/interface/src/avatar/MyAvatarHeadTransformNode.cpp +++ b/interface/src/avatar/MyAvatarHeadTransformNode.cpp @@ -19,5 +19,5 @@ Transform MyAvatarHeadTransformNode::getTransform() { glm::quat headOri = myAvatar->getHeadOrientation(); glm::quat ori = headOri * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT); - return Transform(ori, myAvatar->scaleForChildren(), pos); + return Transform(ori, glm::vec3(1.0f), pos); } \ No newline at end of file From 78a438d89a8da8d0a7260c1ac9bb510b27803359 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 29 Aug 2018 14:55:14 -0700 Subject: [PATCH 079/106] Correct CollisionRegion jsdoc for 'joint' parameter --- libraries/shared/src/RegisteredMetaTypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 32ecfe8b6d..8fefc37741 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -263,7 +263,7 @@ public: * @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region. * @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay. * @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint) -* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Head," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. +* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar. */ class CollisionRegion : public MathPick { public: From 2d73b7845e95257bd40bf725eb597f28ba90de8f Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 29 Aug 2018 15:00:01 -0700 Subject: [PATCH 080/106] Code style fixes --- interface/src/raypick/CollisionPick.cpp | 3 +-- interface/src/raypick/CollisionPick.h | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 407595b86a..091ceb0794 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -94,8 +94,7 @@ bool CollisionPick::getShapeInfoReady() { } return false; - } - else { + } else { computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource); return true; } diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 7ff3670e53..f46f0742f5 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -25,11 +25,7 @@ public: CollisionPickResult() {} CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {} - CollisionPickResult(const CollisionRegion& searchRegion, - LoadState loadState, - const std::vector& entityIntersections, - const std::vector& avatarIntersections - ) : + CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector& entityIntersections, const std::vector& avatarIntersections) : PickResult(searchRegion.toVariantMap()), loadState(loadState), intersects(entityIntersections.size() || avatarIntersections.size()), From 9fb0f8b7c00fef665b94b7e56005063b394832f0 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 30 Aug 2018 00:25:53 +0100 Subject: [PATCH 081/106] added condition to prevent overwriting tracker input with estimated orientation for spine2 joint --- interface/src/avatar/MySkeletonModel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 8fd7d31956..77d1a87195 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -239,7 +239,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) { + if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && + myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && + !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { + AnimPose currentSpine2Pose; AnimPose currentHeadPose; AnimPose currentHipsPose; From 686f9fb18a623d6ce93aa988c7ee3f5f27e1c0cb Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 29 Aug 2018 16:12:58 -0700 Subject: [PATCH 082/106] Move load state flag from CollisionPickResult to CollisionRegion and make it a boolean --- interface/src/raypick/CollisionPick.cpp | 48 +++++++++++-------- interface/src/raypick/CollisionPick.h | 17 ++----- .../src/raypick/PickScriptingInterface.h | 3 -- libraries/shared/src/RegisteredMetaTypes.h | 9 +++- 4 files changed, 41 insertions(+), 36 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 091ceb0794..b2137fc80c 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -32,9 +32,6 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResu } intersects = entityIntersections.size() || avatarIntersections.size(); - if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) { - loadState = (LoadState)newCollisionResult->loadState; - } return std::make_shared(*this); } @@ -80,24 +77,29 @@ QVariantMap CollisionPickResult::toVariantMap() const { } variantMap["intersectingObjects"] = qIntersectingObjects; - variantMap["loaded"] = (loadState == LOAD_STATE_LOADED); variantMap["collisionRegion"] = pickVariant; return variantMap; } +bool CollisionPick::isLoaded() const { + return !_mathPick.shouldComputeShapeInfo() || (_cachedResource && _cachedResource->isLoaded()); +} + bool CollisionPick::getShapeInfoReady() { if (_mathPick.shouldComputeShapeInfo()) { if (_cachedResource && _cachedResource->isLoaded()) { computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource); - return true; + _mathPick.loaded = true; + } else { + _mathPick.loaded = false; } - - return false; } else { computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource); - return true; + _mathPick.loaded = true; } + + return _mathPick.loaded; } void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource) { @@ -349,15 +351,19 @@ CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool e if (collisionRegion.shouldComputeShapeInfo()) { _cachedResource = DependencyManager::get()->getCollisionGeometryResource(collisionRegion.modelURL); } + _mathPick.loaded = isLoaded(); } CollisionRegion CollisionPick::getMathematicalPick() const { + CollisionRegion mathPick = _mathPick; + if (!mathPick.loaded) { + mathPick.loaded = isLoaded(); + } if (!parentTransform) { - return _mathPick; + return mathPick; } else { - CollisionRegion transformedMathPick = _mathPick; - transformedMathPick.transform = parentTransform->getTransform().worldTransform(_mathPick.transform); - return transformedMathPick; + mathPick.transform = parentTransform->getTransform().worldTransform(mathPick.transform); + return mathPick; } } @@ -385,33 +391,35 @@ void CollisionPick::filterIntersections(std::vector& intersec } PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) { - if (!getShapeInfoReady()) { + if (!pick.loaded) { // Cannot compute result - return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), std::vector(), std::vector()); } + getShapeInfoReady(); auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); filterIntersections(entityIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector()); + return std::make_shared(pick, entityIntersections, std::vector()); } PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) { - return std::make_shared(pick, getShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick, std::vector(), std::vector()); } PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) { - if (!getShapeInfoReady()) { + if (!pick.loaded) { // Cannot compute result - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick, std::vector(), std::vector()); } + getShapeInfoReady(); auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold); filterIntersections(avatarIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector(), avatarIntersections); + return std::make_shared(pick, std::vector(), avatarIntersections); } PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), getShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), std::vector(), std::vector()); } Transform CollisionPick::getResultTransform() const { diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index f46f0742f5..a1636e4b47 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -16,18 +16,11 @@ class CollisionPickResult : public PickResult { public: - enum LoadState { - LOAD_STATE_UNKNOWN, - LOAD_STATE_NOT_LOADED, - LOAD_STATE_LOADED - }; - CollisionPickResult() {} CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {} - CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector& entityIntersections, const std::vector& avatarIntersections) : + CollisionPickResult(const CollisionRegion& searchRegion, const std::vector& entityIntersections, const std::vector& avatarIntersections) : PickResult(searchRegion.toVariantMap()), - loadState(loadState), intersects(entityIntersections.size() || avatarIntersections.size()), entityIntersections(entityIntersections), avatarIntersections(avatarIntersections) @@ -38,10 +31,8 @@ public: avatarIntersections = collisionPickResult.avatarIntersections; entityIntersections = collisionPickResult.entityIntersections; intersects = collisionPickResult.intersects; - loadState = collisionPickResult.loadState; } - LoadState loadState { LOAD_STATE_UNKNOWN }; bool intersects { false }; std::vector entityIntersections; std::vector avatarIntersections; @@ -61,7 +52,7 @@ public: CollisionRegion getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { - return std::make_shared(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::vector(), std::vector()); + return std::make_shared(pickVariant, std::vector(), std::vector()); } PickResultPointer getEntityIntersection(const CollisionRegion& pick) override; PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override; @@ -69,7 +60,9 @@ public: PickResultPointer getHUDIntersection(const CollisionRegion& pick) override; Transform getResultTransform() const override; protected: - // Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use. + // Returns true if the resource for _mathPick.shapeInfo is loaded or if a resource is not needed. + bool isLoaded() const; + // Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use. bool getShapeInfoReady(); void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); void computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 717436a13b..4d99309618 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -152,9 +152,6 @@ public: * @property {CollisionRegion} collisionRegion The CollisionRegion that was used. Valid even if there was no intersection. */ - // TODO: Add this to the CollisionPickResult jsdoc once model collision picks are working - //* @property {boolean} loaded If the CollisionRegion was successfully loaded (may be false if a model was used) - /**jsdoc * Information about the Collision Pick's intersection with an object * diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 8fefc37741..8fb1301484 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -253,6 +253,8 @@ public: } }; +// TODO: Add "loaded" to CollisionRegion jsdoc once model collision picks are supported. + /**jsdoc * A CollisionRegion defines a volume for checking collisions in the physics simulation. @@ -270,6 +272,7 @@ public: CollisionRegion() { } CollisionRegion(const CollisionRegion& collisionRegion) : + loaded(collisionRegion.loaded), modelURL(collisionRegion.modelURL), shapeInfo(std::make_shared()), transform(collisionRegion.transform), @@ -279,6 +282,7 @@ public: } CollisionRegion(const QVariantMap& pickVariant) { + // "loaded" is not deserialized here because there is no way to know if the shape is actually loaded if (pickVariant["shape"].isValid()) { auto shape = pickVariant["shape"].toMap(); if (!shape.empty()) { @@ -322,6 +326,7 @@ public: shape["dimensions"] = vec3toVariant(transform.getScale()); collisionRegion["shape"] = shape; + collisionRegion["loaded"] = loaded; collisionRegion["threshold"] = threshold; @@ -339,7 +344,8 @@ public: } bool operator==(const CollisionRegion& other) const { - return threshold == other.threshold && + return loaded == other.loaded && + threshold == other.threshold && glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) && glm::all(glm::equal(transform.getRotation(), other.transform.getRotation())) && glm::all(glm::equal(transform.getScale(), other.transform.getScale())) && @@ -359,6 +365,7 @@ public: } // We can't load the model here because it would create a circular dependency, so we delegate that responsibility to the owning CollisionPick + bool loaded = false; QUrl modelURL; // We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick From 0926968e2bd260ce54a8bb0360632c50dd924921 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 29 Aug 2018 16:17:53 -0700 Subject: [PATCH 083/106] Do not have depth as a parameter in getTransform(..) when called in NestableTransformNode --- libraries/shared/src/NestableTransformNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/NestableTransformNode.cpp b/libraries/shared/src/NestableTransformNode.cpp index 7fb7187aee..17456d69ce 100644 --- a/libraries/shared/src/NestableTransformNode.cpp +++ b/libraries/shared/src/NestableTransformNode.cpp @@ -21,7 +21,7 @@ Transform NestableTransformNode::getTransform() { } bool success; - Transform jointWorldTransform = nestable->getTransform(_jointIndex, success, 30); + Transform jointWorldTransform = nestable->getTransform(_jointIndex, success); if (success) { return jointWorldTransform; From aaf5f593c598ab1b0df24b89a2cbf52beac39e86 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 29 Aug 2018 14:29:15 -0700 Subject: [PATCH 084/106] send MyAvatar avatar entity updates through entity tree --- interface/src/AvatarBookmarks.cpp | 18 ++--------- interface/src/avatar/MyAvatar.cpp | 52 +++++++++++++++++++++++++------ interface/src/avatar/MyAvatar.h | 5 ++- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 4119b843e1..5c79bedc9a 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -145,20 +145,9 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { emit bookmarkDeleted(bookmarkName); } -bool isWearableEntity(const EntityItemPointer& entity) { - return entity->isVisible() && (entity->getParentJointIndex() != INVALID_JOINT_INDEX || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) - && (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == DependencyManager::get()->getMyAvatar()->getSelfID()); -} - void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); - + myAvatar->removeWearableAvatarEntities(); addAvatarEntities(avatarEntities); } @@ -183,10 +172,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { auto myAvatar = DependencyManager::get()->getMyAvatar(); auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); + myAvatar->removeWearableAvatarEntities(); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); qCDebug(interfaceapp) << "Avatar On " << avatarUrl; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 923d071cc0..7bf18f468c 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1703,18 +1703,50 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); } -void MyAvatar::removeAvatarEntities(const std::function& condition) { +bool isWearableEntity(const EntityItemPointer& entity) { + return entity->isVisible() + && (entity->getParentJointIndex() != INVALID_JOINT_INDEX + || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) + && (entity->getParentID() == DependencyManager::get()->getSessionUUID() + || entity->getParentID() == AVATAR_SELF_ID); +} + +void MyAvatar::clearAvatarEntities() { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - if (entityTree) { - entityTree->withWriteLock([&] { - AvatarEntityMap avatarEntities = getAvatarEntityData(); - for (auto entityID : avatarEntities.keys()) { - if (!condition || condition(entityID)) { - entityTree->deleteEntity(entityID, true, true); - } - } + + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + entityTree->withWriteLock([&entityID, &entityTree] { + // remove this entity first from the entity tree + entityTree->deleteEntity(entityID, true, true); }); + + // remove the avatar entity from our internal list + // (but indicate it doesn't need to be pulled from the tree) + clearAvatarEntity(entityID, false); + } +} + +void MyAvatar::removeWearableAvatarEntities() { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + + if (entityTree) { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + auto entity = entityTree->findEntityByID(entityID); + if (entity && isWearableEntity(entity)) { + entityTree->withWriteLock([&entityID, &entityTree] { + // remove this entity first from the entity tree + entityTree->deleteEntity(entityID, true, true); + }); + + // remove the avatar entity from our internal list + // (but indicate it doesn't need to be pulled from the tree) + clearAvatarEntity(entityID, false); + } + } } } @@ -2116,7 +2148,7 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) } // clear any existing avatar entities - setAvatarEntityData(AvatarEntityMap()); + clearAvatarEntities(); for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ba6348cc22..bb0df1af62 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -931,7 +931,8 @@ public: * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); - void removeAvatarEntities(const std::function& condition = {}); + void clearAvatarEntities(); + void removeWearableAvatarEntities(); /**jsdoc * @function MyAvatar.isFlying @@ -1782,4 +1783,6 @@ void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMod QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys); +bool isWearableEntity(const EntityItemPointer& entity); + #endif // hifi_MyAvatar_h From 1fe909f9ad32a01c95dfa15766e8bbcd1cd8acd1 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 29 Aug 2018 16:33:51 -0700 Subject: [PATCH 085/106] Make CollisionPick::getResultTransform() always return a valid transform --- interface/src/raypick/CollisionPick.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index b2137fc80c..b163f23df6 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -423,11 +423,6 @@ PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) } Transform CollisionPick::getResultTransform() const { - PickResultPointer result = getPrevPickResult(); - if (!result) { - return Transform(); - } - Transform transform; transform.setTranslation(getMathematicalPick().transform.getTranslation()); return transform; From b8cd5dd8b97fef9a22fee72dcc5a13df69b7a6e7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 29 Aug 2018 16:58:19 -0700 Subject: [PATCH 086/106] Fixup PR13870: Make the feature work in Master --- interface/src/Application.cpp | 10 +++++----- libraries/networking/src/AccountManager.h | 3 ++- scripts/system/commerce/wallet.js | 6 ++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 44bba6250b..af29ea0bd1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1735,11 +1735,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QTimer* settingsTimer = new QTimer(); moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{ connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{ - bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); - if (autoLogout) { - auto accountManager = DependencyManager::get(); - accountManager->logout(); - } // Disconnect the signal from the save settings QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings); // Stop the settings timer @@ -2491,6 +2486,11 @@ void Application::cleanupBeforeQuit() { } DependencyManager::destroy(); + bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); + if (autoLogout) { + DependencyManager::get()->removeAccountFromFile(); + } + _displayPlugin.reset(); PluginManager::getInstance()->shutdown(); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index a79b69fe2b..b122115dd0 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -96,6 +96,8 @@ public: QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL(); } + void removeAccountFromFile(); + public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); @@ -133,7 +135,6 @@ private: void operator=(AccountManager const& other) = delete; void persistAccountToFile(); - void removeAccountFromFile(); void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 8730273e7c..5939b36438 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -407,8 +407,10 @@ } function onUsernameChanged() { - Settings.setValue("wallet/autoLogout", false); - Settings.setValue("wallet/savedUsername", ""); + if (Account.username !== Settings.getValue("wallet/savedUsername")) { + Settings.setValue("wallet/autoLogout", false); + Settings.setValue("wallet/savedUsername", ""); + } } // Function Name: fromQml() From 248c2621a43a6b9dc67e9d6002f7aa7f77e769e4 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 29 Aug 2018 17:57:54 -0700 Subject: [PATCH 087/106] fix blendshapes crash --- .../render-utils/src/CauterizedModel.cpp | 5 +- libraries/render-utils/src/Model.cpp | 87 +++++++++---------- libraries/render-utils/src/Model.h | 2 + 3 files changed, 45 insertions(+), 49 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 3966d04b6d..259036f500 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -86,9 +86,8 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - if (!fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) { - _blendedVertexBuffers[i] = std::make_shared(); - _blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + if (!fbxGeometry.meshes[i].blendshapes.empty()) { + initializeBlendshapes(fbxGeometry.meshes[i], i); } auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 71250c6483..946275a312 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -302,52 +302,11 @@ bool Model::updateGeometry() { assert(_meshStates.empty()); const FBXGeometry& fbxGeometry = getFBXGeometry(); - int i = 0; foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.push_back(state); - - if (!mesh.blendshapes.isEmpty()) { - if (!_blendedVertexBuffers[i]) { - _blendedVertexBuffers[i] = std::make_shared(); - } - const auto& buffer = _blendedVertexBuffers[i]; - QVector normalsAndTangents; - normalsAndTangents.resize(2 * mesh.normals.size()); - - // Interleave normals and tangents - // Parallel version for performance - tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { - auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end()); - auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end()); - auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin(); - - for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; - normalIt != normalsRange.second; - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto& finalNormal = *normalIt; - const auto& finalTangent = *tangentIt; -#endif - *normalsAndTangentsIt = finalNormal; - ++normalsAndTangentsIt; - *normalsAndTangentsIt = finalTangent; - ++normalsAndTangentsIt; - } - }); - const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3); - buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType)); - buffer->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData()); - buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); - mesh.normalsAndTangents = normalsAndTangents; - } - i++; } needFullUpdate = true; emit rigReady(); @@ -1514,10 +1473,10 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo assert(buffer); buffer->resize(mesh.vertices.size() * sizeof(glm::vec3) + mesh.normalsAndTangents.size() * sizeof(NormalType)); buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + index * sizeof(glm::vec3)); - buffer->setSubData(verticesSize, 2 * mesh.normals.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType)); + buffer->setSubData(verticesSize, mesh.normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data() + normalAndTangentIndex * sizeof(NormalType)); index += vertexCount; - normalAndTangentIndex += 2 * mesh.normals.size(); + normalAndTangentIndex += mesh.normalsAndTangents.size(); } } @@ -1555,6 +1514,42 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const { return _modelMeshRenderItemIDs; } +void Model::initializeBlendshapes(const FBXMesh& mesh, int index) { + QVector normalsAndTangents; + normalsAndTangents.resize(2 * mesh.normals.size()); + + // Interleave normals and tangents + // Parallel version for performance + tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { + auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end()); + auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end()); + auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin(); + + for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; + normalIt != normalsRange.second; + ++normalIt, ++tangentIt) { +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); +#else + const auto& finalNormal = *normalIt; + const auto& finalTangent = *tangentIt; +#endif + *normalsAndTangentsIt = finalNormal; + ++normalsAndTangentsIt; + *normalsAndTangentsIt = finalTangent; + ++normalsAndTangentsIt; + } + }); + const auto verticesSize = mesh.vertices.size() * sizeof(glm::vec3); + _blendedVertexBuffers[index] = std::make_shared(); + _blendedVertexBuffers[index]->resize(mesh.vertices.size() * sizeof(glm::vec3) + normalsAndTangents.size() * sizeof(NormalType)); + _blendedVertexBuffers[index]->setSubData(0, verticesSize, (const gpu::Byte*) mesh.vertices.constData()); + _blendedVertexBuffers[index]->setSubData(verticesSize, normalsAndTangents.size() * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + mesh.normalsAndTangents = normalsAndTangents; +} + void Model::createRenderItemSet() { assert(isLoaded()); const auto& meshes = _renderGeometry->getMeshes(); @@ -1583,7 +1578,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); - auto fbxGeometry = getFBXGeometry(); + auto& fbxGeometry = getFBXGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1593,8 +1588,8 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - if (fbxGeometry.meshes[i].blendshapes.empty() && !_blendedVertexBuffers[i]) { - _blendedVertexBuffers[i] = std::make_shared(); + if (!fbxGeometry.meshes[i].blendshapes.empty()) { + initializeBlendshapes(fbxGeometry.meshes[i], i); } _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 677be61261..eaca068c10 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -482,6 +482,8 @@ protected: bool shouldInvalidatePayloadShapeKey(int meshIndex); + void initializeBlendshapes(const FBXMesh& mesh, int index); + private: float _loadingPriority { 0.0f }; From 6583864597136a7b41ab7abe0613b825333510b7 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 29 Aug 2018 19:19:20 -0700 Subject: [PATCH 088/106] AVX2 implementation of interleave_4x4() --- libraries/audio/src/AudioHRTF.cpp | 6 +++- libraries/audio/src/avx2/AudioHRTF_avx2.cpp | 37 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 0f87df2346..655527a65c 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -453,8 +453,12 @@ static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* ds (*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch } +void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames); + static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) { - interleave_4x4_SSE(src0, src1, src2, src3, dst, numFrames); + + static auto f = cpuSupportsAVX2() ? interleave_4x4_AVX2 : interleave_4x4_SSE; + (*f)(src0, src1, src2, src3, dst, numFrames); // dispatch } void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames); diff --git a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp index 0c3c6b5096..9000df367f 100644 --- a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp +++ b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp @@ -87,6 +87,43 @@ void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3 _mm256_zeroupper(); } +// 4 channel planar to interleaved +void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) { + + assert(numFrames % 8 == 0); + + for (int i = 0; i < numFrames; i += 8) { + + __m256 x0 = _mm256_loadu_ps(&src0[i]); + __m256 x1 = _mm256_loadu_ps(&src1[i]); + __m256 x2 = _mm256_loadu_ps(&src2[i]); + __m256 x3 = _mm256_loadu_ps(&src3[i]); + + // interleave (4x4 matrix transpose) + __m256 t0 = _mm256_unpacklo_ps(x0, x1); + __m256 t1 = _mm256_unpackhi_ps(x0, x1); + __m256 t2 = _mm256_unpacklo_ps(x2, x3); + __m256 t3 = _mm256_unpackhi_ps(x2, x3); + + x0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0)); + x1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2)); + x2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0)); + x3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2)); + + t0 = _mm256_permute2f128_ps(x0, x1, 0x20); + t1 = _mm256_permute2f128_ps(x2, x3, 0x20); + t2 = _mm256_permute2f128_ps(x0, x1, 0x31); + t3 = _mm256_permute2f128_ps(x2, x3, 0x31); + + _mm256_storeu_ps(&dst[4*i+0], t0); + _mm256_storeu_ps(&dst[4*i+8], t1); + _mm256_storeu_ps(&dst[4*i+16], t2); + _mm256_storeu_ps(&dst[4*i+24], t3); + } + + _mm256_zeroupper(); +} + // process 2 cascaded biquads on 4 channels (interleaved) // biquads are computed in parallel, by adding one sample of delay void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { From a4bb8cb6221747730b27cf6de3ca6e7f41567255 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 30 Aug 2018 09:08:02 -0700 Subject: [PATCH 089/106] Add comment on future of PickTransformNodes --- libraries/pointers/src/PickTransformNode.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/pointers/src/PickTransformNode.h b/libraries/pointers/src/PickTransformNode.h index 7547d3f181..b4c92f3ba7 100644 --- a/libraries/pointers/src/PickTransformNode.h +++ b/libraries/pointers/src/PickTransformNode.h @@ -10,6 +10,7 @@ #include "TransformNode.h" +// TODO: Remove this class when Picks are converted to SpatiallyNestables class PickTransformNode : public TransformNode { public: PickTransformNode(unsigned int uid); From 6c4862910e70fe0236b8ce9e4539a03c205fe802 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 30 Aug 2018 10:13:35 -0700 Subject: [PATCH 090/106] smoother edges for workload region feedback response --- libraries/workload/src/workload/ViewTask.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index 27b136d64e..9d0c693149 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -135,15 +135,15 @@ glm::vec2 Regulator::run(const Timing_ns& deltaTime, const Timing_ns& measuredTi float noise = sqrtf(_measuredTimeNoiseSquared); // check budget - float budgetDelta = _budget.count() - _measuredTimeAverage; - if (abs(budgetDelta) < noise) { - // budget is within the noise + float offsetFromTarget = _budget.count() - _measuredTimeAverage; + if (fabsf(offsetFromTarget) < noise) { + // budget is within the noise --> do nothing return currentFrontBack; } - // clamp the step factor - glm::vec2 stepDelta = budgetDelta < 0.0f ? -_relativeStepDown : _relativeStepUp; - + // compute response + glm::vec2 stepDelta = offsetFromTarget < 0.0f ? -_relativeStepDown : _relativeStepUp; + stepDelta *= glm::min(1.0f, (fabsf(offsetFromTarget) - noise) / noise); // ease out of "do nothing" return currentFrontBack * (1.0f + stepDelta); } From 8b1fcfcce909c735b448df52b2ada545e0f042c1 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 30 Aug 2018 10:16:27 -0700 Subject: [PATCH 091/106] fix double windows platform --- 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 c365e61624..eae42ea32e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -913,7 +913,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, - STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_WINDOWS } }); + STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); From 4b7ca76ebdecd76950acb7b2f0daecc174a397bd Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 30 Aug 2018 14:15:57 -0700 Subject: [PATCH 092/106] Create correct folder. --- libraries/shared/src/shared/FileUtils.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/shared/FileUtils.cpp b/libraries/shared/src/shared/FileUtils.cpp index 9bdd65bf11..0709a53602 100644 --- a/libraries/shared/src/shared/FileUtils.cpp +++ b/libraries/shared/src/shared/FileUtils.cpp @@ -156,10 +156,12 @@ bool FileUtils::canCreateFile(const QString& fullPath) { qDebug(shared) << "unable to overwrite file '" << fullPath << "'"; return false; } - QDir dir(fileInfo.absolutePath()); + + QString absolutePath = fileInfo.absolutePath(); + QDir dir(absolutePath); if (!dir.exists()) { - if (!dir.mkpath(fullPath)) { - qDebug(shared) << "unable to create dir '" << dir.absolutePath() << "'"; + if (!dir.mkpath(absolutePath)) { + qDebug(shared) << "unable to create dir '" << absolutePath << "'"; return false; } } From 0f6fdef9284c95bc8a68e031bca7796618ec519d Mon Sep 17 00:00:00 2001 From: NissimHadar Date: Thu, 30 Aug 2018 15:06:05 -0700 Subject: [PATCH 093/106] Fixed compilation error (within #ifdef) --- libraries/fbx/src/OBJReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 501fb72130..c46a1e234c 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -273,7 +273,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename << " emissive texture:" << currentMaterial.emissiveTextureFilename << - " bump texture:" << currentMaterial.bumpTextureFilename; + " bump texture:" << currentMaterial.bumpTextureFilename << " opacity texture:" << currentMaterial.opacityTextureFilename; #endif return; From e94d7de99afb4f8914150a9cca3cc524d4fa8503 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 30 Aug 2018 16:05:25 -0700 Subject: [PATCH 094/106] Add JS API hook for getting overlay IDs from a pointer via Pointer.getPointerProperties --- interface/src/raypick/LaserPointer.cpp | 33 ++++++++++++++++++ interface/src/raypick/LaserPointer.h | 2 ++ interface/src/raypick/ParabolaPointer.cpp | 31 +++++++++++++++++ interface/src/raypick/ParabolaPointer.h | 2 ++ .../src/raypick/PointerScriptingInterface.cpp | 34 ++++++++++++------- .../src/raypick/PointerScriptingInterface.h | 8 +++++ interface/src/raypick/StylusPointer.cpp | 4 +++ interface/src/raypick/StylusPointer.h | 2 ++ libraries/pointers/src/Pointer.h | 2 ++ libraries/pointers/src/PointerManager.cpp | 9 +++++ libraries/pointers/src/PointerManager.h | 1 + 11 files changed, 116 insertions(+), 12 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 2382a95105..ee92a0fa6d 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -35,6 +35,39 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& } } +QVariantMap LaserPointer::toVariantMap() const { + QVariantMap qVariantMap; + + QVariantList qRenderStates; + for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) { + auto renderState = iter->second; + QVariantMap qRenderState; + qRenderState["name"] = iter->first.c_str(); + qRenderState["start"] = renderState->getStartID(); + qRenderState["path"] = std::static_pointer_cast(renderState)->getPathID(); + qRenderState["end"] = renderState->getEndID(); + qRenderStates.append(qRenderState); + } + qVariantMap["renderStates"] = qRenderStates; + + QVariantList qDefaultRenderStates; + for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) { + float distance = iter->second.first; + auto defaultRenderState = iter->second.second; + QVariantMap qDefaultRenderState; + + qDefaultRenderState["name"] = iter->first.c_str(); + qDefaultRenderState["distance"] = distance; + qDefaultRenderState["start"] = defaultRenderState->getStartID(); + qDefaultRenderState["path"] = std::static_pointer_cast(defaultRenderState)->getPathID(); + qDefaultRenderState["end"] = defaultRenderState->getEndID(); + qDefaultRenderStates.append(qDefaultRenderState); + } + qVariantMap["defaultRenderStates"] = qDefaultRenderStates; + + return qVariantMap; +} + glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const { auto rayPickResult = std::static_pointer_cast(pickResult); return (rayPickResult ? vec3FromVariant(rayPickResult->pickVariant["origin"]) : glm::vec3(0.0f)); diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 95a5bccc6c..c0ac3259d9 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -42,6 +42,8 @@ public: LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled); + QVariantMap toVariantMap() const override; + static std::shared_ptr buildRenderState(const QVariantMap& propMap); protected: diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 097c98340c..b8c6224f08 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -60,6 +60,37 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria } } +QVariantMap ParabolaPointer::toVariantMap() const { + QVariantMap qVariantMap; + + QVariantList qRenderStates; + for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) { + auto renderState = iter->second; + QVariantMap qRenderState; + qRenderState["name"] = iter->first.c_str(); + qRenderState["start"] = renderState->getStartID(); + qRenderState["end"] = renderState->getEndID(); + qRenderStates.append(qRenderState); + } + qVariantMap["renderStates"] = qRenderStates; + + QVariantList qDefaultRenderStates; + for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) { + float distance = iter->second.first; + auto defaultRenderState = iter->second.second; + QVariantMap qDefaultRenderState; + + qDefaultRenderState["name"] = iter->first.c_str(); + qDefaultRenderState["distance"] = distance; + qDefaultRenderState["start"] = defaultRenderState->getStartID(); + qDefaultRenderState["end"] = defaultRenderState->getEndID(); + qDefaultRenderStates.append(qDefaultRenderState); + } + qVariantMap["defaultRenderStates"] = qDefaultRenderStates; + + return qVariantMap; +} + glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) const { auto parabolaPickResult = std::static_pointer_cast(pickResult); return (parabolaPickResult ? vec3FromVariant(parabolaPickResult->pickVariant["origin"]) : glm::vec3(0.0f)); diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h index 93f9a7b055..526abe3b0d 100644 --- a/interface/src/raypick/ParabolaPointer.h +++ b/interface/src/raypick/ParabolaPointer.h @@ -97,6 +97,8 @@ public: ParabolaPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled); + QVariantMap toVariantMap() const override; + static std::shared_ptr buildRenderState(const QVariantMap& propMap); protected: diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index a9b9b6e9ea..cd3ffdac82 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -76,16 +76,19 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) * @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined. */ /**jsdoc - * A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. + * A set of properties which define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. * * @typedef {object} Pointers.RayPointerRenderState * @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} - * @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). - * An overlay to represent the beginning of the Ray Pointer, if desired. - * @property {Overlays.OverlayProperties} [path] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field), which must be "line3d". - * An overlay to represent the path of the Ray Pointer, if desired. - * @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). - * An overlay to represent the end of the Ray Pointer, if desired. + * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Ray Pointer, + * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). + * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. + * @property {Overlays.OverlayProperties|QUuid} [path] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the path of the Ray Pointer, + * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field), which must be "line3d". + * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. + * @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Ray Pointer, + * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). + * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. */ /**jsdoc * A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick. @@ -225,11 +228,14 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope * * @typedef {object} Pointers.ParabolaPointerRenderState * @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} -* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). -* An overlay to represent the beginning of the Parabola Pointer, if desired. -* @property {Pointers.ParabolaProperties} [path] The rendering properties of the parabolic path defined by the Parabola Pointer. -* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). -* An overlay to represent the end of the Parabola Pointer, if desired. +* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Parabola Pointer, +* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). +* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. +* @property {Pointers.ParabolaProperties} [path] When using {@link Pointers.createPointer}, the optionally defined rendering properties of the parabolic path defined by the Parabola Pointer. +* Not defined in {@link Pointers.getPointerProperties}. +* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Parabola Pointer, +* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). +* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. */ /**jsdoc * A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick. @@ -375,4 +381,8 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const result = pickResult->toVariantMap(); } return result; +} + +QVariantMap PointerScriptingInterface::getPointerProperties(unsigned int uid) const { + return DependencyManager::get()->getPointerProperties(uid); } \ No newline at end of file diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 9fe05182c7..94f1d62552 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -203,6 +203,14 @@ public: */ Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get()->isMouse(uid); } + /**jsdoc + * Returns information about an existing Pointer + * @function Pointers.getPointerState + * @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}. + * @returns {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} The information about the Pointer. + * Currently only includes renderStates and defaultRenderStates with associated overlay IDs. + */ + Q_INVOKABLE QVariantMap getPointerProperties(unsigned int uid) const; }; #endif // hifi_PointerScriptingInterface_h diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 8c0fb59106..d963b0c670 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -203,6 +203,10 @@ void StylusPointer::setRenderState(const std::string& state) { } } +QVariantMap StylusPointer::toVariantMap() const { + return QVariantMap(); +} + glm::vec3 StylusPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) { switch (pickedObject.type) { case ENTITY: diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 950b03b7c9..1fe8b047e5 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -33,6 +33,8 @@ public: void setRenderState(const std::string& state) override; void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override {} + QVariantMap toVariantMap() const override; + static OverlayID buildStylusOverlay(const QVariantMap& properties); protected: diff --git a/libraries/pointers/src/Pointer.h b/libraries/pointers/src/Pointer.h index 0c842dbd88..4264a60079 100644 --- a/libraries/pointers/src/Pointer.h +++ b/libraries/pointers/src/Pointer.h @@ -50,6 +50,8 @@ public: virtual void setRenderState(const std::string& state) = 0; virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0; + virtual QVariantMap toVariantMap() const = 0; + virtual void setPrecisionPicking(bool precisionPicking); virtual void setIgnoreItems(const QVector& ignoreItems) const; virtual void setIncludeItems(const QVector& includeItems) const; diff --git a/libraries/pointers/src/PointerManager.cpp b/libraries/pointers/src/PointerManager.cpp index be890da392..922f0bb5bc 100644 --- a/libraries/pointers/src/PointerManager.cpp +++ b/libraries/pointers/src/PointerManager.cpp @@ -77,6 +77,15 @@ PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const { return result; } +QVariantMap PointerManager::getPointerProperties(unsigned int uid) const { + auto pointer = find(uid); + if (pointer) { + return pointer->toVariantMap(); + } else { + return QVariantMap(); + } +} + void PointerManager::update() { auto cachedPointers = resultWithReadLock>>([&] { return _pointers; diff --git a/libraries/pointers/src/PointerManager.h b/libraries/pointers/src/PointerManager.h index b98558622f..2d0b2a107e 100644 --- a/libraries/pointers/src/PointerManager.h +++ b/libraries/pointers/src/PointerManager.h @@ -30,6 +30,7 @@ public: void setRenderState(unsigned int uid, const std::string& renderState) const; void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const; PickResultPointer getPrevPickResult(unsigned int uid) const; + QVariantMap getPointerProperties(unsigned int uid) const; void setPrecisionPicking(unsigned int uid, bool precisionPicking) const; void setIgnoreItems(unsigned int uid, const QVector& ignoreEntities) const; From 9bf088d32b9c803c4974061ced8170b7ed999185 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 31 Aug 2018 10:28:45 -0700 Subject: [PATCH 095/106] Just assign mathPick.loaded in CollisionPick::getMathematicalPick --- interface/src/raypick/CollisionPick.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index b163f23df6..654378cc9b 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -356,9 +356,7 @@ CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool e CollisionRegion CollisionPick::getMathematicalPick() const { CollisionRegion mathPick = _mathPick; - if (!mathPick.loaded) { - mathPick.loaded = isLoaded(); - } + mathPick.loaded = isLoaded(); if (!parentTransform) { return mathPick; } else { From 4d85cb17f36b4f73582419ee944492725e1f5db3 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 31 Aug 2018 10:46:17 -0700 Subject: [PATCH 096/106] Include full transform in CollisionPick::getResultTransform --- interface/src/raypick/CollisionPick.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 654378cc9b..7d0276875b 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -421,7 +421,5 @@ PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) } Transform CollisionPick::getResultTransform() const { - Transform transform; - transform.setTranslation(getMathematicalPick().transform.getTranslation()); - return transform; + return Transform(getMathematicalPick().transform); } \ No newline at end of file From f71fc833099aaa678f41779adc9fd1a2bf439c97 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 31 Aug 2018 10:48:48 -0700 Subject: [PATCH 097/106] Remove PickResult::needsToCompareResults --- interface/src/raypick/CollisionPick.h | 1 - libraries/pointers/src/Pick.h | 1 - libraries/pointers/src/PickCacheOptimizer.h | 6 +++--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index a1636e4b47..ce8b3bd199 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -40,7 +40,6 @@ public: QVariantMap toVariantMap() const override; bool doesIntersect() const override { return intersects; } - bool needsToCompareResults() const override { return true; } bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return true; } PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override; diff --git a/libraries/pointers/src/Pick.h b/libraries/pointers/src/Pick.h index 9c4de6c46e..099a791407 100644 --- a/libraries/pointers/src/Pick.h +++ b/libraries/pointers/src/Pick.h @@ -115,7 +115,6 @@ public: } virtual bool doesIntersect() const = 0; - virtual bool needsToCompareResults() const { return doesIntersect(); } // for example: if we want the closest result, compare based on distance // if we want all results, combine them diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index ae515fc8d7..448d67b5a9 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -92,7 +92,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetEntityIntersection(mathematicalPick); if (entityRes) { - cacheResult(entityRes->needsToCompareResults(), entityRes, entityKey, res, mathematicalPick, results, pick); + cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick); } } } @@ -102,7 +102,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetOverlayIntersection(mathematicalPick); if (overlayRes) { - cacheResult(overlayRes->needsToCompareResults(), overlayRes, overlayKey, res, mathematicalPick, results, pick); + cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick); } } } @@ -112,7 +112,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetAvatarIntersection(mathematicalPick); if (avatarRes) { - cacheResult(avatarRes->needsToCompareResults(), avatarRes, avatarKey, res, mathematicalPick, results, pick); + cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick); } } } From 2e290ea798254481fe7dd91af5916686a4543f29 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 31 Aug 2018 10:52:00 -0700 Subject: [PATCH 098/106] Only create a NestableTransformNode if the spatially nestable exists --- interface/src/raypick/PickScriptingInterface.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index f551c0ec90..6dedf3fca1 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -373,7 +373,8 @@ std::shared_ptr PickScriptingInterface::createTransformNode(const if (propMap["parentJointIndex"].isValid()) { parentJointIndex = propMap["parentJointIndex"].toInt(); } - if (success && !nestablePointer.expired()) { + auto sharedNestablePointer = nestablePointer.lock(); + if (success && sharedNestablePointer) { return std::make_shared(nestablePointer, parentJointIndex); } } From b5862aca2aa8c4d397f8f962468cccf4d205576c Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 31 Aug 2018 10:53:20 -0700 Subject: [PATCH 099/106] Use bracket initialization for CollisionRegion.loaded --- libraries/shared/src/RegisteredMetaTypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 8fb1301484..02f5fa570c 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -365,7 +365,7 @@ public: } // We can't load the model here because it would create a circular dependency, so we delegate that responsibility to the owning CollisionPick - bool loaded = false; + bool loaded { false }; QUrl modelURL; // We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick From 62344c106dadb5fe54687f7c9b9d1c8b4ab34e46 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 31 Aug 2018 11:19:53 -0700 Subject: [PATCH 100/106] Make render states a map when returned from PointerScriptingInterface::getPointerProperties --- interface/src/raypick/LaserPointer.cpp | 10 ++++------ interface/src/raypick/ParabolaPointer.cpp | 10 ++++------ .../src/raypick/PointerScriptingInterface.cpp | 20 +++++++++++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index ee92a0fa6d..afbb278dc7 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -38,30 +38,28 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant& QVariantMap LaserPointer::toVariantMap() const { QVariantMap qVariantMap; - QVariantList qRenderStates; + QVariantMap qRenderStates; for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) { auto renderState = iter->second; QVariantMap qRenderState; - qRenderState["name"] = iter->first.c_str(); qRenderState["start"] = renderState->getStartID(); qRenderState["path"] = std::static_pointer_cast(renderState)->getPathID(); qRenderState["end"] = renderState->getEndID(); - qRenderStates.append(qRenderState); + qRenderStates[iter->first.c_str()] = qRenderState; } qVariantMap["renderStates"] = qRenderStates; - QVariantList qDefaultRenderStates; + QVariantMap qDefaultRenderStates; for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) { float distance = iter->second.first; auto defaultRenderState = iter->second.second; QVariantMap qDefaultRenderState; - qDefaultRenderState["name"] = iter->first.c_str(); qDefaultRenderState["distance"] = distance; qDefaultRenderState["start"] = defaultRenderState->getStartID(); qDefaultRenderState["path"] = std::static_pointer_cast(defaultRenderState)->getPathID(); qDefaultRenderState["end"] = defaultRenderState->getEndID(); - qDefaultRenderStates.append(qDefaultRenderState); + qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState; } qVariantMap["defaultRenderStates"] = qDefaultRenderStates; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index b8c6224f08..fc3aecdb51 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -63,28 +63,26 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria QVariantMap ParabolaPointer::toVariantMap() const { QVariantMap qVariantMap; - QVariantList qRenderStates; + QVariantMap qRenderStates; for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) { auto renderState = iter->second; QVariantMap qRenderState; - qRenderState["name"] = iter->first.c_str(); qRenderState["start"] = renderState->getStartID(); qRenderState["end"] = renderState->getEndID(); - qRenderStates.append(qRenderState); + qRenderStates[iter->first.c_str()] = qRenderState; } qVariantMap["renderStates"] = qRenderStates; - QVariantList qDefaultRenderStates; + QVariantMap qDefaultRenderStates; for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) { float distance = iter->second.first; auto defaultRenderState = iter->second.second; QVariantMap qDefaultRenderState; - qDefaultRenderState["name"] = iter->first.c_str(); qDefaultRenderState["distance"] = distance; qDefaultRenderState["start"] = defaultRenderState->getStartID(); qDefaultRenderState["end"] = defaultRenderState->getEndID(); - qDefaultRenderStates.append(qDefaultRenderState); + qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState; } qVariantMap["defaultRenderStates"] = qDefaultRenderStates; diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index cd3ffdac82..7209e402a1 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -79,7 +79,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) * A set of properties which define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something. * * @typedef {object} Pointers.RayPointerRenderState - * @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} + * @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Ray Pointer, * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. @@ -102,8 +102,12 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) * @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. 0-1. If 0 or 1, * the normal will follow exactly. * @property {boolean} [enabled=false] - * @property {Pointers.RayPointerRenderState[]} [renderStates] A list of different visual states to switch between. - * @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection. + * @property {Pointers.RayPointerRenderState[]|Object.} [renderStates] A collection of different visual states to switch between. + * When using {@link Pointers.createPointer}, a list of RayPointerRenderStates. + * When returned from {@link Pointers.getPointerProperties}, a map between render state names and RayPointRenderStates. + * @property {Pointers.DefaultRayPointerRenderState[]|Object.} [defaultRenderStates] A collection of different visual states to use if there is no intersection. + * When using {@link Pointers.createPointer}, a list of DefaultRayPointerRenderStates. + * When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultRayPointRenderStates. * @property {boolean} [hover=false] If this Pointer should generate hover events. * @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation. */ @@ -227,7 +231,7 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope * A set of properties used to define the visual aspect of a Parabola Pointer in the case that the Pointer is intersecting something. * * @typedef {object} Pointers.ParabolaPointerRenderState -* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} +* @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState} * @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Parabola Pointer, * using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a type field). * When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise. @@ -249,8 +253,12 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope * @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. 0-1. If 0 or 1, * the normal will follow exactly. * @property {boolean} [enabled=false] -* @property {Pointers.ParabolaPointerRenderState[]} [renderStates] A list of different visual states to switch between. -* @property {Pointers.DefaultParabolaPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection. +* @property {Pointers.ParabolaPointerRenderState[]|Object.} [renderStates] A collection of different visual states to switch between. +* When using {@link Pointers.createPointer}, a list of ParabolaPointerRenderStates. +* When returned from {@link Pointers.getPointerProperties}, a map between render state names and ParabolaPointerRenderStates. +* @property {Pointers.DefaultParabolaPointerRenderState[]|Object.} [defaultRenderStates] A collection of different visual states to use if there is no intersection. +* When using {@link Pointers.createPointer}, a list of DefaultParabolaPointerRenderStates. +* When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultParabolaPointerRenderStates. * @property {boolean} [hover=false] If this Pointer should generate hover events. * @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation. */ From 727bd6b05af29d3edd9b3d23a34428418e553712 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 31 Aug 2018 13:31:26 -0700 Subject: [PATCH 101/106] don't bid for simulation ownership of locked things --- libraries/physics/src/EntityMotionState.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 925cfee740..2c130274bc 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -778,8 +778,10 @@ void EntityMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma bool EntityMotionState::shouldSendBid() const { // NOTE: this method is only ever called when the entity's simulation is NOT locally owned - return _body->isActive() && (_region == workload::Region::R1) && - glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority(); + return _body->isActive() + && (_region == workload::Region::R1) + && glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority() + && !_entity->getLocked(); } uint8_t EntityMotionState::computeFinalBidPriority() const { From 028092f80398a1127da3bf019e59675b5192566c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 31 Aug 2018 13:33:45 -0700 Subject: [PATCH 102/106] fix blendshapes one last time --- libraries/render-utils/src/CauterizedModel.cpp | 4 ++-- libraries/render-utils/src/CauterizedModel.h | 2 +- libraries/render-utils/src/Model.cpp | 6 +++--- libraries/render-utils/src/Model.h | 2 +- libraries/render-utils/src/SoftAttachmentModel.cpp | 4 ++-- libraries/render-utils/src/SoftAttachmentModel.h | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 259036f500..2754697db7 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -102,7 +102,7 @@ void CauterizedModel::createRenderItemSet() { } } -void CauterizedModel::updateClusterMatrices() { +void CauterizedModel::updateClusterMatrices(bool triggerBlendshapes) { PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices"); if (!_needsUpdateClusterMatrices || !isLoaded()) { @@ -175,7 +175,7 @@ void CauterizedModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/CauterizedModel.h b/libraries/render-utils/src/CauterizedModel.h index 36a96fb006..12cf921e5b 100644 --- a/libraries/render-utils/src/CauterizedModel.h +++ b/libraries/render-utils/src/CauterizedModel.h @@ -33,7 +33,7 @@ public: void createRenderItemSet() override; - virtual void updateClusterMatrices() override; + virtual void updateClusterMatrices(bool triggerBlendshapes = true) override; void updateRenderItems() override; const Model::MeshState& getCauterizeMeshState(int index) const; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 4158b14223..8287b9cca6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -976,7 +976,7 @@ bool Model::addToScene(const render::ScenePointer& scene, render::Transaction& transaction, render::Item::Status::Getters& statusGetters) { if (!_addedToScene && isLoaded()) { - updateClusterMatrices(); + updateClusterMatrices(false); if (_modelMeshRenderItems.empty()) { createRenderItemSet(); } @@ -1486,7 +1486,7 @@ void Model::computeMeshPartLocalBounds() { } // virtual -void Model::updateClusterMatrices() { +void Model::updateClusterMatrices(bool triggerBlendshapes) { DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices"); if (!_needsUpdateClusterMatrices || !isLoaded()) { @@ -1515,7 +1515,7 @@ void Model::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 4642515294..5ab50da162 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -159,7 +159,7 @@ public: bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; } virtual void simulate(float deltaTime, bool fullUpdate = true); - virtual void updateClusterMatrices(); + virtual void updateClusterMatrices(bool triggerBlendshapes = true); /// Returns a reference to the shared geometry. const Geometry::Pointer& getGeometry() const { return _renderGeometry; } diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index f9b90f38ba..93ce8f595a 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -31,7 +31,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const { // virtual // use the _rigOverride matrices instead of the Model::_rig -void SoftAttachmentModel::updateClusterMatrices() { +void SoftAttachmentModel::updateClusterMatrices(bool triggerBlendshapes) { if (!_needsUpdateClusterMatrices) { return; } @@ -78,7 +78,7 @@ void SoftAttachmentModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/SoftAttachmentModel.h b/libraries/render-utils/src/SoftAttachmentModel.h index 4335c1634e..03038c35f3 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.h +++ b/libraries/render-utils/src/SoftAttachmentModel.h @@ -27,7 +27,7 @@ public: ~SoftAttachmentModel(); void updateRig(float deltaTime, glm::mat4 parentTransform) override; - void updateClusterMatrices() override; + void updateClusterMatrices(bool triggerBlendshapes = true) override; protected: int getJointIndexOverride(int i) const; From 96aa0549be8d21d05ea5b3826b5d9a869d50c225 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 1 Sep 2018 12:08:49 -0700 Subject: [PATCH 103/106] AVX2 implementation of crossfade_4x2() --- libraries/audio/src/AudioHRTF.cpp | 6 ++- libraries/audio/src/avx2/AudioHRTF_avx2.cpp | 57 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 655527a65c..eea01b703b 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -469,8 +469,12 @@ static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3] (*f)(src, dst, coef, state, numFrames); // dispatch } +void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames); + static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) { - crossfade_4x2_SSE(src, dst, win, numFrames); + + static auto f = cpuSupportsAVX2() ? crossfade_4x2_AVX2 : crossfade_4x2_SSE; + (*f)(src, dst, win, numFrames); // dispatch } static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) { diff --git a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp index 9000df367f..b82098b72f 100644 --- a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp +++ b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp @@ -169,4 +169,61 @@ void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8 _mm256_zeroupper(); } +// crossfade 4 inputs into 2 outputs with accumulation (interleaved) +void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames) { + + assert(numFrames % 8 == 0); + + for (int i = 0; i < numFrames; i += 8) { + + __m256 f0 = _mm256_loadu_ps(&win[i]); + + __m256 x0 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+0])); + __m256 x1 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+4])); + __m256 x2 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+8])); + __m256 x3 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+12])); + + x0 = _mm256_insertf128_ps(x0, _mm_loadu_ps(&src[4*i+16]), 1); + x1 = _mm256_insertf128_ps(x1, _mm_loadu_ps(&src[4*i+20]), 1); + x2 = _mm256_insertf128_ps(x2, _mm_loadu_ps(&src[4*i+24]), 1); + x3 = _mm256_insertf128_ps(x3, _mm_loadu_ps(&src[4*i+28]), 1); + + __m256 y0 = _mm256_loadu_ps(&dst[2*i+0]); + __m256 y1 = _mm256_loadu_ps(&dst[2*i+8]); + + // deinterleave (4x4 matrix transpose) + __m256 t0 = _mm256_unpacklo_ps(x0, x1); + __m256 t1 = _mm256_unpackhi_ps(x0, x1); + __m256 t2 = _mm256_unpacklo_ps(x2, x3); + __m256 t3 = _mm256_unpackhi_ps(x2, x3); + + x0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0)); + x1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2)); + x2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0)); + x3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2)); + + // crossfade + x0 = _mm256_sub_ps(x0, x2); + x1 = _mm256_sub_ps(x1, x3); + x2 = _mm256_fmadd_ps(f0, x0, x2); + x3 = _mm256_fmadd_ps(f0, x1, x3); + + // interleave + t0 = _mm256_unpacklo_ps(x2, x3); + t1 = _mm256_unpackhi_ps(x2, x3); + + x0 = _mm256_permute2f128_ps(t0, t1, 0x20); + x1 = _mm256_permute2f128_ps(t0, t1, 0x31); + + // accumulate + y0 = _mm256_add_ps(y0, x0); + y1 = _mm256_add_ps(y1, x1); + + _mm256_storeu_ps(&dst[2*i+0], y0); + _mm256_storeu_ps(&dst[2*i+8], y1); + } + + _mm256_zeroupper(); +} + #endif From 94e8ee99f5cade6f6f9a9bcf179c6b229a05cc37 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Sep 2018 13:06:49 -0700 Subject: [PATCH 104/106] AVX2 implementation of interpolate() --- libraries/audio/src/AudioHRTF.cpp | 6 +++++- libraries/audio/src/avx2/AudioHRTF_avx2.cpp | 22 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index eea01b703b..b52f624620 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -477,8 +477,12 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame (*f)(src, dst, win, numFrames); // dispatch } +void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain); + static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) { - interpolate_SSE(src0, src1, dst, frac, gain); + + static auto f = cpuSupportsAVX2() ? interpolate_AVX2 : interpolate_SSE; + (*f)(src0, src1, dst, frac, gain); // dispatch } #else // portable reference code diff --git a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp index b82098b72f..aadbb2d0cd 100644 --- a/libraries/audio/src/avx2/AudioHRTF_avx2.cpp +++ b/libraries/audio/src/avx2/AudioHRTF_avx2.cpp @@ -226,4 +226,26 @@ void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames) _mm256_zeroupper(); } +// linear interpolation with gain +void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain) { + + __m256 f0 = _mm256_set1_ps(gain * (1.0f - frac)); + __m256 f1 = _mm256_set1_ps(gain * frac); + + static_assert(HRTF_TAPS % 8 == 0, "HRTF_TAPS must be a multiple of 8"); + + for (int k = 0; k < HRTF_TAPS; k += 8) { + + __m256 x0 = _mm256_loadu_ps(&src0[k]); + __m256 x1 = _mm256_loadu_ps(&src1[k]); + + x0 = _mm256_mul_ps(f0, x0); + x0 = _mm256_fmadd_ps(f1, x1, x0); + + _mm256_storeu_ps(&dst[k], x0); + } + + _mm256_zeroupper(); +} + #endif From f5b7232f58906e82e24c22b12e778746f946b033 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 3 Sep 2018 14:00:30 -0700 Subject: [PATCH 105/106] Cleanup --- libraries/audio/src/AudioHRTF.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index b52f624620..b639301404 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -446,41 +446,32 @@ static void interpolate_SSE(const float* src0, const float* src1, float* dst, fl void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames); +void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames); +void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames); +void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames); +void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain); static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) { - static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE); (*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch } -void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames); - static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) { - static auto f = cpuSupportsAVX2() ? interleave_4x4_AVX2 : interleave_4x4_SSE; (*f)(src0, src1, src2, src3, dst, numFrames); // dispatch } -void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames); - static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) { - static auto f = cpuSupportsAVX2() ? biquad2_4x4_AVX2 : biquad2_4x4_SSE; (*f)(src, dst, coef, state, numFrames); // dispatch } -void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames); - static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) { - static auto f = cpuSupportsAVX2() ? crossfade_4x2_AVX2 : crossfade_4x2_SSE; (*f)(src, dst, win, numFrames); // dispatch } -void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain); - static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) { - static auto f = cpuSupportsAVX2() ? interpolate_AVX2 : interpolate_SSE; (*f)(src0, src1, dst, frac, gain); // dispatch } From 24cf4adb44a55d79fe3a97f1a72dc4c2b238032f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 4 Sep 2018 09:08:42 -0700 Subject: [PATCH 106/106] Fix MS17902: Prevent case where My Purchases says Open instead of Uninstall --- interface/src/commerce/QmlCommerce.cpp | 59 +++++++++++++++----------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 1c6600cf3f..06da18148f 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -40,11 +40,9 @@ QmlCommerce::QmlCommerce() { connect(ledger.data(), &Ledger::transferAssetToUsernameResult, this, &QmlCommerce::transferAssetToUsernameResult); connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult); connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult); - + auto accountManager = DependencyManager::get(); - connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { - setPassphrase(""); - }); + connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { setPassphrase(""); }); _appsPath = PathUtils::getAppDataPath() + "Apps/"; } @@ -105,7 +103,11 @@ void QmlCommerce::balance() { } } -void QmlCommerce::inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) { +void QmlCommerce::inventory(const QString& editionFilter, + const QString& typeFilter, + const QString& titleFilter, + const int& page, + const int& perPage) { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); QStringList cachedPublicKeys = wallet->listPublicKeys(); @@ -166,24 +168,30 @@ void QmlCommerce::certificateInfo(const QString& certificateId) { ledger->certificateInfo(certificateId); } -void QmlCommerce::transferAssetToNode(const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage) { +void QmlCommerce::transferAssetToNode(const QString& nodeID, + const QString& certificateID, + const int& amount, + const QString& optionalMessage) { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); QStringList keys = wallet->listPublicKeys(); if (keys.count() == 0) { - QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } }; + QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } }; return emit transferAssetToNodeResult(result); } QString key = keys[0]; ledger->transferAssetToNode(key, nodeID, certificateID, amount, optionalMessage); } -void QmlCommerce::transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage) { +void QmlCommerce::transferAssetToUsername(const QString& username, + const QString& certificateID, + const int& amount, + const QString& optionalMessage) { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); QStringList keys = wallet->listPublicKeys(); if (keys.count() == 0) { - QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } }; + QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } }; return emit transferAssetToUsernameResult(result); } QString key = keys[0]; @@ -194,10 +202,7 @@ void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& cert auto ledger = DependencyManager::get(); ledger->updateLocation(certificateID, DependencyManager::get()->getPlaceName(), true); qApp->replaceDomainContent(itemHref); - QJsonObject messageProperties = { - { "status", "SuccessfulRequestToReplaceContent" }, - { "content_set_url", itemHref } - }; + QJsonObject messageProperties = { { "status", "SuccessfulRequestToReplaceContent" }, { "content_set_url", itemHref } }; UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties); emit contentSetChanged(itemHref); @@ -214,10 +219,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) { QDir directory(_appsPath); QStringList apps = directory.entryList(QStringList("*.app.json")); - foreach(QString appFileName, apps) { - installedAppsFromMarketplace += appFileName; - installedAppsFromMarketplace += ","; - + foreach (QString appFileName, apps) { // If we were supplied a "justInstalledAppID" argument, that means we're entering this function // to get the new list of installed apps immediately after installing an app. // In that case, the app we installed may not yet have its associated script running - @@ -243,10 +245,12 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) { // delete the .app.json from the user's local disk. if (!runningScripts.contains(scriptURL)) { if (!appFile.remove()) { - qCWarning(commerce) - << "Couldn't delete local .app.json file (app's script isn't running). App filename is:" - << appFileName; + qCWarning(commerce) << "Couldn't delete local .app.json file (app's script isn't running). App filename is:" + << appFileName; } + } else { + installedAppsFromMarketplace += appFileName; + installedAppsFromMarketplace += ","; } } else { qCDebug(commerce) << "Couldn't open local .app.json file for reading."; @@ -317,7 +321,9 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) { // Read from the file to know what .js script to stop QFile appFile(_appsPath + "/" + appHref.fileName()); if (!appFile.open(QIODevice::ReadOnly)) { - qCDebug(commerce) << "Couldn't open local .app.json file for deletion. Cannot continue with app uninstallation. App filename is:" << appHref.fileName(); + qCDebug(commerce) + << "Couldn't open local .app.json file for deletion. Cannot continue with app uninstallation. App filename is:" + << appHref.fileName(); return false; } QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll()); @@ -325,13 +331,15 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) { QString scriptUrl = appFileJsonObject["scriptURL"].toString(); if (!DependencyManager::get()->stopScript(scriptUrl.trimmed(), false)) { - qCWarning(commerce) << "Couldn't stop script during app uninstall. Continuing anyway. ScriptURL is:" << scriptUrl.trimmed(); + qCWarning(commerce) << "Couldn't stop script during app uninstall. Continuing anyway. ScriptURL is:" + << scriptUrl.trimmed(); } // Delete the .app.json from the filesystem // remove() closes the file first. if (!appFile.remove()) { - qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" << appHref.fileName(); + qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" + << appHref.fileName(); } QFileInfo appFileInfo(appFile); @@ -352,7 +360,8 @@ bool QmlCommerce::openApp(const QString& itemHref) { QJsonObject appFileJsonObject = appFileJsonDocument.object(); QString homeUrl = appFileJsonObject["homeURL"].toString(); - auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast( + DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); if (homeUrl.contains(".qml", Qt::CaseInsensitive)) { tablet->loadQMLSource(homeUrl); } else if (homeUrl.contains(".html", Qt::CaseInsensitive)) { @@ -377,7 +386,7 @@ void QmlCommerce::updateItem(const QString& certificateId) { auto wallet = DependencyManager::get(); QStringList keys = wallet->listPublicKeys(); if (keys.count() == 0) { - QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } }; + QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } }; return emit updateItemResult(result); } QString key = keys[0];