From b2ac4381eb935e89eb907e523501f2330ae0a425 Mon Sep 17 00:00:00 2001 From: volansystech Date: Thu, 18 May 2017 19:13:59 +0530 Subject: [PATCH 01/43] Solve Jitteriness when scrolling the content in HMD mode -> Marketplace using the scrollbar. --- interface/src/ui/overlays/Web3DOverlay.cpp | 41 +++++++++++----------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index d9eab9a78d..37dce9d6e4 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -427,29 +427,30 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { event.getButtons() == PointerEvent::SecondaryButton) { return; } + if (_inputMode == Touch) { + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(touchPointState); + point.setPos(windowPoint); + point.setScreenPos(windowPoint); + QList touchPoints; + touchPoints.push_back(point); - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(touchPointState); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); - QList touchPoints; - touchPoints.push_back(point); + QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); + touchEvent->setWindow(_webSurface->getWindow()); + touchEvent->setTarget(_webSurface->getRootItem()); + touchEvent->setTouchPoints(touchPoints); + touchEvent->setTouchPointStates(touchPointState); - QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); - touchEvent->setWindow(_webSurface->getWindow()); - touchEvent->setTarget(_webSurface->getRootItem()); - touchEvent->setTouchPoints(touchPoints); - touchEvent->setTouchPointStates(touchPointState); + QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); + } else { + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. + // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). + // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". - QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); - - // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. - // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). - // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". - - QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + } } void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { From 6da46c2d4c35350ada8a16a0c613e8513206bb06 Mon Sep 17 00:00:00 2001 From: volansystech Date: Sat, 20 May 2017 02:01:10 +0530 Subject: [PATCH 02/43] Resolve Mouse hover issue. --- interface/src/ui/overlays/Web3DOverlay.cpp | 52 ++++++++++------------ 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 37dce9d6e4..8a06c0fdec 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -370,8 +370,7 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { QEvent::Type touchType; Qt::TouchPointState touchPointState; - QEvent::Type mouseType; - + Qt::MouseButton button = Qt::NoButton; Qt::MouseButtons buttons = Qt::NoButton; if (event.getButton() == PointerEvent::PrimaryButton) { @@ -385,18 +384,15 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { case PointerEvent::Press: touchType = QEvent::TouchBegin; touchPointState = Qt::TouchPointPressed; - mouseType = QEvent::MouseButtonPress; break; case PointerEvent::Release: touchType = QEvent::TouchEnd; touchPointState = Qt::TouchPointReleased; - mouseType = QEvent::MouseButtonRelease; break; case PointerEvent::Move: touchType = QEvent::TouchUpdate; touchPointState = Qt::TouchPointMoved; - mouseType = QEvent::MouseMove; - + if (((event.getButtons() & PointerEvent::PrimaryButton) > 0) != this->_pressed) { // Mouse was pressed/released while off the overlay; convert touch and mouse events to press/release to reflect // current mouse/touch status. @@ -404,12 +400,10 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { if (this->_pressed) { touchType = QEvent::TouchBegin; touchPointState = Qt::TouchPointPressed; - mouseType = QEvent::MouseButtonPress; } else { touchType = QEvent::TouchEnd; touchPointState = Qt::TouchPointReleased; - mouseType = QEvent::MouseButtonRelease; } button = Qt::LeftButton; @@ -427,30 +421,30 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { event.getButtons() == PointerEvent::SecondaryButton) { return; } - if (_inputMode == Touch) { - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(touchPointState); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); - QList touchPoints; - touchPoints.push_back(point); + + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(touchPointState); + point.setPos(windowPoint); + point.setScreenPos(windowPoint); + QList touchPoints; + touchPoints.push_back(point); - QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); - touchEvent->setWindow(_webSurface->getWindow()); - touchEvent->setTarget(_webSurface->getRootItem()); - touchEvent->setTouchPoints(touchPoints); - touchEvent->setTouchPointStates(touchPointState); + QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); + touchEvent->setWindow(_webSurface->getWindow()); + touchEvent->setTarget(_webSurface->getRootItem()); + touchEvent->setTouchPoints(touchPoints); + touchEvent->setTouchPointStates(touchPointState); - QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); - } else { - // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. - // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). - // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". + QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent, Qt::HighEventPriority); + + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. + // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). + // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". - QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); - } + QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent, Qt::LowEventPriority); + } void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { From cab6f3f7c37aa7e07965564a4341187ef8345971 Mon Sep 17 00:00:00 2001 From: volansystech Date: Thu, 25 May 2017 17:28:19 +0530 Subject: [PATCH 03/43] Solve Jitteriness while scrolling. --- interface/src/ui/overlays/Web3DOverlay.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 8a06c0fdec..bfb5291170 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -370,7 +370,8 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { QEvent::Type touchType; Qt::TouchPointState touchPointState; - + QEvent::Type mouseType; + Qt::MouseButton button = Qt::NoButton; Qt::MouseButtons buttons = Qt::NoButton; if (event.getButton() == PointerEvent::PrimaryButton) { @@ -384,15 +385,18 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { case PointerEvent::Press: touchType = QEvent::TouchBegin; touchPointState = Qt::TouchPointPressed; + mouseType = QEvent::MouseButtonPress; break; case PointerEvent::Release: touchType = QEvent::TouchEnd; touchPointState = Qt::TouchPointReleased; + mouseType = QEvent::MouseButtonRelease; break; case PointerEvent::Move: touchType = QEvent::TouchUpdate; touchPointState = Qt::TouchPointMoved; - + mouseType = QEvent::MouseMove; + if (((event.getButtons() & PointerEvent::PrimaryButton) > 0) != this->_pressed) { // Mouse was pressed/released while off the overlay; convert touch and mouse events to press/release to reflect // current mouse/touch status. @@ -400,10 +404,12 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { if (this->_pressed) { touchType = QEvent::TouchBegin; touchPointState = Qt::TouchPointPressed; + mouseType = QEvent::MouseButtonPress; } else { touchType = QEvent::TouchEnd; touchPointState = Qt::TouchPointReleased; + mouseType = QEvent::MouseButtonRelease; } button = Qt::LeftButton; @@ -436,14 +442,18 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { touchEvent->setTouchPoints(touchPoints); touchEvent->setTouchPointStates(touchPointState); - QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent, Qt::HighEventPriority); + QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); + if (this->_pressed && event.getType() == PointerEvent::Move) { + return; + } + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". - QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent, Qt::LowEventPriority); + QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); } From f0c3a0ac2876c984900572d7123f89cdb985347e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 25 May 2017 13:39:47 -0700 Subject: [PATCH 04/43] Fix SendQueue not updating lastReceiverResponse when recv handshake ack --- libraries/networking/src/udt/SendQueue.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index c14ae0a39c..0c029751aa 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -241,6 +241,9 @@ void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) { std::lock_guard locker { _handshakeMutex }; _hasReceivedHandshakeACK = true; } + + _lastReceiverResponse = QDateTime::currentMSecsSinceEpoch(); + // Notify on the handshake ACK condition _handshakeACKCondition.notify_one(); } From 8538c700b5dfc16a0582aa5c0d33a5585887a722 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 25 May 2017 21:41:41 +0100 Subject: [PATCH 05/43] saving head work --- interface/resources/controllers/vive.json | 22 ++++---- plugins/openvr/src/ViveControllerManager.cpp | 53 ++++++++++++++++++-- plugins/openvr/src/ViveControllerManager.h | 26 +++++----- 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 591b6f515e..984f9c65fb 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -34,36 +34,32 @@ { "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" }, { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" }, - { "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] }, - { "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] }, + { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, + { "from": "Vive.RightHand", "to": "Standard.RightHand" }, { "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] }, { "from": "Vive.RightFoot", "to" : "Standard.RightFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] }, { "from": "Vive.Hips", "to" : "Standard.Hips", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] }, { "from": "Vive.Spine2", "to" : "Standard.Spine2", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], - "when": [ "Application.InHMD"] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] }, - { "from": "Vive.Head", "to" : "Standard.Head", "when" : [ "Application.InHMD"] }, + { "from": "Vive.Head", "to" : "Standard.Head"}, - { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when" : [ "Application.InHMD"] }, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when" : [ "Application.InHMD"] } + { "from": "Vive.RightArm", "to" : "Standard.RightArm"}, + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm"} ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 016b2a1c55..a9bc066dc2 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -51,8 +51,7 @@ static const int MIN_PUCK_COUNT = 2; static const int MIN_FEET_AND_HIPS = 3; static const int MIN_FEET_HIPS_CHEST = 4; static const int MIN_FEET_HIPS_SHOULDERS = 5; -static const int MIN_FEET_HIPS_CHEST_AND_HANDS = 6; -static const int MIN_FEET_HIPS_SHOULDERS_AND_HANDS = 7; +static const int MIN_FEET_HIPS_CHEST_HEAD = 5; static const int FIRST_FOOT = 0; static const int SECOND_FOOT = 1; static const int HIP = 2; @@ -177,6 +176,7 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro _configStringMap[Config::FeetAndHips] = QString("FeetAndHips"); _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest"); _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders"); + _configStringMap[Config::FeetHipsChestAndHead] = QString("FeetHipsChestAndHead"); if (openVrSupported()) { createPreferences(); @@ -351,12 +351,18 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr calibrateFeet(defaultToReferenceMat, inputCalibration); calibrateHips(defaultToReferenceMat, inputCalibration); calibrateChest(defaultToReferenceMat, inputCalibration); - } else if (_config == Config::FeetHipsAndShoulders && puckCount >= MIN_FEET_HIPS_SHOULDERS){ + } else if (_config == Config::FeetHipsAndShoulders && puckCount >= MIN_FEET_HIPS_SHOULDERS) { calibrateFeet(defaultToReferenceMat, inputCalibration); calibrateHips(defaultToReferenceMat, inputCalibration); int firstShoulderIndex = 3; int secondShoulderIndex = 4; calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex); + } else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) { + glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); + calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration); + calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); + calibrateChest(headPuckDefaultToReferenceMat, inputCalibration); + calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); } else { qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks"; uncalibrate(); @@ -371,6 +377,7 @@ void ViveControllerManager::InputDevice::uncalibrate() { _pucksOffset.clear(); _jointToPuckMap.clear(); _calibrated = false; + _overrideHead = false; } void ViveControllerManager::InputDevice::updateCalibratedLimbs() { @@ -380,6 +387,10 @@ void ViveControllerManager::InputDevice::updateCalibratedLimbs() { _poseStateMap[controller::SPINE2] = addOffsetToPuckPose(controller::SPINE2); _poseStateMap[controller::RIGHT_ARM] = addOffsetToPuckPose(controller::RIGHT_ARM); _poseStateMap[controller::LEFT_ARM] = addOffsetToPuckPose(controller::LEFT_ARM); + + if (_overrideHead) { + _poseStateMap[controller::HEAD] = addOffsetToPuckPose(controller::HEAD); + } } controller::Pose ViveControllerManager::InputDevice::addOffsetToPuckPose(int joint) const { @@ -445,6 +456,27 @@ void ViveControllerManager::InputDevice::handleHandController(float deltaTime, u } } +glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration) { + size_t headPuckIndex = _validTrackedObjects.size() - 1; + controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second; + + // make the head puck rotation to match the default head rotation + glm::quat headPuckRotation = cancelOutRollAndPitch(headPuckPose.getRotation()); + glm::quat defaultHeadRotation = glmExtractRotation(inputCalibration.defaultHeadMat); + glm::quat defaultRotationOffset = glm::inverse(headPuckRotation) * defaultHeadRotation; + glm::quat finalHeadPuckRotation = defaultRotationOffset * headPuckRotation; + glm::vec3 headPuckTranslation = headPuckPose.getTranslation(); + glm::mat4 headPuckMat = createMatFromQuatAndPos(finalHeadPuckRotation, headPuckTranslation); + + // calculate the offset from the centerOfEye to defaultHeadMat + glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat; + + glm::mat4 currentHead = headPuckMat * defaultHeadOffset; + + // calculate the defaultToRefrenceXform + return currentHead * glm::inverse(inputCalibration.defaultHeadMat); +} + void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) { // Populate the L/RS_CENTER/OUTER pseudo buttons, corresponding to a partition of the L/RS space based on the X/Y values. const float CENTER_DEADBAND = 0.6f; @@ -664,7 +696,16 @@ void ViveControllerManager::InputDevice::calibrateShoulders(glm::mat4& defaultTo } void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { - int headIndex = _validTrackedObjects.size() - 1; + size_t headIndex = _validTrackedObjects.size() - 1; + const PuckPosePair& head = _validTrackedObjects[headIndex]; + + // assume the person is wearing the head puck on his/her forehead + glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat; + controller::Pose newHead = head.second.postTransform(defaultHeadOffset); + + _jointToPuckMap[controller::HEAD] = head.first; + _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead); + _overrideHead = true; } @@ -701,6 +742,8 @@ void ViveControllerManager::InputDevice::setConfigFromString(const QString& valu _preferedConfig = Config::FeetHipsAndChest; } else if (value == "FeetHipsAndShoulders") { _preferedConfig = Config::FeetHipsAndShoulders; + } else if (value == "FeetHipsChestAndHead") { + _preferedConfig = Config::FeetHipsChestAndHead; } } @@ -713,7 +756,7 @@ void ViveControllerManager::InputDevice::createPreferences() { auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; }; auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); }; auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter); - QStringList list = (QStringList() << "Auto" << "Feet" << "FeetAndHips" << "FeetHipsAndChest" << "FeetHipsAndShoulders"); + QStringList list = (QStringList() << "Feet" << "FeetAndHips" << "FeetHipsAndChest" << "FeetHipsAndShoulders" << "FeetHipsChestAndHead"); preference->setItems(list); preferences->addPreference(preference); diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index bc9558beaa..f3b87ea3a0 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -67,6 +67,7 @@ private: void calibrate(const controller::InputCalibrationData& inputCalibration); void uncalibrate(); controller::Pose addOffsetToPuckPose(int joint) const; + glm::mat4 recalculateDefaultToReferenceForHeadPuck(const controller::InputCalibrationData& inputCalibration); void updateCalibratedLimbs(); bool checkForCalibrationEvent(); void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); @@ -80,6 +81,17 @@ private: const vec3& angularVelocity); void partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPsuedoButton, int xPseudoButton, int yPseudoButton); void printDeviceTrackingResultChange(uint32_t deviceIndex); + void setConfigFromString(const QString& value); + void loadSettings(); + void saveSettings() const; + void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + + void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, + int firstShoulderIndex, int secondShoulderIndex); + + void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); class FilteredStick { public: @@ -106,9 +118,7 @@ private: }; enum class Config { Auto, - Head, Feet, - Shoulders, FeetAndHips, FeetHipsAndChest, FeetHipsAndShoulders, @@ -139,21 +149,11 @@ private: bool _triggersPressedHandled { false }; bool _calibrated { false }; bool _timeTilCalibrationSet { false }; - bool overrideHands { false }; + bool _overrideHead { false }; mutable std::recursive_mutex _lock; QString configToString(Config config); - void setConfigFromString(const QString& value); - void loadSettings(); - void saveSettings() const; - void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); - void calibrateShoulders(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, - int firstShoulderIndex, int secondShoulderIndex); - - void calibrateHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); friend class ViveControllerManager; }; From 32b5ac50204239e6c693107f6fcaf7f464b3a936 Mon Sep 17 00:00:00 2001 From: seefo Date: Thu, 25 May 2017 16:10:53 -0700 Subject: [PATCH 06/43] Moved audio asset processing to a separate thread --- libraries/audio/src/Sound.cpp | 37 ++++++++++++++++++++++++----------- libraries/audio/src/Sound.h | 29 +++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index d6607424db..b48ea1fed7 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -13,6 +13,8 @@ #include +#include +#include #include #include #include @@ -53,9 +55,25 @@ Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) : } void Sound::downloadFinished(const QByteArray& data) { + SoundProcessor* soundProcessor = new SoundProcessor(_url, data, this); + QThreadPool::globalInstance()->start(soundProcessor); +} + +void Sound::setReady(bool isReady) { + if (isReady) { + finishedLoading(true); + _isReady = true; + emit ready(); + qCDebug(audio) << "Setting ready state for audio asset" << _url; + } +} + +void SoundProcessor::run() { // replace our byte array with the downloaded data - QByteArray rawAudioByteArray = QByteArray(data); - QString fileName = getURL().fileName().toLower(); + QByteArray rawAudioByteArray = QByteArray(_data); + QString fileName = _url.fileName().toLower(); + + //Sound* sound = dynamic_cast(_sound); static const QString WAV_EXTENSION = ".wav"; static const QString RAW_EXTENSION = ".raw"; @@ -63,28 +81,25 @@ void Sound::downloadFinished(const QByteArray& data) { QByteArray outputAudioByteArray; - int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray); + int sampleRate = _sound->interpretAsWav(rawAudioByteArray, outputAudioByteArray); if (sampleRate != 0) { - downSample(outputAudioByteArray, sampleRate); + _sound->downSample(outputAudioByteArray, sampleRate); } } else if (fileName.endsWith(RAW_EXTENSION)) { // check if this was a stereo raw file // since it's raw the only way for us to know that is if the file was called .stereo.raw if (fileName.toLower().endsWith("stereo.raw")) { - _isStereo = true; - qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file."; + _sound->setStereo(true); + qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << _url << "as stereo audio file."; } // Process as 48khz RAW file - downSample(rawAudioByteArray, 48000); + _sound->downSample(rawAudioByteArray, 48000); } else { qCDebug(audio) << "Unknown sound file type"; } - finishedLoading(true); - - _isReady = true; - emit ready(); + _sound->setReady(true); } void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) { diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 37d5b40e95..6ec099b071 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -12,6 +12,7 @@ #ifndef hifi_Sound_h #define hifi_Sound_h +#include #include #include #include @@ -28,10 +29,15 @@ public: bool isAmbisonic() const { return _isAmbisonic; } bool isReady() const { return _isReady; } float getDuration() const { return _duration; } - const QByteArray& getByteArray() const { return _byteArray; } + void setStereo(bool stereo) { _isStereo = stereo; } + void setReady(bool ready); + + void downSample(const QByteArray& rawAudioByteArray, int sampleRate); + int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); + signals: void ready(); @@ -42,12 +48,27 @@ private: bool _isReady; float _duration; // In seconds - void downSample(const QByteArray& rawAudioByteArray, int sampleRate); - int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); - virtual void downloadFinished(const QByteArray& data) override; }; +class SoundProcessor : public QObject, public QRunnable { + Q_OBJECT + +public: + SoundProcessor(const QUrl& url, const QByteArray& data, Sound* sound) + : _url(url), _data(data), _sound(sound) + { + } + + virtual void run() override; + +private: + QUrl _url; + QByteArray _data; + Sound* _sound; + +}; + typedef QSharedPointer SharedSoundPointer; class SoundScriptingInterface : public QObject { From 8d9cfbdf31a97aab13c46966be0657ae85be1aa3 Mon Sep 17 00:00:00 2001 From: volansystech Date: Fri, 26 May 2017 11:14:50 +0530 Subject: [PATCH 07/43] Resolve comments of @sethalves . Remove whitespaces. --- interface/src/ui/overlays/Web3DOverlay.cpp | 56 +++++++++++----------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index bfb5291170..75c793bf77 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -385,17 +385,17 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { case PointerEvent::Press: touchType = QEvent::TouchBegin; touchPointState = Qt::TouchPointPressed; - mouseType = QEvent::MouseButtonPress; + mouseType = QEvent::MouseButtonPress; break; case PointerEvent::Release: touchType = QEvent::TouchEnd; touchPointState = Qt::TouchPointReleased; - mouseType = QEvent::MouseButtonRelease; + mouseType = QEvent::MouseButtonRelease; break; case PointerEvent::Move: touchType = QEvent::TouchUpdate; touchPointState = Qt::TouchPointMoved; - mouseType = QEvent::MouseMove; + mouseType = QEvent::MouseMove; if (((event.getButtons() & PointerEvent::PrimaryButton) > 0) != this->_pressed) { // Mouse was pressed/released while off the overlay; convert touch and mouse events to press/release to reflect @@ -404,12 +404,12 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { if (this->_pressed) { touchType = QEvent::TouchBegin; touchPointState = Qt::TouchPointPressed; - mouseType = QEvent::MouseButtonPress; + mouseType = QEvent::MouseButtonPress; } else { touchType = QEvent::TouchEnd; touchPointState = Qt::TouchPointReleased; - mouseType = QEvent::MouseButtonRelease; + mouseType = QEvent::MouseButtonRelease; } button = Qt::LeftButton; @@ -427,34 +427,32 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { event.getButtons() == PointerEvent::SecondaryButton) { return; } - - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(touchPointState); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); - QList touchPoints; - touchPoints.push_back(point); - QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); - touchEvent->setWindow(_webSurface->getWindow()); - touchEvent->setTarget(_webSurface->getRootItem()); - touchEvent->setTouchPoints(touchPoints); - touchEvent->setTouchPointStates(touchPointState); + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(touchPointState); + point.setPos(windowPoint); + point.setScreenPos(windowPoint); + QList touchPoints; + touchPoints.push_back(point); - QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); - - if (this->_pressed && event.getType() == PointerEvent::Move) { - return; - } + QTouchEvent* touchEvent = new QTouchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); + touchEvent->setWindow(_webSurface->getWindow()); + touchEvent->setTarget(_webSurface->getRootItem()); + touchEvent->setTouchPoints(touchPoints); + touchEvent->setTouchPointStates(touchPointState); - // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. - // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). - // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". + QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); - QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); - + if (this->_pressed && event.getType() == PointerEvent::Move) { + return; + } + // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. + // FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). + // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". + + QMouseEvent* mouseEvent = new QMouseEvent(mouseType, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); } void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { From 90d5b2a6d45671b64dd3fdcfd887880573aff3b0 Mon Sep 17 00:00:00 2001 From: seefo Date: Fri, 26 May 2017 10:26:32 -0700 Subject: [PATCH 08/43] Removed SoundProcessor's dependence on Sound objects --- libraries/audio/src/Sound.cpp | 62 +++++++++++++++++++++-------------- libraries/audio/src/Sound.h | 26 +++++++++------ 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index b48ea1fed7..5ac9f8f53d 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -51,67 +51,81 @@ Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) : _isAmbisonic(isAmbisonic), _isReady(false) { - } void Sound::downloadFinished(const QByteArray& data) { - SoundProcessor* soundProcessor = new SoundProcessor(_url, data, this); + // this is a QRunnable, will delete itself after it has finished running + SoundProcessor* soundProcessor = new SoundProcessor(_url, data, _isStereo, _isAmbisonic); + connect(soundProcessor, SIGNAL(onSuccess(QByteArray, bool, bool, float)), SLOT(soundProcessSuccess(QByteArray, bool, bool, float))); + connect(soundProcessor, SIGNAL(onError(int, QString)), SLOT(soundProcessError(int, QString))); QThreadPool::globalInstance()->start(soundProcessor); } -void Sound::setReady(bool isReady) { - if (isReady) { - finishedLoading(true); - _isReady = true; - emit ready(); - qCDebug(audio) << "Setting ready state for audio asset" << _url; - } +void Sound::soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration) { + + qCDebug(audio) << "Setting ready state for sound file" << _url.toDisplayString(); + + _byteArray = data; + _isStereo = stereo; + _isAmbisonic = ambisonic; + _duration = duration; + _isReady = true; + finishedLoading(true); + + emit ready(); +} + +void Sound::soundProcessError(int error, QString str) { + qCCritical(audio) << "Failed to process sound file" << _url.toDisplayString() << "code =" << error << str; + emit failed(QNetworkReply::UnknownContentError); + finishedLoading(false); } void SoundProcessor::run() { + + qCDebug(audio) << "Processing sound file" << _url.toDisplayString(); + // replace our byte array with the downloaded data QByteArray rawAudioByteArray = QByteArray(_data); QString fileName = _url.fileName().toLower(); - //Sound* sound = dynamic_cast(_sound); - static const QString WAV_EXTENSION = ".wav"; static const QString RAW_EXTENSION = ".raw"; if (fileName.endsWith(WAV_EXTENSION)) { QByteArray outputAudioByteArray; - int sampleRate = _sound->interpretAsWav(rawAudioByteArray, outputAudioByteArray); + int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray); if (sampleRate != 0) { - _sound->downSample(outputAudioByteArray, sampleRate); + downSample(outputAudioByteArray, sampleRate); } } else if (fileName.endsWith(RAW_EXTENSION)) { // check if this was a stereo raw file // since it's raw the only way for us to know that is if the file was called .stereo.raw if (fileName.toLower().endsWith("stereo.raw")) { - _sound->setStereo(true); + _isStereo = true; qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << _url << "as stereo audio file."; } // Process as 48khz RAW file - _sound->downSample(rawAudioByteArray, 48000); + downSample(rawAudioByteArray, 48000); } else { qCDebug(audio) << "Unknown sound file type"; + emit onError(300, "Failed to load sound file, reason: unknown sound file type"); + return; } - _sound->setReady(true); + emit onSuccess(_data, _isStereo, _isAmbisonic, _duration); } -void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) { +void SoundProcessor::downSample(const QByteArray& rawAudioByteArray, int sampleRate) { // we want to convert it to the format that the audio-mixer wants // which is signed, 16-bit, 24Khz if (sampleRate == AudioConstants::SAMPLE_RATE) { - // no resampling needed - _byteArray = rawAudioByteArray; - + _data = rawAudioByteArray; } else { int numChannels = _isAmbisonic ? AudioConstants::AMBISONIC : (_isStereo ? AudioConstants::STEREO : AudioConstants::MONO); @@ -121,15 +135,15 @@ void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) { int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample)); int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames); int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); - _byteArray.resize(maxDestinationBytes); + _data.resize(maxDestinationBytes); int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(), - (int16_t*)_byteArray.data(), + (int16_t*)_data.data(), numSourceFrames); // truncate to actual output int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample); - _byteArray.resize(numDestinationBytes); + _data.resize(numDestinationBytes); } } @@ -178,7 +192,7 @@ struct WAVEFormat { }; // returns wavfile sample rate, used for resampling -int Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { +int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) { // Create a data stream to analyze the data QDataStream waveStream(const_cast(&inputAudioByteArray), QIODevice::ReadOnly); diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 6ec099b071..43a4eb7685 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -32,14 +32,12 @@ public: const QByteArray& getByteArray() const { return _byteArray; } - void setStereo(bool stereo) { _isStereo = stereo; } - void setReady(bool ready); - - void downSample(const QByteArray& rawAudioByteArray, int sampleRate); - int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); - signals: void ready(); + +protected slots: + void soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration); + void soundProcessError(int error, QString str); private: QByteArray _byteArray; @@ -55,18 +53,26 @@ class SoundProcessor : public QObject, public QRunnable { Q_OBJECT public: - SoundProcessor(const QUrl& url, const QByteArray& data, Sound* sound) - : _url(url), _data(data), _sound(sound) + SoundProcessor(const QUrl& url, const QByteArray& data, const bool stereo, const bool ambisonic) + : _url(url), _data(data), _isStereo(stereo), _isAmbisonic(ambisonic) { } virtual void run() override; + void downSample(const QByteArray& rawAudioByteArray, int sampleRate); + int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray); + +signals: + void onSuccess(QByteArray data, bool stereo, bool ambisonic, float duration); + void onError(int error, QString str); + private: QUrl _url; QByteArray _data; - Sound* _sound; - + bool _isStereo; + bool _isAmbisonic; + float _duration; }; typedef QSharedPointer SharedSoundPointer; From cc57d28cccf1080b723317ee4bcbd81b46308ef4 Mon Sep 17 00:00:00 2001 From: seefo Date: Fri, 26 May 2017 11:39:47 -0700 Subject: [PATCH 09/43] Made requested changes to PR10554 --- libraries/audio/src/Sound.cpp | 4 ++-- libraries/audio/src/Sound.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 5ac9f8f53d..476a8d4d88 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -56,8 +56,8 @@ Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) : void Sound::downloadFinished(const QByteArray& data) { // this is a QRunnable, will delete itself after it has finished running SoundProcessor* soundProcessor = new SoundProcessor(_url, data, _isStereo, _isAmbisonic); - connect(soundProcessor, SIGNAL(onSuccess(QByteArray, bool, bool, float)), SLOT(soundProcessSuccess(QByteArray, bool, bool, float))); - connect(soundProcessor, SIGNAL(onError(int, QString)), SLOT(soundProcessError(int, QString))); + connect(soundProcessor, &SoundProcessor::onSuccess, this, &Sound::soundProcessSuccess); + connect(soundProcessor, &SoundProcessor::onError, this, &Sound::soundProcessError); QThreadPool::globalInstance()->start(soundProcessor); } diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 43a4eb7685..69dbf5a913 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -53,7 +53,7 @@ class SoundProcessor : public QObject, public QRunnable { Q_OBJECT public: - SoundProcessor(const QUrl& url, const QByteArray& data, const bool stereo, const bool ambisonic) + SoundProcessor(const QUrl& url, const QByteArray& data, bool stereo, bool ambisonic) : _url(url), _data(data), _isStereo(stereo), _isAmbisonic(ambisonic) { } From ccc7cb638de6fac18a945894434bfe1ba907eba7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 26 May 2017 14:18:29 -0700 Subject: [PATCH 10/43] Fix "retain avatar info" --- interface/src/Application.cpp | 8 ++++---- interface/src/CrashHandler.cpp | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f7628ee240..8430b0443a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -436,6 +436,10 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); const int listenPort = portStr ? atoi(portStr) : INVALID_PORT; + static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset"; + bool suppressPrompt = cmdOptionExists(argc, const_cast(argv), SUPPRESS_SETTINGS_RESET); + bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt); + Setting::init(); if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { @@ -456,10 +460,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { QCoreApplication::addLibraryPath(audioDLLPath); #endif - static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset"; - bool suppressPrompt = cmdOptionExists(argc, const_cast(argv), SUPPRESS_SETTINGS_RESET); - bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt); - DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); DependencyManager::registerInheritance(); diff --git a/interface/src/CrashHandler.cpp b/interface/src/CrashHandler.cpp index 8081dd3ffc..19e887eb6e 100644 --- a/interface/src/CrashHandler.cpp +++ b/interface/src/CrashHandler.cpp @@ -24,12 +24,15 @@ #include "Application.h" #include "Menu.h" -#include #include +#include +#include + bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt) { - Settings settings; + QSettings::setDefaultFormat(JSON_FORMAT); + QSettings settings; settings.beginGroup("Developer"); QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions); QVariant askToResetSettingsOption = settings.value(MenuOption::AskToResetSettings); @@ -106,7 +109,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) { return; } - Settings settings; + QSettings settings; const QString ADDRESS_MANAGER_GROUP = "AddressManager"; const QString ADDRESS_KEY = "address"; const QString AVATAR_GROUP = "Avatar"; From 765bd3890eee93e7af0a5b516f96c22922443065 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 30 May 2017 10:00:30 -0700 Subject: [PATCH 11/43] Improving test code for KTX library --- libraries/ktx/src/khronos/KHR.h | 2 + tests/ktx/CMakeLists.txt | 20 +-- tests/ktx/src/KtxTests.cpp | 195 ++++++++++++++++++++++++++++ tests/ktx/src/KtxTests.h | 21 +++ tests/ktx/src/main.cpp | 223 -------------------------------- 5 files changed, 225 insertions(+), 236 deletions(-) create mode 100644 tests/ktx/src/KtxTests.cpp create mode 100644 tests/ktx/src/KtxTests.h delete mode 100644 tests/ktx/src/main.cpp diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h index a98f2cc0d4..98cc1a4736 100644 --- a/libraries/ktx/src/khronos/KHR.h +++ b/libraries/ktx/src/khronos/KHR.h @@ -211,6 +211,8 @@ namespace khronos { template inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) { + enum { val = ALIGNMENT && !(ALIGNMENT & (ALIGNMENT - 1)) }; + static_assert(val, "template parameter ALIGNMENT must be a power of 2"); // FIXME add static assert that ALIGNMENT is a power of 2 static uint32_t ALIGNMENT_REMAINDER = ALIGNMENT - 1; return (value + ALIGNMENT_REMAINDER) / ALIGNMENT; diff --git a/tests/ktx/CMakeLists.txt b/tests/ktx/CMakeLists.txt index 7133c30898..599435a90b 100644 --- a/tests/ktx/CMakeLists.txt +++ b/tests/ktx/CMakeLists.txt @@ -1,15 +1,9 @@ +# Declare dependencies +macro (SETUP_TESTCASE_DEPENDENCIES) + # link in the shared libraries + link_hifi_libraries(shared ktx gpu image) -set(TARGET_NAME ktx-test) - -if (WIN32) - SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4049 /ignore:4217") -endif() + package_libraries_for_deployment() +endmacro () -# This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Quick Gui OpenGL) -set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") - -# link in the shared libraries -link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image) - -package_libraries_for_deployment() +setup_hifi_testcase() diff --git a/tests/ktx/src/KtxTests.cpp b/tests/ktx/src/KtxTests.cpp new file mode 100644 index 0000000000..94e5d7e8e7 --- /dev/null +++ b/tests/ktx/src/KtxTests.cpp @@ -0,0 +1,195 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// Copyright 2014 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 "KtxTests.h" + +#include + +#include + +#include +#include +#include + + +QTEST_GUILESS_MAIN(KtxTests) + +QString getRootPath() { + static std::once_flag once; + static QString result; + std::call_once(once, [&] { + QFileInfo file(__FILE__); + QDir parent = file.absolutePath(); + result = QDir::cleanPath(parent.currentPath() + "/../../.."); + }); + return result; +} + +void KtxTests::initTestCase() { +} + +void KtxTests::cleanupTestCase() { +} + +void KtxTests::testKhronosCompressionFunctions() { + using namespace khronos::gl::texture; + QCOMPARE(evalAlignedCompressedBlockCount<4>(0), (uint32_t)0x0); + QCOMPARE(evalAlignedCompressedBlockCount<4>(1), (uint32_t)0x1); + QCOMPARE(evalAlignedCompressedBlockCount<4>(4), (uint32_t)0x1); + QCOMPARE(evalAlignedCompressedBlockCount<4>(5), (uint32_t)0x2); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x00), (uint32_t)0x00); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x01), (uint32_t)0x01); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x04), (uint32_t)0x01); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x05), (uint32_t)0x02); + QCOMPARE(evalCompressedBlockCount(InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT, 0x1000), (uint32_t)0x400); + + QVERIFY_EXCEPTION_THROWN(evalCompressedBlockCount(InternalFormat::RGBA8, 0x00), std::runtime_error); +} + +void KtxTests::testKtxEvalFunctions() { + QCOMPARE(sizeof(ktx::Header), (size_t)64); + QCOMPARE(ktx::evalPadding(0x0), (uint8_t)0); + QCOMPARE(ktx::evalPadding(0x1), (uint8_t)3); + QCOMPARE(ktx::evalPadding(0x2), (uint8_t)2); + QCOMPARE(ktx::evalPadding(0x3), (uint8_t)1); + QCOMPARE(ktx::evalPadding(0x4), (uint8_t)0); + QCOMPARE(ktx::evalPadding(0x400), (uint8_t)0); + QCOMPARE(ktx::evalPadding(0x401), (uint8_t)3); + QCOMPARE(ktx::evalPaddedSize(0x0), 0x0); + QCOMPARE(ktx::evalPaddedSize(0x1), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x2), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x3), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x4), 0x4); + QCOMPARE(ktx::evalPaddedSize(0x400), 0x400); + QCOMPARE(ktx::evalPaddedSize(0x401), 0x404); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x0), (uint32_t)0x0); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x1), (uint32_t)0x1); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x4), (uint32_t)0x1); + QCOMPARE(ktx::evalAlignedCount((uint32_t)0x5), (uint32_t)0x2); +} + +void KtxTests::testKtxSerialization() { + const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; + QImage image(TEST_IMAGE); + gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true); + auto ktxMemory = gpu::Texture::serialize(*testTexture); + QVERIFY(ktxMemory.get()); + + // Serialize the image to a file + QTemporaryFile TEST_IMAGE_KTX; + { + const auto& ktxStorage = ktxMemory->getStorage(); + QVERIFY(ktx::KTX::validate(ktxStorage)); + QVERIFY(ktxMemory->isValid()); + + auto& outFile = TEST_IMAGE_KTX; + if (!outFile.open()) { + QFAIL("Unable to open file"); + } + auto ktxSize = ktxStorage->size(); + outFile.resize(ktxSize); + auto dest = outFile.map(0, ktxSize); + memcpy(dest, ktxStorage->data(), ktxSize); + outFile.unmap(dest); + outFile.close(); + } + + + { + auto ktxStorage = std::make_shared(TEST_IMAGE_KTX.fileName()); + QVERIFY(ktx::KTX::validate(ktxStorage)); + auto ktxFile = ktx::KTX::create(ktxStorage); + QVERIFY(ktxFile.get()); + QVERIFY(ktxFile->isValid()); + { + const auto& memStorage = ktxMemory->getStorage(); + const auto& fileStorage = ktxFile->getStorage(); + QVERIFY(memStorage->size() == fileStorage->size()); + QVERIFY(memStorage->data() != fileStorage->data()); + QVERIFY(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); + QVERIFY(ktxFile->_images.size() == ktxMemory->_images.size()); + auto imageCount = ktxFile->_images.size(); + auto startMemory = ktxMemory->_storage->data(); + auto startFile = ktxFile->_storage->data(); + for (size_t i = 0; i < imageCount; ++i) { + auto memImages = ktxMemory->_images[i]; + auto fileImages = ktxFile->_images[i]; + QVERIFY(memImages._padding == fileImages._padding); + QVERIFY(memImages._numFaces == fileImages._numFaces); + QVERIFY(memImages._imageSize == fileImages._imageSize); + QVERIFY(memImages._faceSize == fileImages._faceSize); + QVERIFY(memImages._faceBytes.size() == memImages._numFaces); + QVERIFY(fileImages._faceBytes.size() == fileImages._numFaces); + auto faceCount = fileImages._numFaces; + for (uint32_t face = 0; face < faceCount; ++face) { + auto memFace = memImages._faceBytes[face]; + auto memOffset = memFace - startMemory; + auto fileFace = fileImages._faceBytes[face]; + auto fileOffset = fileFace - startFile; + QVERIFY(memOffset % 4 == 0); + QVERIFY(memOffset == fileOffset); + } + } + } + } + testTexture->setKtxBacking(TEST_IMAGE_KTX.fileName().toStdString()); +} + +#if 0 + +static const QString TEST_FOLDER { "H:/ktx_cacheold" }; +//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" }; + +//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" }; +static const QString EXTENSIONS { "*.ktx" }; + +int mainTemp(int, char**) { + qInstallMessageHandler(messageHandler); + auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS }); + for (auto fileInfo : fileInfoList) { + qDebug() << fileInfo.filePath(); + std::shared_ptr storage { new storage::FileStorage { fileInfo.filePath() } }; + + if (!ktx::KTX::validate(storage)) { + qDebug() << "KTX invalid"; + } + + auto ktxFile = ktx::KTX::create(storage); + ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor(); + + qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs"; + for (const auto& kv : ktxDescriptor.keyValues) { + qDebug() << "\t" << kv._key.c_str(); + } + + auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); + if (offsetToMinMipKV) { + auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV; + auto minMipLevelAvailable = *data; + qDebug() << "\tMin mip available " << minMipLevelAvailable; + assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels); + } + auto storageSize = storage->size(); + for (const auto& faceImageDesc : ktxDescriptor.images) { + //assert(0 == (faceImageDesc._faceSize % 4)); + for (const auto& faceOffset : faceImageDesc._faceOffsets) { + assert(0 == (faceOffset % 4)); + auto faceEndOffset = faceOffset + faceImageDesc._faceSize; + assert(faceEndOffset <= storageSize); + } + } + + for (const auto& faceImage : ktxFile->_images) { + for (const ktx::Byte* faceBytes : faceImage._faceBytes) { + assert(0 == (reinterpret_cast(faceBytes) % 4)); + } + } + } + return 0; +} +#endif diff --git a/tests/ktx/src/KtxTests.h b/tests/ktx/src/KtxTests.h new file mode 100644 index 0000000000..5627dc313d --- /dev/null +++ b/tests/ktx/src/KtxTests.h @@ -0,0 +1,21 @@ +// +// Created by Bradley Austin Davis on 2016/07/01 +// Copyright 2014 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 + +class KtxTests : public QObject { + Q_OBJECT +private slots: + void initTestCase(); + void cleanupTestCase(); + void testKtxEvalFunctions(); + void testKhronosCompressionFunctions(); + void testKtxSerialization(); +}; + + diff --git a/tests/ktx/src/main.cpp b/tests/ktx/src/main.cpp deleted file mode 100644 index 3b62b89948..0000000000 --- a/tests/ktx/src/main.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/07/01 -// Copyright 2014 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 -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -QSharedPointer logger; - -gpu::Texture* cacheTexture(const std::string& name, gpu::Texture* srcTexture, bool write = true, bool read = true); - - -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - QString logMessage = LogHandler::getInstance().printMessage((LogMsgType)type, context, message); - - if (!logMessage.isEmpty()) { -#ifdef Q_OS_WIN - OutputDebugStringA(logMessage.toLocal8Bit().constData()); - OutputDebugStringA("\n"); -#endif - if (logger) { - logger->addMessage(qPrintable(logMessage + "\n")); - } - } -} - -const char * LOG_FILTER_RULES = R"V0G0N( -hifi.gpu=true -)V0G0N"; - -QString getRootPath() { - static std::once_flag once; - static QString result; - std::call_once(once, [&] { - QFileInfo file(__FILE__); - QDir parent = file.absolutePath(); - result = QDir::cleanPath(parent.currentPath() + "/../../.."); - }); - return result; -} - -const QString TEST_IMAGE = getRootPath() + "/scripts/developer/tests/cube_texture.png"; -const QString TEST_IMAGE_KTX = getRootPath() + "/scripts/developer/tests/cube_texture.ktx"; - -int main(int argc, char** argv) { - QApplication app(argc, argv); - QCoreApplication::setApplicationName("KTX"); - QCoreApplication::setOrganizationName("High Fidelity"); - QCoreApplication::setOrganizationDomain("highfidelity.com"); - logger.reset(new FileLogger()); - - Q_ASSERT(ktx::evalPadding(0) == 0); - Q_ASSERT(ktx::evalPadding(1) == 3); - Q_ASSERT(ktx::evalPadding(2) == 2); - Q_ASSERT(ktx::evalPadding(3) == 1); - Q_ASSERT(ktx::evalPadding(4) == 0); - Q_ASSERT(ktx::evalPadding(1024) == 0); - Q_ASSERT(ktx::evalPadding(1025) == 3); - Q_ASSERT(ktx::evalPaddedSize(0) == 0); - Q_ASSERT(ktx::evalPaddedSize(1) == 4); - Q_ASSERT(ktx::evalPaddedSize(2) == 4); - Q_ASSERT(ktx::evalPaddedSize(3) == 4); - Q_ASSERT(ktx::evalPaddedSize(4) == 4); - Q_ASSERT(ktx::evalPaddedSize(1024) == 1024); - Q_ASSERT(ktx::evalPaddedSize(1025) == 1028); - Q_ASSERT(sizeof(ktx::Header) == 12 + (sizeof(uint32_t) * 13)); - - DependencyManager::set(); - qInstallMessageHandler(messageHandler); - QLoggingCategory::setFilterRules(LOG_FILTER_RULES); - - QImage image(TEST_IMAGE); - gpu::TexturePointer testTexture = image::TextureUsage::process2DTextureColorFromImage(image, TEST_IMAGE.toStdString(), true); - - auto ktxMemory = gpu::Texture::serialize(*testTexture); - { - const auto& ktxStorage = ktxMemory->getStorage(); - Q_ASSERT_X(ktx::KTX::validate(ktxStorage), __FUNCTION__, "KTX storage validation failed"); - Q_ASSERT_X(ktxMemory->isValid(), __FUNCTION__, "KTX self-validation failed"); - QSaveFile outFile(TEST_IMAGE_KTX); - if (!outFile.open(QFile::WriteOnly)) { - throw std::runtime_error("Unable to open file"); - } - auto ktxSize = ktxStorage->size(); - outFile.resize(ktxSize); - auto dest = outFile.map(0, ktxSize); - memcpy(dest, ktxStorage->data(), ktxSize); - outFile.unmap(dest); - outFile.commit(); - } - - { - auto ktxFile = ktx::KTX::create(std::shared_ptr(new storage::FileStorage(TEST_IMAGE_KTX))); - { - const auto& memStorage = ktxMemory->getStorage(); - const auto& fileStorage = ktxFile->getStorage(); - Q_ASSERT(memStorage->size() == fileStorage->size()); - Q_ASSERT(memStorage->data() != fileStorage->data()); - Q_ASSERT(0 == memcmp(memStorage->data(), fileStorage->data(), memStorage->size())); - Q_ASSERT(ktxFile->_images.size() == ktxMemory->_images.size()); - auto imageCount = ktxFile->_images.size(); - auto startMemory = ktxMemory->_storage->data(); - auto startFile = ktxFile->_storage->data(); - for (size_t i = 0; i < imageCount; ++i) { - auto memImages = ktxMemory->_images[i]; - auto fileImages = ktxFile->_images[i]; - Q_ASSERT(memImages._padding == fileImages._padding); - Q_ASSERT(memImages._numFaces == fileImages._numFaces); - Q_ASSERT(memImages._imageSize == fileImages._imageSize); - Q_ASSERT(memImages._faceSize == fileImages._faceSize); - Q_ASSERT(memImages._faceBytes.size() == memImages._numFaces); - Q_ASSERT(fileImages._faceBytes.size() == fileImages._numFaces); - auto faceCount = fileImages._numFaces; - for (uint32_t face = 0; face < faceCount; ++face) { - auto memFace = memImages._faceBytes[face]; - auto memOffset = memFace - startMemory; - auto fileFace = fileImages._faceBytes[face]; - auto fileOffset = fileFace - startFile; - Q_ASSERT(memOffset % 4 == 0); - Q_ASSERT(memOffset == fileOffset); - } - } - } - } - testTexture->setKtxBacking(TEST_IMAGE_KTX.toStdString()); - return 0; -} - -#if 0 -static const QString TEST_FOLDER { "H:/ktx_cacheold" }; -//static const QString TEST_FOLDER { "C:/Users/bdavis/Git/KTX/testimages" }; - -//static const QString EXTENSIONS { "4bbdf8f786470e4ab3e672d44b8e8df2.ktx" }; -static const QString EXTENSIONS { "*.ktx" }; - -int mainTemp(int, char**) { - qInstallMessageHandler(messageHandler); - auto fileInfoList = QDir { TEST_FOLDER }.entryInfoList(QStringList { EXTENSIONS }); - for (auto fileInfo : fileInfoList) { - qDebug() << fileInfo.filePath(); - std::shared_ptr storage { new storage::FileStorage { fileInfo.filePath() } }; - - if (!ktx::KTX::validate(storage)) { - qDebug() << "KTX invalid"; - } - - auto ktxFile = ktx::KTX::create(storage); - ktx::KTXDescriptor ktxDescriptor = ktxFile->toDescriptor(); - - qDebug() << "Contains " << ktxDescriptor.keyValues.size() << " key value pairs"; - for (const auto& kv : ktxDescriptor.keyValues) { - qDebug() << "\t" << kv._key.c_str(); - } - - auto offsetToMinMipKV = ktxDescriptor.getValueOffsetForKey(ktx::HIFI_MIN_POPULATED_MIP_KEY); - if (offsetToMinMipKV) { - auto data = storage->data() + ktx::KTX_HEADER_SIZE + offsetToMinMipKV; - auto minMipLevelAvailable = *data; - qDebug() << "\tMin mip available " << minMipLevelAvailable; - assert(minMipLevelAvailable < ktxDescriptor.header.numberOfMipmapLevels); - } - auto storageSize = storage->size(); - for (const auto& faceImageDesc : ktxDescriptor.images) { - //assert(0 == (faceImageDesc._faceSize % 4)); - for (const auto& faceOffset : faceImageDesc._faceOffsets) { - assert(0 == (faceOffset % 4)); - auto faceEndOffset = faceOffset + faceImageDesc._faceSize; - assert(faceEndOffset <= storageSize); - } - } - - for (const auto& faceImage : ktxFile->_images) { - for (const ktx::Byte* faceBytes : faceImage._faceBytes) { - assert(0 == (reinterpret_cast(faceBytes) % 4)); - } - } - } - return 0; -} -#endif - -#include "main.moc" - From 133951617704427025d83fa2095440698b2ab007 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 30 May 2017 20:56:45 +0200 Subject: [PATCH 12/43] OpenSSL installer update --- BUILD_WIN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 841cfba3b3..14709b7e92 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -24,7 +24,7 @@ Go to "Control Panel > System > Advanced System Settings > Environment Variables ###Step 5. Installing OpenSSL -Download and install the [Win64 OpenSSL v1.0.2k Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2k.exe). +Download and install the [Win64 OpenSSL v1.0.2L Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2L.exe). ###Step 6. Running CMake to Generate Build Files From 1ea3b2b7f43599bbbb38124e6484cdffda2d60d0 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 30 May 2017 21:01:42 +0200 Subject: [PATCH 13/43] let headers be headers --- BUILD_WIN.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 14709b7e92..311dfd9267 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -1,32 +1,32 @@ This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit. -###Step 1. Installing Visual Studio 2013 +### Step 1. Installing Visual Studio 2013 If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer. Note: Newer versions of Visual Studio are not yet compatible. -###Step 2. Installing CMake +### Step 2. Installing CMake Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer. -###Step 3. Installing Qt +### Step 3. Installing Qt Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe). Keep the default components checked when going through the installer. -###Step 4. Setting Qt Environment Variable +### Step 4. Setting Qt Environment Variable Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." (or search “Environment Variables” in Start Search). * Set "Variable name": QT_CMAKE_PREFIX_PATH * Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake` -###Step 5. Installing OpenSSL +### Step 5. Installing OpenSSL Download and install the [Win64 OpenSSL v1.0.2L Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2L.exe). -###Step 6. Running CMake to Generate Build Files +### Step 6. Running CMake to Generate Build Files Run Command Prompt from Start and run the following commands: cd "%HIFI_DIR%" @@ -36,7 +36,7 @@ Run Command Prompt from Start and run the following commands: Where %HIFI_DIR% is the directory for the highfidelity repository. -###Step 7. Making a Build +### Step 7. Making a Build Open '%HIFI_DIR%\build\hifi.sln' using Visual Studio. @@ -44,7 +44,7 @@ Change the Solution Configuration (next to the green play button) from "Debug" t Run Build > Build Solution. -###Step 8. Testing Interface +### Step 8. Testing Interface Create another environment variable (see Step #4) * Set "Variable name": _NO_DEBUG_HEAP @@ -56,7 +56,7 @@ Now, you should have a full build of High Fidelity and be able to run the Interf Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Release\interface.exe -###Troubleshooting +### Troubleshooting For any problems after Step #6, first try this: * Delete your locally cloned copy of the highfidelity repository @@ -64,18 +64,18 @@ For any problems after Step #6, first try this: * Redownload the [repository](https://github.com/highfidelity/hifi) * Restart directions from Step #6 -####CMake gives you the same error message repeatedly after the build fails +#### CMake gives you the same error message repeatedly after the build fails Remove `CMakeCache.txt` found in the '%HIFI_DIR%\build' directory -####nmake cannot be found +#### nmake cannot be found Make sure nmake.exe is located at the following path: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin If not, add the directory where nmake is located to the PATH environment variable. -####Qt is throwing an error +#### Qt is throwing an error Make sure you have the correct version (5.6.2) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly. From 68ad8fa9816ca4829fbdc78cce247a9397eb9d5a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 30 May 2017 21:12:51 +0200 Subject: [PATCH 14/43] visual headers --- BUILD.md | 25 +++++++++++++++---------- BUILD_ANDROID.md | 18 +++++++++--------- BUILD_LINUX.md | 3 ++- BUILD_OSX.md | 11 +++++++---- INSTALL.md | 8 ++++---- 5 files changed, 37 insertions(+), 28 deletions(-) diff --git a/BUILD.md b/BUILD.md index fc2359b057..c45b7cb636 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,4 +1,4 @@ -###Dependencies +### Dependencies * [cmake](https://cmake.org/download/) ~> 3.3.2 * [Qt](https://www.qt.io/download-open-source) ~> 5.6.2 @@ -6,7 +6,7 @@ * IMPORTANT: Use the latest available version of OpenSSL to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) -####CMake External Project Dependencies +#### CMake External Project Dependencies * [boostconfig](https://github.com/boostorg/config) ~> 1.58 * [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases) ~> 2.83 @@ -30,16 +30,19 @@ These are not placed in your normal build tree when doing an out of source build If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project. -###OS Specific Build Guides +### OS Specific Build Guides + * [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X. * [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux. * [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows. * [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android -###CMake +### CMake + Hifi uses CMake to generate build files and project files for your platform. -####Qt +#### Qt + In order for CMake to find the Qt5 find modules, you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt installation. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). @@ -50,7 +53,8 @@ The path it needs to be set to will depend on where and how Qt5 was installed. e export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake -####Generating build files +#### Generating build files + Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean. mkdir build @@ -59,14 +63,15 @@ Create a build directory in the root of your checkout and then run the CMake bui If cmake gives you the same error message repeatedly after the build fails (e.g. you had a typo in the QT_CMAKE_PREFIX_PATH that you fixed but the `.cmake` files still cannot be found), try removing `CMakeCache.txt`. -####Variables +#### Variables + Any variables that need to be set for CMake to find dependencies can be set as ENV variables in your shell profile, or passed directly to CMake with a `-D` flag appended to the `cmake ..` command. For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation: cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/lib/cmake -####Finding Dependencies +#### Finding Dependencies The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies). @@ -78,8 +83,8 @@ In the examples below the variable $NAME would be replaced by the name of the de * $NAME_ROOT_DIR - set this variable in your ENV * HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name' -###Optional Components +### Optional Components -####Devices +#### Devices You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index 1f144bf3ba..d69d20ee8a 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -1,6 +1,6 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Android specific instructions are found in this file. -###Android Dependencies +### Android Dependencies You will need the following tools to build our Android targets. @@ -17,23 +17,23 @@ You will need the following tools to build our Android targets. You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine. -####Scribe +#### Scribe High Fidelity has a shader pre-processing tool called `scribe` that various libraries will call on during the build process. You must compile scribe using your native toolchain (following the build instructions for your platform) and then pass a CMake variable or set an ENV variable `SCRIBE_PATH` that is a path to the scribe executable. CMake will fatally error if it does not find the scribe executable while using the android toolchain. -####Optional Components +#### Optional Components * [Oculus Mobile SDK](https://developer.oculus.com/downloads/#sdk=mobile) ~> 0.4.2 -####ANDROID_LIB_DIR +#### ANDROID_LIB_DIR Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies. This is most easily accomplished by installing all Android dependencies in the same folder. You can place this folder wherever you like on your machine. In this build guide and across our CMakeLists files this folder is referred to as `ANDROID_LIB_DIR`. You can set `ANDROID_LIB_DIR` in your environment or by passing when you run CMake. -####Qt +#### Qt Install Qt 5.5.1 for Android for your host environment from the [Qt downloads page](http://www.qt.io/download/). Install Qt to ``$ANDROID_LIB_DIR/Qt``. This is required so that our root CMakeLists file can help CMake find your Android Qt installation. @@ -41,7 +41,7 @@ The component required for the Android build is the `Android armv7` component. If you would like to install Qt to a different location, or attempt to build with a different Qt version, you can pass `ANDROID_QT_CMAKE_PREFIX_PATH` to CMake. Point to the `cmake` folder inside `$VERSION_NUMBER/android_armv7/lib`. Otherwise, our root CMakeLists will set it to `$ANDROID_LIB_DIR/Qt/5.5/android_armv7/lib/cmake`. -####OpenSSL +#### OpenSSL Cross-compilation of OpenSSL has been tested from an OS X machine running 10.10 compiling OpenSSL 1.0.2. It is likely that the steps below will work for other OpenSSL versions than 1.0.2. @@ -76,7 +76,7 @@ This should generate libcrypto and libssl in the root of the OpenSSL directory. If you have been building other components it is possible that the OpenSSL compile will fail based on the values other cross-compilations (tbb, bullet) have set. Ensure that you are in a new terminal window to avoid compilation errors from previously set environment variables. -####Oculus Mobile SDK +#### Oculus Mobile SDK The Oculus Mobile SDK is optional, for Gear VR support. It is not required to compile gvr-interface. @@ -91,7 +91,7 @@ ndk-build This will create the liboculus.a archive that our FindLibOVR module will look for when cmake is run. -#####Hybrid testing +##### Hybrid testing Currently the 'vr_dual' mode that would allow us to run a hybrid app has limited support in the Oculus Mobile SDK. The best way to have an application we can launch without having to connect to the GearVR is to put the Gear VR Service into developer mode. This stops Oculus Home from taking over the device when it is plugged into the Gear VR headset, and allows the application to be launched from the Applications page. @@ -99,7 +99,7 @@ To put the Gear VR Service into developer mode you need an application with an O Once the application is on your device, go to `Settings->Application Manager->Gear VR Service->Manage Storage`. Tap on `VR Service Version` six times. It will scan your device to verify that you have an osig file in an application on your device, and then it will let you enable Developer mode. -###CMake +### CMake We use CMake to generate the makefiles that compile and deploy the Android APKs to your device. In order to create Makefiles for the Android targets, CMake requires that some environment variables are set, and that other variables are passed to it when it is run. diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 34ba41894c..d40576a75d 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -1,6 +1,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file. -###Qt5 Dependencies +### Qt5 Dependencies + Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required: libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev diff --git a/BUILD_OSX.md b/BUILD_OSX.md index afd3fa040c..3365627b8c 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,12 +1,13 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file. -###Homebrew +### Homebrew + [Homebrew](https://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple. brew tap homebrew/versions brew install cmake openssl -###OpenSSL +### OpenSSL Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations. For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: @@ -15,7 +16,8 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change. -###Qt +### Qt + Download and install the [Qt 5.6.2 for macOS](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-mac-x64-clang-5.6.2.dmg). Keep the default components checked when going through the installer. @@ -23,7 +25,8 @@ Keep the default components checked when going through the installer. Once Qt is installed, you need to manually configure the following: * Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.2/5.6/clang_64/lib/cmake/` directory. -###Xcode +### Xcode + If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. cmake .. -GXcode diff --git a/INSTALL.md b/INSTALL.md index 701752f6af..79d7e96977 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -2,15 +2,15 @@ Follow the [build guide](BUILD.md) to figure out how to build High Fidelity for During generation, CMake should produce an `install` target and a `package` target. -###Install +### Install The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`. -###Packaging +### Packaging To produce an installer, run the `package` target. -####Windows +#### Windows To produce an executable installer on Windows, the following are required: @@ -20,6 +20,6 @@ To produce an executable installer on Windows, the following are required: Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System. -####OS X +#### OS X Run the `package` target to create an Apple Disk Image (.dmg). From 4c4811dca82ea881bdf19d07dd581d179b807194 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 May 2017 22:52:39 +0100 Subject: [PATCH 15/43] finished the head puck --- libraries/shared/src/GLMHelpers.h | 3 +- plugins/openvr/src/ViveControllerManager.cpp | 78 +++++++++++++++++--- plugins/openvr/src/ViveControllerManager.h | 1 + 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 5574007b9b..ef92552d1f 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -147,8 +147,7 @@ bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, gl glm::vec3 extractTranslation(const glm::mat4& matrix); -void setTranslation(glm::mat4& matrix, cons - t glm::vec3& translation); +void setTranslation(glm::mat4& matrix, const glm::vec3& translation); glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false); glm::quat glmExtractRotation(const glm::mat4& matrix); diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 0f0cc5860e..82457928a2 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -82,6 +82,28 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck) return (firstPuck.second.translation.x < secondPuck.second.translation.x); } +static bool comparePosePositions(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { + glm::vec3 poseAPosition = poseA.getTranslation(); + glm::vec3 poseBPosition = poseB.getTranslation(); + + glm::vec3 poseAFinalPosition = poseAPosition - axisOrigin; + glm::vec3 poseBFinalPosition = poseBPosition - axisOrigin; + + float poseADistance = glm::dot(poseAFinalPosition, axis); + float poseBDistance = glm::dot(poseBFinalPosition, axis); + return (poseADistance > poseBDistance); +} + +static glm::vec3 getHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { + glm::mat4 finalHead = defaultToReferenceMat * defaultHead; + return glmExtractRotation(finalHead) * Vectors::UNIT_X; +} + +static glm::vec3 getHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { + glm::mat4 finalHead = defaultToReferenceMat * defaultHead; + return extractTranslation(finalHead); +} + static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) { QString result; auto iterator = TRACKING_RESULT_TO_STRING.find(trackingResult); @@ -361,12 +383,16 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex); } else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration); + glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); } else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration); + glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateChest(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); @@ -468,17 +494,29 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat; size_t headPuckIndex = _validTrackedObjects.size() - 1; controller::Pose headPuckPose = _validTrackedObjects[headPuckIndex].second; - glm::mat4 headPuckSensorMat = avatarToSensorMat * createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()); - glm::vec3 headPuckTranslation = extractTranslation(headPuckSensorMat); - glm::vec3 headPuckYRotation = glmExtractRotation(headPuckSensorMat) * glm::vec3(0.0f, 1.0f, 0.0f); + glm::mat4 headPuckAvatarMat = createMatFromQuatAndPos(headPuckPose.getRotation(), headPuckPose.getTranslation()) * Matrices::Y_180; + glm::vec3 headPuckTranslation = extractTranslation(headPuckAvatarMat); + glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f); glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f); - glm::vec3 desiredY = glm::vec3(0.0f, 1.0f, 0.0f); - glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckYRotation)); - glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, desiredY)); + glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis)); + glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime)); + glm::mat4 newHeadPuck = glm::mat4(glm::vec4(xPrime, 0.0f), glm::vec4(yPrime, 0.0f), + glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f)); - glm::mat4 - return glm::mat4(); + glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.005f, 0.0f, 1.0f)); + + glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset; + + glm::mat4 defaultHeadOffset = glm::inverse(inputCalibration.defaultCenterEyeMat) * inputCalibration.defaultHeadMat; + + glm::mat4 currentHead = finalHeadPuck * defaultHeadOffset; + + // calculate the defaultToRefrenceXform + glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat); + return defaultToReferenceMat; } void ViveControllerManager::InputDevice::partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPseudoButton, int xPseudoButton, int yPseudoButton) { @@ -668,6 +706,26 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer } } + +void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition) { + auto& firstFoot = _validTrackedObjects[FIRST_FOOT]; + auto& secondFoot = _validTrackedObjects[SECOND_FOOT]; + controller::Pose& firstFootPose = firstFoot.second; + controller::Pose& secondFootPose = secondFoot.second; + + if (comparePosePositions(firstFootPose, secondFootPose, headXAxis, headPosition)) { + _jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first; + _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose); + _jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first; + _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, secondFootPose); + } else { + _jointToPuckMap[controller::LEFT_FOOT] = secondFoot.first; + _pucksOffset[secondFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, secondFootPose); + _jointToPuckMap[controller::RIGHT_FOOT] = firstFoot.first; + _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultRightFoot, firstFootPose); + } +} + void ViveControllerManager::InputDevice::calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration) { _jointToPuckMap[controller::HIPS] = _validTrackedObjects[HIP].first; _pucksOffset[_validTrackedObjects[HIP].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHips, _validTrackedObjects[HIP].second); diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 545af1b00b..c32579b0d8 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -85,6 +85,7 @@ private: void loadSettings(); void saveSettings() const; void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); + void calibrateFeet(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration, glm::vec3 headXAxis, glm::vec3 headPosition); void calibrateHips(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); void calibrateChest(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); From 9c787eb784326adc4f650dc113fe03bf400a445b Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 May 2017 22:59:12 +0100 Subject: [PATCH 16/43] removed commented code --- scripts/defaultScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index db0b705f8e..81ce72d901 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -13,7 +13,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/progress.js", - //"system/away.js", + "system/away.js", "system/audio.js", "system/hmd.js", "system/menu.js", From b3401d97629a6544358b2c908ff43349268f091e Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 30 May 2017 23:55:45 +0100 Subject: [PATCH 17/43] making some final changes --- interface/resources/controllers/vive.json | 22 ++++++++++++-------- plugins/openvr/src/ViveControllerManager.cpp | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 984f9c65fb..a0e9bd30d4 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -34,32 +34,36 @@ { "from": "Vive.RSCenter", "to": "Standard.RightPrimaryThumb" }, { "from": "Vive.RightApplicationMenu", "to": "Standard.RightSecondaryThumb" }, - { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, - { "from": "Vive.RightHand", "to": "Standard.RightHand" }, + { "from": "Vive.LeftHand", "to": "Standard.LeftHand", "when": [ "Application.InHMD" ] }, + { "from": "Vive.RightHand", "to": "Standard.RightHand", "when": [ "Application.InHMD" ] }, { "from": "Vive.LeftFoot", "to" : "Standard.LeftFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], + "when": [ "Application.InHMD" ] }, { "from": "Vive.RightFoot", "to" : "Standard.RightFoot", - "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}] + "filters" : [{"type" : "lowVelocity", "rotation" : 1.0, "translation": 1.0}], + "when": [ "Application.InHMD" ] }, { "from": "Vive.Hips", "to" : "Standard.Hips", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], + "when": [ "Application.InHMD" ] }, { "from": "Vive.Spine2", "to" : "Standard.Spine2", - "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}] + "filters" : [{"type" : "lowVelocity", "rotation" : 0.01, "translation": 0.01}], + "when": [ "Application.InHMD" ] }, - { "from": "Vive.Head", "to" : "Standard.Head"}, + { "from": "Vive.Head", "to" : "Standard.Head", "when": [ "Application.InHMD" ] }, - { "from": "Vive.RightArm", "to" : "Standard.RightArm"}, - { "from": "Vive.LeftArm", "to" : "Standard.LeftArm"} + { "from": "Vive.RightArm", "to" : "Standard.RightArm", "when": [ "Application.InHMD" ] }, + { "from": "Vive.LeftArm", "to" : "Standard.LeftArm", "when": [ "Application.InHMD" ] } ] } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 82457928a2..6edcbcb3fa 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -506,7 +506,7 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f)); glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.005f, 0.0f, 1.0f)); + glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.0254f, -0.152f, 1.0f)); glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset; From 41869430f7dab1dc1eefb6a5788926c19f811305 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 May 2017 01:29:32 +0100 Subject: [PATCH 18/43] made some changes that were requested --- plugins/openvr/src/ViveControllerManager.cpp | 33 +++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 6edcbcb3fa..e08929cf08 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -57,6 +57,8 @@ static const int FIRST_FOOT = 0; static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; +static const float HEAD_PUCK_Y_OFFSET = -0.0254f; +static const float HEAD_PUCK_Z_OFFSET = -0.152f; const char* ViveControllerManager::NAME { "OpenVR" }; @@ -82,24 +84,24 @@ static bool sortPucksXPosition(PuckPosePair firstPuck, PuckPosePair secondPuck) return (firstPuck.second.translation.x < secondPuck.second.translation.x); } -static bool comparePosePositions(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { +static bool determineFeetOrdering(const controller::Pose& poseA, const controller::Pose& poseB, glm::vec3 axis, glm::vec3 axisOrigin) { glm::vec3 poseAPosition = poseA.getTranslation(); glm::vec3 poseBPosition = poseB.getTranslation(); - glm::vec3 poseAFinalPosition = poseAPosition - axisOrigin; - glm::vec3 poseBFinalPosition = poseBPosition - axisOrigin; + glm::vec3 poseAVector = poseAPosition - axisOrigin; + glm::vec3 poseBVector = poseBPosition - axisOrigin; - float poseADistance = glm::dot(poseAFinalPosition, axis); - float poseBDistance = glm::dot(poseBFinalPosition, axis); - return (poseADistance > poseBDistance); + float poseAProjection = glm::dot(poseAVector, axis); + float poseBProjection = glm::dot(poseBVector, axis); + return (poseAProjection > poseBProjection); } -static glm::vec3 getHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { +static glm::vec3 getReferenceHeadXAxis(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return glmExtractRotation(finalHead) * Vectors::UNIT_X; } -static glm::vec3 getHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { +static glm::vec3 getReferenceHeadPosition(glm::mat4 defaultToReferenceMat, glm::mat4 defaultHead) { glm::mat4 finalHead = defaultToReferenceMat * defaultHead; return extractTranslation(finalHead); } @@ -383,19 +385,21 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr calibrateShoulders(defaultToReferenceMat, inputCalibration, firstShoulderIndex, secondShoulderIndex); } else if (_config == Config::FeetHipsAndHead && puckCount == MIN_FEET_HIPS_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); - glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); + _overrideHead = true; } else if (_config == Config::FeetHipsChestAndHead && puckCount == MIN_FEET_HIPS_CHEST_HEAD) { glm::mat4 headPuckDefaultToReferenceMat = recalculateDefaultToReferenceForHeadPuck(inputCalibration); - glm::vec3 headXAxis = getHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); - glm::vec3 headPosition = getHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headXAxis = getReferenceHeadXAxis(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); + glm::vec3 headPosition = getReferenceHeadPosition(headPuckDefaultToReferenceMat, inputCalibration.defaultHeadMat); calibrateFeet(headPuckDefaultToReferenceMat, inputCalibration, headXAxis, headPosition); calibrateHips(headPuckDefaultToReferenceMat, inputCalibration); calibrateChest(headPuckDefaultToReferenceMat, inputCalibration); calibrateHead(headPuckDefaultToReferenceMat, inputCalibration); + _overrideHead = true; } else { qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks"; uncalibrate(); @@ -506,7 +510,7 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::vec4(zPrime, 0.0f), glm::vec4(headPuckTranslation, 1.0f)); glm::mat4 headPuckOffset = glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, -0.0254f, -0.152f, 1.0f)); + glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), glm::vec4(0.0f, HEAD_PUCK_Y_OFFSET, HEAD_PUCK_Z_OFFSET, 1.0f)); glm::mat4 finalHeadPuck = newHeadPuck * headPuckOffset; @@ -713,7 +717,7 @@ void ViveControllerManager::InputDevice::calibrateFeet(glm::mat4& defaultToRefer controller::Pose& firstFootPose = firstFoot.second; controller::Pose& secondFootPose = secondFoot.second; - if (comparePosePositions(firstFootPose, secondFootPose, headXAxis, headPosition)) { + if (determineFeetOrdering(firstFootPose, secondFootPose, headXAxis, headPosition)) { _jointToPuckMap[controller::LEFT_FOOT] = firstFoot.first; _pucksOffset[firstFoot.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultLeftFoot, firstFootPose); _jointToPuckMap[controller::RIGHT_FOOT] = secondFoot.first; @@ -766,7 +770,6 @@ void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToRefer _jointToPuckMap[controller::HEAD] = head.first; _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead); - _overrideHead = true; } From f4328af66f6d2535707a5bef5f86b438defe2761 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 May 2017 17:12:27 +0100 Subject: [PATCH 19/43] made final requested changes --- plugins/openvr/src/ViveControllerManager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index e08929cf08..8ddf458210 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -503,6 +503,13 @@ glm::mat4 ViveControllerManager::InputDevice::recalculateDefaultToReferenceForHe glm::vec3 headPuckZAxis = cancelOutRollAndPitch(glmExtractRotation(headPuckAvatarMat)) * glm::vec3(0.0f, 0.0f, 1.0f); glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f); + // check that the head puck z axis is not parrallel to the world up + const float EPSILON = 1.0e-4f; + glm::vec3 zAxis = glmExtractRotation(headPuckAvatarMat) * glm::vec3(0.0f, 0.0f, 1.0f); + if (fabsf(fabsf(glm::dot(glm::normalize(worldUp), glm::normalize(zAxis))) - 1.0f) < EPSILON) { + headPuckZAxis = glm::vec3(1.0f, 0.0f, 0.0f); + } + glm::vec3 yPrime = glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 xPrime = glm::normalize(glm::cross(worldUp, headPuckZAxis)); glm::vec3 zPrime = glm::normalize(glm::cross(xPrime, yPrime)); From 7fcdc6124450e988df0a8900a9843edeb33816a0 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 31 May 2017 18:20:35 +0100 Subject: [PATCH 20/43] add head puck offset preference --- plugins/openvr/src/ViveControllerManager.cpp | 36 ++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 8ddf458210..5583ec6351 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -57,8 +57,8 @@ static const int FIRST_FOOT = 0; static const int SECOND_FOOT = 1; static const int HIP = 2; static const int CHEST = 3; -static const float HEAD_PUCK_Y_OFFSET = -0.0254f; -static const float HEAD_PUCK_Z_OFFSET = -0.152f; +static float HEAD_PUCK_Y_OFFSET = -0.0254f; +static float HEAD_PUCK_Z_OFFSET = -0.152f; const char* ViveControllerManager::NAME { "OpenVR" }; @@ -825,6 +825,38 @@ void ViveControllerManager::InputDevice::createPreferences() { auto preferences = DependencyManager::get(); static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration"; + { + static const float MIN_VALUE = -3.0f; + static const float MAX_VALUE = 3.0f; + static const float STEP = 0.01f; + + auto getter = [this]()->float { return HEAD_PUCK_Y_OFFSET; }; + auto setter = [this](const float& value) { HEAD_PUCK_Y_OFFSET = value; }; + + auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckYOffset", getter, setter); + preference->setMin(MIN_VALUE); + preference->setMax(MAX_VALUE); + preference->setDecimals(3); + preference->setStep(STEP); + preferences->addPreference(preference); + } + + { + static const float MIN_VALUE = -3.0f; + static const float MAX_VALUE = 3.0f; + static const float STEP = 0.01f; + + auto getter = [this]()->float { return HEAD_PUCK_Z_OFFSET; }; + auto setter = [this](const float& value) { HEAD_PUCK_Z_OFFSET = value; }; + + auto preference = new SpinnerPreference(VIVE_PUCKS_CONFIG, "HeadPuckXOffset", getter, setter); + preference->setMin(MIN_VALUE); + preference->setMax(MAX_VALUE); + preference->setStep(STEP); + preference->setDecimals(3); + preferences->addPreference(preference); + } + { auto getter = [this]()->QString { return _configStringMap[_preferedConfig]; }; auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); }; From 0441d8c70364550cee596b5e0f9a0bbc8de2a5a6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 May 2017 10:11:59 -0700 Subject: [PATCH 21/43] Add placeholder image when previous snapshot can't be loaded --- interface/resources/snapshot/img/no-image.jpg | Bin 0 -> 20880 bytes scripts/system/html/js/SnapshotReview.js | 32 +++++++++++------- scripts/system/snapshot.js | 6 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 interface/resources/snapshot/img/no-image.jpg diff --git a/interface/resources/snapshot/img/no-image.jpg b/interface/resources/snapshot/img/no-image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fff41c4e540fac630a540ab0ced18b498e86a576 GIT binary patch literal 20880 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 literal 0 HcmV?d00001 diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 6c5829d64f..2a4d535fee 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -293,19 +293,25 @@ function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPrevi isGif = img.src.split('.').pop().toLowerCase() === "gif"; imageContainer.appendChild(img); document.getElementById("snapshot-images").appendChild(imageContainer); - paths.push(image_data.localPath); - if (isGif) { - imageContainer.innerHTML += 'GIF'; - } - if (!isGifLoading) { - appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); - } - if (!isGifLoading || (isShowingPreviousImages && !image_data.story_id)) { - shareForUrl(id); - } - if (isShowingPreviousImages && isLoggedIn && image_data.story_id) { - updateShareInfo(id, image_data.story_id); - } + img.onload = function () { + paths.push(image_data.localPath); + if (isGif) { + imageContainer.innerHTML += 'GIF'; + } + if (!isGifLoading) { + appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); + } + if (!isGifLoading || (isShowingPreviousImages && !image_data.story_id)) { + shareForUrl(id); + } + if (isShowingPreviousImages && isLoggedIn && image_data.story_id) { + updateShareInfo(id, image_data.story_id); + } + }; + img.onerror = function () { + img.onload = null; + img.src = image_data.errorPath; + }; } function showConfirmationMessage(selectedID, destination) { if (selectedID.id) { diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 4c661482fc..1c257cfed5 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -273,7 +273,8 @@ function fillImageDataFromPrevious() { localPath: previousStillSnapPath, story_id: previousStillSnapStoryID, blastButtonDisabled: previousStillSnapBlastingDisabled, - hifiButtonDisabled: previousStillSnapHifiSharingDisabled + hifiButtonDisabled: previousStillSnapHifiSharingDisabled, + errorPath: Script.resolvePath(Script.resourcesPath() + 'snapshot/img/no-image.jpg') }); } if (previousAnimatedSnapPath !== "") { @@ -281,7 +282,8 @@ function fillImageDataFromPrevious() { localPath: previousAnimatedSnapPath, story_id: previousAnimatedSnapStoryID, blastButtonDisabled: previousAnimatedSnapBlastingDisabled, - hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled + hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled, + errorPath: Script.resolvePath(Script.resourcesPath() + 'snapshot/img/no-image.jpg') }); } } From 7b69a5dbd5f66dc383376d154c3bd5412292681b Mon Sep 17 00:00:00 2001 From: Patrick Manalich Date: Wed, 31 May 2017 10:46:04 -0700 Subject: [PATCH 22/43] Changed the DEFAULT_DISCOVERABILITY_MODE to Connections --- interface/src/DiscoverabilityManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 36f6d8633e..94fdacb5c0 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -25,7 +25,7 @@ #include -const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Friends; +const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Connections; DiscoverabilityManager::DiscoverabilityManager() : _mode("discoverabilityMode", DEFAULT_DISCOVERABILITY_MODE) From 5c5aaf3254757d320022b3136194b94b7d0e618f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 31 May 2017 20:18:42 +0200 Subject: [PATCH 23/43] Fix hidden dynamic property whenever the item is clone-able / DRYing propertyUpdates --- scripts/system/html/js/entityProperties.js | 186 +++++++-------------- 1 file changed, 60 insertions(+), 126 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index e000e14aec..f2873954ed 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -82,11 +82,23 @@ function showElements(els, show) { } } +function updateProperty(propertyName, propertyValue) { + var properties = {}; + properties[propertyName] = propertyValue; + updateProperties(properties); +} + +function updateProperties(properties) { + EventBridge.emitWebEvent(JSON.stringify({ + id: lastEntityID, + type: "update", + properties: properties + })); +} + function createEmitCheckedPropertyUpdateFunction(propertyName) { return function() { - EventBridge.emitWebEvent( - '{"id":' + lastEntityID + ', "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}' - ); + updateProperty(propertyName, this.checked); }; } @@ -105,13 +117,7 @@ function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { var properties = {}; properties[group] = {}; properties[group][propertyName] = this.checked; - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: properties - }) - ); + updateProperties(properties); }; } @@ -119,10 +125,7 @@ function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { decimals = decimals == undefined ? 4 : decimals; return function() { var value = parseFloat(this.value).toFixed(decimals); - - EventBridge.emitWebEvent( - '{"id":' + lastEntityID + ', "type":"update", "properties":{"' + propertyName + '":' + value + '}}' - ); + updateProperty(propertyName, value); }; } @@ -131,28 +134,14 @@ function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { var properties = {}; properties[group] = {}; properties[group][propertyName] = this.value; - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: properties, - }) - ); + updateProperties(properties); }; } function createEmitTextPropertyUpdateFunction(propertyName) { return function() { - var properties = {}; - properties[propertyName] = this.value; - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: properties, - }) - ); + updateProperty(propertyName, this.value); }; } @@ -161,62 +150,44 @@ function createEmitGroupTextPropertyUpdateFunction(group, propertyName) { var properties = {}; properties[group] = {}; properties[group][propertyName] = this.value; - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: properties, - }) - ); + updateProperties(properties); }; } function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { return function() { - var data = { - id: lastEntityID, - type: "update", - properties: {} - }; - data.properties[property] = { + var properties = {}; + properties[property] = { x: elX.value, y: elY.value, z: elZ.value, }; - EventBridge.emitWebEvent(JSON.stringify(data)); + updateProperties(properties); } }; function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) { return function() { - var data = { - id: lastEntityID, - type: "update", - properties: {} - }; - data.properties[group] = {}; - data.properties[group][property] = { + var properties = {}; + properties[group] = {}; + properties[group][property] = { x: elX.value, y: elY.value, z: elZ ? elZ.value : 0, }; - EventBridge.emitWebEvent(JSON.stringify(data)); + updateProperties(properties); } }; function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) { return function() { - var data = { - id: lastEntityID, - type: "update", - properties: {} - }; - data.properties[property] = { + var properties = {}; + properties[property] = { x: elX.value * multiplier, y: elY.value * multiplier, z: elZ.value * multiplier, }; - EventBridge.emitWebEvent(JSON.stringify(data)); + updateProperties(properties); } }; @@ -227,44 +198,35 @@ function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) }; function emitColorPropertyUpdate(property, red, green, blue, group) { - var data = { - id: lastEntityID, - type: "update", - properties: {} - }; + var properties = {}; if (group) { - data.properties[group] = {}; - data.properties[group][property] = { + properties[group] = {}; + properties[group][property] = { red: red, green: green, blue: blue, }; } else { - data.properties[property] = { + properties[property] = { red: red, green: green, blue: blue, }; } - EventBridge.emitWebEvent(JSON.stringify(data)); + updateProperties(properties); }; function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) { return function() { - var data = { - id: lastEntityID, - type: "update", - properties: {} - }; - data.properties[group] = {}; - - data.properties[group][property] = { + var properties = {}; + properties[group] = {}; + properties[group][property] = { red: elRed.value, green: elGreen.value, blue: elBlue.value, }; - EventBridge.emitWebEvent(JSON.stringify(data)); + updateProperties(properties); } }; @@ -277,18 +239,7 @@ function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElemen // We've unchecked, so remove propertyValue = propertyValue.replace(subPropertyString + ",", ""); } - - var _properties = {} - _properties[propertyName] = propertyValue; - - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: _properties - }) - ); - + updateProperty(propertyName, propertyValue); } function setUserDataFromEditor(noUpdate) { @@ -314,18 +265,11 @@ function setUserDataFromEditor(noUpdate) { ); return; } else { - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: { - userData: text - }, - }) - ); + updateProperty('userData', text); } } } + function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults) { var properties = {}; var parsedData = {}; @@ -372,13 +316,7 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults) { userDataElement.value = properties['userData']; - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: properties, - }) - ); + updateProperties(properties); } function userDataChanger(groupName, keyName, values, userDataElement, defaultValue) { var val = {}, def = {}; @@ -900,7 +838,6 @@ function loaded() { elCloneable.checked = parsedUserData["grabbableKey"].cloneable; elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; elCloneableDynamic.checked = parsedUserData["grabbableKey"].cloneDynamic ? parsedUserData["grabbableKey"].cloneDynamic : properties.dynamic; - elDynamic.checked = elCloneable.checked ? false: properties.dynamic; if (elCloneable.checked) { if ("cloneLifetime" in parsedUserData["grabbableKey"]) { elCloneableLifetime.value = parsedUserData["grabbableKey"].cloneLifetime ? parsedUserData["grabbableKey"].cloneLifetime : 300; @@ -1202,8 +1139,8 @@ function loaded() { }); elGrabbable.addEventListener('change', function() { - if(elCloneable.checked) { - elGrabbable.checked = false; + if (elCloneable.checked) { + elGrabbable.checked = false; } userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic); }); @@ -1213,17 +1150,22 @@ function loaded() { elCloneable.addEventListener('change', function (event) { var checked = event.target.checked; if (checked) { - multiDataUpdater("grabbableKey", - {cloneLifetime: elCloneableLifetime, cloneLimit: elCloneableLimit, cloneDynamic: elCloneableDynamic, cloneable: event.target}, - elUserData, {}); + multiDataUpdater("grabbableKey", { + cloneLifetime: elCloneableLifetime, + cloneLimit: elCloneableLimit, + cloneDynamic: elCloneableDynamic, + cloneable: event.target, + grabbable: null + }, elUserData, {}); elCloneableGroup.style.display = "block"; - EventBridge.emitWebEvent( - '{"id":' + lastEntityID + ', "type":"update", "properties":{"dynamic":false, "grabbable": false}}' - ); + updateProperty('dynamic', false); } else { - multiDataUpdater("grabbableKey", - {cloneLifetime: null, cloneLimit: null, cloneDynamic: null, cloneable: false}, - elUserData, {}); + multiDataUpdater("grabbableKey", { + cloneLifetime: null, + cloneLimit: null, + cloneDynamic: null, + cloneable: false + }, elUserData, {}); elCloneableGroup.style.display = "none"; } }); @@ -1258,15 +1200,7 @@ function loaded() { showUserDataTextArea(); showNewJSONEditorButton(); hideSaveUserDataButton(); - var properties = {}; - properties['userData'] = elUserData.value; - EventBridge.emitWebEvent( - JSON.stringify({ - id: lastEntityID, - type: "update", - properties: properties, - }) - ); + updateProperty('userData', elUserData.value) }); elSaveUserData.addEventListener("click", function() { From e1700915f7adf668f3a048d9123900e4a1d29dc4 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 31 May 2017 13:28:57 -0700 Subject: [PATCH 24/43] remove extra activity logger call --- scripts/system/makeUserConnection.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 37a334bd70..d95ad919b6 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -613,7 +613,6 @@ error = "All participants must be logged in to connect."; } result = error ? {status: 'error', connection: error} : response; - UserActivityLogger.makeUserConnection(connectingId, false, error || response); connectionRequestCompleted(); } else { result = response; @@ -668,8 +667,8 @@ // to be sure the hand is still close enough. If not, we terminate // the interval, go back to the waiting state. If we make it // the entire CONNECTING_TIME, we make the connection. We pass in - // whether or not the connecting id is actually logged in, as now we - // will allow to start the connection process but have it stop with a + // whether or not the connecting id is actually logged in, as now we + // will allow to start the connection process but have it stop with a // fail message before trying to call the backend if the other guy isn't // logged in. function startConnecting(id, jointIndex, isLoggedIn) { From a77491ccb1128679fbe92f47d8caa9eb75b08b80 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 25 May 2017 10:33:34 -0700 Subject: [PATCH 25/43] FileCache refactoring and tests --- libraries/networking/src/FileCache.cpp | 173 +++++++++++++++------- libraries/networking/src/FileCache.h | 52 ++++--- libraries/shared/src/NumericalConstants.h | 3 + tests/networking/src/FileCacheTests.cpp | 170 +++++++++++++++++++++ tests/networking/src/FileCacheTests.h | 30 ++++ 5 files changed, 353 insertions(+), 75 deletions(-) create mode 100644 tests/networking/src/FileCacheTests.cpp create mode 100644 tests/networking/src/FileCacheTests.h diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 8f3509d8f3..6cb8cd8f7c 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -11,52 +11,81 @@ #include "FileCache.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include + +#include +#include +#include +#include #include +#include +#ifdef Q_OS_WIN +#include +#else +#include +#endif + +#ifdef NDEBUG Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache", QtWarningMsg) - +#else +Q_LOGGING_CATEGORY(file_cache, "hifi.file_cache") +#endif using namespace cache; -static const std::string MANIFEST_NAME = "manifest"; +static const char DIR_SEP = '/'; +static const char EXT_SEP = '.'; -static const size_t BYTES_PER_MEGABYTES = 1024 * 1024; -static const size_t BYTES_PER_GIGABYTES = 1024 * BYTES_PER_MEGABYTES; -const size_t FileCache::DEFAULT_UNUSED_MAX_SIZE = 5 * BYTES_PER_GIGABYTES; // 5GB -const size_t FileCache::MAX_UNUSED_MAX_SIZE = 100 * BYTES_PER_GIGABYTES; // 100GB -const size_t FileCache::DEFAULT_OFFLINE_MAX_SIZE = 2 * BYTES_PER_GIGABYTES; // 2GB +const size_t FileCache::DEFAULT_MAX_SIZE { GB_TO_BYTES(5) }; +const size_t FileCache::MAX_MAX_SIZE { GB_TO_BYTES(100) }; +const size_t FileCache::DEFAULT_MIN_FREE_STORAGE_SPACE { GB_TO_BYTES(1) }; -void FileCache::setUnusedFileCacheSize(size_t unusedFilesMaxSize) { - _unusedFilesMaxSize = std::min(unusedFilesMaxSize, MAX_UNUSED_MAX_SIZE); - reserve(0); + +std::string getCacheName(const std::string& dirname_str) { + QString dirname { dirname_str.c_str() }; + QDir dir(dirname); + if (!dir.isAbsolute()) { + return dirname_str; + } + return dir.dirName().toStdString(); +} + +std::string getCachePath(const std::string& dirname_str) { + QString dirname { dirname_str.c_str() }; + QDir dir(dirname); + if (dir.isAbsolute()) { + return dirname_str; + } + return PathUtils::getAppLocalDataFilePath(dirname).toStdString(); +} + +void FileCache::setMinFreeSize(size_t size) { + _minFreeSpaceSize = size; + clean(); emit dirty(); } -void FileCache::setOfflineFileCacheSize(size_t offlineFilesMaxSize) { - _offlineFilesMaxSize = std::min(offlineFilesMaxSize, MAX_UNUSED_MAX_SIZE); +void FileCache::setMaxSize(size_t maxSize) { + _maxSize = std::min(maxSize, MAX_MAX_SIZE); + clean(); + emit dirty(); } FileCache::FileCache(const std::string& dirname, const std::string& ext, QObject* parent) : QObject(parent), _ext(ext), - _dirname(dirname), - _dirpath(PathUtils::getAppLocalDataFilePath(dirname.c_str()).toStdString()) {} + _dirname(getCacheName(dirname)), + _dirpath(getCachePath(dirname)) { +} FileCache::~FileCache() { clear(); } -void fileDeleter(File* file) { - file->deleter(); -} - void FileCache::initialize() { QDir dir(_dirpath.c_str()); @@ -84,7 +113,7 @@ void FileCache::initialize() { } FilePointer FileCache::addFile(Metadata&& metadata, const std::string& filepath) { - FilePointer file(createFile(std::move(metadata), filepath).release(), &fileDeleter); + FilePointer file(createFile(std::move(metadata), filepath).release(), std::mem_fn(&File::deleter)); if (file) { _numTotalFiles += 1; _totalFilesSize += file->getLength(); @@ -141,6 +170,7 @@ FilePointer FileCache::getFile(const Key& key) { if (it != _files.cend()) { file = it->second.lock(); if (file) { + file->touch(); // if it exists, it is active - remove it from the cache removeUnusedFile(file); qCDebug(file_cache, "[%s] Found %s", _dirname.c_str(), key.c_str()); @@ -155,44 +185,74 @@ FilePointer FileCache::getFile(const Key& key) { } std::string FileCache::getFilepath(const Key& key) { - return _dirpath + '/' + key + '.' + _ext; + return _dirpath + DIR_SEP + key + EXT_SEP + _ext; } -void FileCache::addUnusedFile(const FilePointer file) { +void FileCache::addUnusedFile(const FilePointer& file) { { Lock lock(_filesMutex); _files[file->getKey()] = file; } - reserve(file->getLength()); - file->_LRUKey = ++_lastLRUKey; - { Lock lock(_unusedFilesMutex); - _unusedFiles.insert({ file->_LRUKey, file }); + _unusedFiles.insert(file); _numUnusedFiles += 1; _unusedFilesSize += file->getLength(); } + clean(); emit dirty(); } -void FileCache::removeUnusedFile(const FilePointer file) { +void FileCache::removeUnusedFile(const FilePointer& file) { Lock lock(_unusedFilesMutex); - const auto it = _unusedFiles.find(file->_LRUKey); - if (it != _unusedFiles.cend()) { - _unusedFiles.erase(it); + if (_unusedFiles.erase(file)) { _numUnusedFiles -= 1; _unusedFilesSize -= file->getLength(); } } -void FileCache::reserve(size_t length) { +size_t FileCache::getOverbudgetAmount() const { + size_t result = 0; + + size_t currentFreeSpace = QStorageInfo(_dirpath.c_str()).bytesFree(); + if (_minFreeSpaceSize > currentFreeSpace) { + result = _minFreeSpaceSize - currentFreeSpace; + } + + if (_totalFilesSize > _maxSize) { + result = std::max(_totalFilesSize - _maxSize, result); + } + + return result; +} + +namespace cache { + struct FilePointerComparator { + bool operator()(const FilePointer& a, const FilePointer& b) { + return a->_modified > b->_modified; + } + }; +} + +void FileCache::clean() { + size_t overbudgetAmount = getOverbudgetAmount(); + + // Avoid sorting the unused files by LRU if we're not over budget / under free space + if (0 == overbudgetAmount) { + return; + } + Lock unusedLock(_unusedFilesMutex); - while (!_unusedFiles.empty() && - _unusedFilesSize + length > _unusedFilesMaxSize) { - auto it = _unusedFiles.begin(); - auto file = it->second; + using Queue = std::priority_queue, FilePointerComparator>; + Queue queue; + for (const auto& file : _unusedFiles) { + queue.push(file); + } + while (!queue.empty() && overbudgetAmount > 0) { + auto file = queue.top(); + queue.pop(); auto length = file->getLength(); unusedLock.unlock(); @@ -203,34 +263,32 @@ void FileCache::reserve(size_t length) { } unusedLock.lock(); - _unusedFiles.erase(it); + _unusedFiles.erase(file); _numTotalFiles -= 1; _numUnusedFiles -= 1; _totalFilesSize -= length; _unusedFilesSize -= length; + overbudgetAmount -= std::min(length, overbudgetAmount); } } void FileCache::clear() { - Lock unusedFilesLock(_unusedFilesMutex); - for (const auto& pair : _unusedFiles) { - auto& file = pair.second; - file->_cache = nullptr; + // Eliminate any overbudget files + clean(); - if (_totalFilesSize > _offlineFilesMaxSize) { - _totalFilesSize -= file->getLength(); - } else { - file->_shouldPersist = true; - qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); - } + // Mark everything remaining as persisted + Lock unusedFilesLock(_unusedFilesMutex); + for (auto& file : _unusedFiles) { + file->_shouldPersist = true; + file->_cache = nullptr; + qCDebug(file_cache, "[%s] Persisting %s", _dirname.c_str(), file->getKey().c_str()); } _unusedFiles.clear(); } void File::deleter() { if (_cache) { - FilePointer self(this, &fileDeleter); - _cache->addUnusedFile(self); + _cache->addUnusedFile(FilePointer(this, std::mem_fn(&File::deleter))); } else { deleteLater(); } @@ -239,7 +297,9 @@ void File::deleter() { File::File(Metadata&& metadata, const std::string& filepath) : _key(std::move(metadata.key)), _length(metadata.length), - _filepath(filepath) {} + _filepath(filepath), + _modified(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch()) { +} File::~File() { QFile file(getFilepath().c_str()); @@ -248,3 +308,8 @@ File::~File() { file.remove(); } } + +void File::touch() { + utime(_filepath.c_str(), nullptr); + _modified = std::max(QFileInfo(_filepath.c_str()).lastRead().toMSecsSinceEpoch(), _modified); +} \ No newline at end of file diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 908ddcd285..040e1ab592 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -35,24 +36,28 @@ class FileCache : public QObject { Q_PROPERTY(size_t sizeTotal READ getSizeTotalFiles NOTIFY dirty) Q_PROPERTY(size_t sizeCached READ getSizeCachedFiles NOTIFY dirty) - static const size_t DEFAULT_UNUSED_MAX_SIZE; - static const size_t MAX_UNUSED_MAX_SIZE; - static const size_t DEFAULT_OFFLINE_MAX_SIZE; + static const size_t DEFAULT_MAX_SIZE; + static const size_t MAX_MAX_SIZE; + static const size_t DEFAULT_MIN_FREE_STORAGE_SPACE; public: + // You can initialize the FileCache with a directory name (ex.: "temp_jpgs") that will be relative to the application local data, OR with a full path + // The file cache will ignore any file that doesn't match the extension provided + FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); + virtual ~FileCache(); + size_t getNumTotalFiles() const { return _numTotalFiles; } size_t getNumCachedFiles() const { return _numUnusedFiles; } size_t getSizeTotalFiles() const { return _totalFilesSize; } size_t getSizeCachedFiles() const { return _unusedFilesSize; } - void setUnusedFileCacheSize(size_t unusedFilesMaxSize); - size_t getUnusedFileCacheSize() const { return _unusedFilesSize; } + // Set the maximum amount of disk space to use on disk + void setMaxSize(size_t maxCacheSize); - void setOfflineFileCacheSize(size_t offlineFilesMaxSize); - - // initialize FileCache with a directory name (not a path, ex.: "temp_jpgs") and an ext (ex.: "jpg") - FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); - virtual ~FileCache(); + // Set the minumum amount of free disk space to retain. This supercedes the max size, + // so if the cache is consuming all but 500 MB of the drive, unused entries will be ejected + // to free up more space, regardless of the cache max size + void setMinFreeSize(size_t size); using Key = std::string; struct Metadata { @@ -76,10 +81,11 @@ public: signals: void dirty(); -protected: +public: /// must be called after construction to create the cache on the fs and restore persisted files void initialize(); + // Add file to the cache and return the cache entry. FilePointer writeFile(const char* data, Metadata&& metadata, bool overwrite = false); FilePointer getFile(const Key& key); @@ -95,11 +101,17 @@ private: std::string getFilepath(const Key& key); FilePointer addFile(Metadata&& metadata, const std::string& filepath); - void addUnusedFile(const FilePointer file); - void removeUnusedFile(const FilePointer file); - void reserve(size_t length); + void addUnusedFile(const FilePointer& file); + void removeUnusedFile(const FilePointer& file); + void clean(); void clear(); + size_t getOverbudgetAmount() const; + + // FIXME it might be desirable to have the min free space variable be static so it can be + // shared among multiple instances of FileCache + std::atomic _minFreeSpaceSize { DEFAULT_MIN_FREE_STORAGE_SPACE }; + std::atomic _maxSize { DEFAULT_MAX_SIZE }; std::atomic _numTotalFiles { 0 }; std::atomic _numUnusedFiles { 0 }; std::atomic _totalFilesSize { 0 }; @@ -113,12 +125,8 @@ private: std::unordered_map> _files; Mutex _filesMutex; - std::map _unusedFiles; + std::unordered_set _unusedFiles; Mutex _unusedFilesMutex; - size_t _unusedFilesMaxSize { DEFAULT_UNUSED_MAX_SIZE }; - int _lastLRUKey { 0 }; - - size_t _offlineFilesMaxSize { DEFAULT_OFFLINE_MAX_SIZE }; }; class File : public QObject { @@ -142,13 +150,15 @@ protected: private: friend class FileCache; + friend struct FilePointerComparator; const Key _key; const size_t _length; const std::string _filepath; - FileCache* _cache; - int _LRUKey { 0 }; + void touch(); + FileCache* _cache { nullptr }; + int64_t _modified { 0 }; bool _shouldPersist { false }; }; diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index d37e1e31c5..e2d2409d56 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -50,10 +50,13 @@ const int KILO_PER_MEGA = 1000; #define KB_TO_BYTES_SHIFT 10 #define MB_TO_BYTES_SHIFT 20 +#define GB_TO_BYTES_SHIFT 30 +#define GB_TO_BYTES(X) ((size_t)(X) << GB_TO_BYTES_SHIFT) #define MB_TO_BYTES(X) ((size_t)(X) << MB_TO_BYTES_SHIFT) #define KB_TO_BYTES(X) ((size_t)(X) << KB_TO_BYTES_SHIFT) +#define BYTES_TO_GB(X) (X >> GB_TO_BYTES_SHIFT) #define BYTES_TO_MB(X) (X >> MB_TO_BYTES_SHIFT) #define BYTES_TO_KB(X) (X >> KB_TO_BYTES_SHIFT) diff --git a/tests/networking/src/FileCacheTests.cpp b/tests/networking/src/FileCacheTests.cpp new file mode 100644 index 0000000000..0813d05a54 --- /dev/null +++ b/tests/networking/src/FileCacheTests.cpp @@ -0,0 +1,170 @@ +// +// ResoruceTests.cpp +// +// Copyright 2015 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 "FileCacheTests.h" + +#include + +QTEST_GUILESS_MAIN(FileCacheTests) + +using namespace cache; + +// Limit the file size to 10 MB +static const size_t MAX_UNUSED_SIZE { 1024 * 1024 * 10 }; +static const QByteArray TEST_DATA { 1024 * 1024, '0' }; + +static std::string getFileKey(int i) { + return QString(QByteArray { 1, (char)i }.toHex()).toStdString(); +} + +class TestFile : public File { + using Parent = File; +public: + TestFile(Metadata&& metadata, const std::string& filepath) + : Parent(std::move(metadata), filepath) { + } +}; + +class TestFileCache : public FileCache { + using Parent = FileCache; + +public: + TestFileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr) : Parent(dirname, ext, nullptr) { + initialize(); + } + + std::unique_ptr createFile(Metadata&& metadata, const std::string& filepath) override { + qCInfo(file_cache) << "Wrote KTX" << metadata.key.c_str(); + return std::unique_ptr(new TestFile(std::move(metadata), filepath)); + } +}; + +using CachePointer = std::shared_ptr; + +// The FileCache relies on deleteLater to clear unused files, but QTest classes don't run a conventional event loop +// so we need to call this function to force any pending deletes to occur in the File destructor +static void forceDeletes() { + while (QCoreApplication::hasPendingEvents()) { + QCoreApplication::sendPostedEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + } +} + +size_t FileCacheTests::getCacheDirectorySize() const { + size_t result = 0; + QDir dir(_testDir.path()); + for (const auto& file : dir.entryList({ "*.tmp" })) { + result += QFileInfo(dir.absoluteFilePath(file)).size(); + } + return result; +} + +CachePointer makeFileCache(QString& location) { + auto result = std::make_shared(location.toStdString(), "tmp"); + result->setMaxSize(MAX_UNUSED_SIZE); + return result; +} + +void FileCacheTests::initTestCase() { +} + +void FileCacheTests::testUnusedFiles() { + auto cache = makeFileCache(_testDir.path()); + std::list inUseFiles; + + { + for (int i = 0; i < 100; ++i) { + std::string key = getFileKey(i); + auto file = cache->writeFile(TEST_DATA.data(), TestFileCache::Metadata(key, TEST_DATA.size())); + inUseFiles.push_back(file); + forceDeletes(); + QThread::msleep(10); + } + QCOMPARE(cache->getNumCachedFiles(), (size_t)0); + QCOMPARE(cache->getNumTotalFiles(), (size_t)100); + // Release the in-use files + inUseFiles.clear(); + // Cache state is updated, but the directory state is unchanged, + // because the file deletes are triggered by an event loop + QCOMPARE(cache->getNumCachedFiles(), (size_t)10); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + QVERIFY(getCacheDirectorySize() > MAX_UNUSED_SIZE); + forceDeletes(); + QCOMPARE(cache->getNumCachedFiles(), (size_t)10); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + QVERIFY(getCacheDirectorySize() <= MAX_UNUSED_SIZE); + } + + // Reset the cache + cache = makeFileCache(_testDir.path()); + { + // Test files 0 to 89 are missing, because the LRU algorithm deleted them when we released the files + for (int i = 0; i < 90; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(!file.get()); + } + + QThread::msleep(1000); + // Test files 90 to 99 are present + for (int i = 90; i < 100; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(file.get()); + inUseFiles.push_back(file); + // Each access touches the file, so we need to sleep here to ensure that the files are + // spaced out in numeric order, otherwise later tests can't reliably determine the order + // for cache ejection + QThread::msleep(1000); + } + QCOMPARE(cache->getNumCachedFiles(), (size_t)0); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + inUseFiles.clear(); + QCOMPARE(cache->getNumCachedFiles(), (size_t)10); + QCOMPARE(cache->getNumTotalFiles(), (size_t)10); + } +} + +size_t FileCacheTests::getFreeSpace() const { + return QStorageInfo(_testDir.path()).bytesFree(); +} + +// FIXME if something else is changing the amount of free space on the target drive concurrently with this test +// running, then it may fail +void FileCacheTests::testFreeSpacePreservation() { + QCOMPARE(getCacheDirectorySize(), MAX_UNUSED_SIZE); + // Set the target free space to slightly above whatever the current free space is... + size_t targetFreeSpace = getFreeSpace() + MAX_UNUSED_SIZE / 2; + + // Reset the cache + auto cache = makeFileCache(_testDir.path()); + // Setting the min free space causes it to eject the oldest files that cause the cache to exceed the minimum space + cache->setMinFreeSize(targetFreeSpace); + QVERIFY(getFreeSpace() < targetFreeSpace); + forceDeletes(); + QCOMPARE(cache->getNumCachedFiles(), (size_t)5); + QCOMPARE(cache->getNumTotalFiles(), (size_t)5); + QVERIFY(getFreeSpace() >= targetFreeSpace); + for (int i = 0; i < 95; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(!file.get()); + } + for (int i = 95; i < 100; ++i) { + std::string key = getFileKey(i); + auto file = cache->getFile(key); + QVERIFY(file.get()); + } +} + +void FileCacheTests::cleanupTestCase() { +} + diff --git a/tests/networking/src/FileCacheTests.h b/tests/networking/src/FileCacheTests.h new file mode 100644 index 0000000000..838c15afb8 --- /dev/null +++ b/tests/networking/src/FileCacheTests.h @@ -0,0 +1,30 @@ +// +// ResourceTests.h +// +// Copyright 2015 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_ResourceTests_h +#define hifi_ResourceTests_h + +#include +#include + +class FileCacheTests : public QObject { + Q_OBJECT +private slots: + void initTestCase(); + void testUnusedFiles(); + void testFreeSpacePreservation(); + void cleanupTestCase(); + +private: + size_t getFreeSpace() const; + size_t getCacheDirectorySize() const; + QTemporaryDir _testDir; +}; + +#endif // hifi_ResourceTests_h From fd92afff8cedf0c9b0d0095671b867eeeb9b669e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 May 2017 11:00:43 -0700 Subject: [PATCH 26/43] Ensure old snaps don't accidentally get shared for_url --- scripts/system/html/js/SnapshotReview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 2a4d535fee..5fd884fba7 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -301,7 +301,7 @@ function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPrevi if (!isGifLoading) { appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); } - if (!isGifLoading || (isShowingPreviousImages && !image_data.story_id)) { + if ((!isGifLoading && !isShowingPreviousImages) || (isShowingPreviousImages && !image_data.story_id)) { shareForUrl(id); } if (isShowingPreviousImages && isLoggedIn && image_data.story_id) { From 70fdb4a168372855c9f218711c7a43496252630a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 May 2017 12:00:44 -0700 Subject: [PATCH 27/43] Fix incorrectly sharing old snapshots --- scripts/system/snapshot.js | 57 +++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 1c257cfed5..a1ea8e81af 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -36,6 +36,8 @@ var shareAfterLogin = false; var snapshotToShareAfterLogin = []; var METAVERSE_BASE = location.metaverseServerUrl; var isLoggedIn; +var numGifSnapshotUploadsPending = 0; +var numStillSnapshotUploadsPending = 0; // It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story, // POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS @@ -139,6 +141,12 @@ function onMessage(message) { if (isLoggedIn) { print('Sharing snapshot with audience "for_url":', message.data); Window.shareSnapshot(message.data, Settings.getValue("previousSnapshotHref")); + var isGif = message.data.split('.').pop().toLowerCase() === "gif"; + if (isGif) { + numGifSnapshotUploadsPending++; + } else { + numStillSnapshotUploadsPending++; + } } else { shareAfterLogin = true; snapshotToShareAfterLogin.push({ path: message.data, href: Settings.getValue("previousSnapshotHref") }); @@ -307,22 +315,39 @@ function onButtonClicked() { function snapshotUploaded(isError, reply) { if (!isError) { - var replyJson = JSON.parse(reply); - var storyID = replyJson.user_story.id; + var replyJson = JSON.parse(reply), + storyID = replyJson.user_story.id, + imageURL = replyJson.user_story.details.image_url, + isGif = imageURL.split('.').pop().toLowerCase() === "gif", + ignoreGifSnapshotData = false, + ignoreStillSnapshotData = false; storyIDsToMaybeDelete.push(storyID); - var imageURL = replyJson.user_story.details.image_url; - var isGif = imageURL.split('.').pop().toLowerCase() === "gif"; - print('SUCCESS: Snapshot uploaded! Story with audience:for_url created! ID:', storyID); - tablet.emitScriptEvent(JSON.stringify({ - type: "snapshot", - action: "snapshotUploadComplete", - story_id: storyID, - image_url: imageURL, - })); if (isGif) { - Settings.setValue("previousAnimatedSnapStoryID", storyID); + numGifSnapshotUploadsPending--; + if (numGifSnapshotUploadsPending !== 0) { + ignoreGifSnapshotData = true; + } } else { - Settings.setValue("previousStillSnapStoryID", storyID); + numStillSnapshotUploadsPending--; + if (numStillSnapshotUploadsPending !== 0) { + ignoreStillSnapshotData = true; + } + } + if ((isGif && !ignoreGifSnapshotData) || (!isGif && !ignoreStillSnapshotData)) { + print('SUCCESS: Snapshot uploaded! Story with audience:for_url created! ID:', storyID); + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "snapshotUploadComplete", + story_id: storyID, + image_url: imageURL, + })); + if (isGif) { + Settings.setValue("previousAnimatedSnapStoryID", storyID); + } else { + Settings.setValue("previousStillSnapStoryID", storyID); + } + } else { + print('Ignoring snapshotUploaded() callback for stale ' + (isGif ? 'GIF' : 'Still' ) + ' snapshot. Stale story ID:', storyID); } } else { print(reply); @@ -568,6 +593,12 @@ function onUsernameChanged() { snapshotToShareAfterLogin.forEach(function (element) { print('Uploading snapshot after login:', element.path); Window.shareSnapshot(element.path, element.href); + var isGif = element.path.split('.').pop().toLowerCase() === "gif"; + if (isGif) { + numGifSnapshotUploadsPending++; + } else { + numStillSnapshotUploadsPending++; + } }); } }); From cca0fa66004464713641aef86bd73a75ba4963bd Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 00:23:37 +0100 Subject: [PATCH 28/43] fixed some input recorder design issue --- .../controllers/src/controllers/Actions.h | 4 +- .../src/controllers/InputRecorder.cpp | 120 +++++++++++++----- .../src/controllers/InputRecorder.h | 20 +-- .../impl/endpoints/ActionEndpoint.cpp | 14 +- 4 files changed, 111 insertions(+), 47 deletions(-) diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 5727d4906e..2cb500c42a 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -42,8 +42,6 @@ enum class Action { LEFT_HAND = NUM_COMBINED_AXES, RIGHT_HAND, - LEFT_ARM, - RIGHT_ARM, LEFT_FOOT, RIGHT_FOOT, HIPS, @@ -103,6 +101,8 @@ enum class Action { // Bisected aliases for TRANSLATE_CAMERA_Z BOOM_IN, BOOM_OUT, + LEFT_ARM, + RIGHT_ARM, NUM_ACTIONS, diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index 60ff592144..a5bd58196d 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -9,7 +9,6 @@ #include "InputRecorder.h" #include -#include #include #include #include @@ -19,9 +18,12 @@ #include #include #include +#include #include #include +#include +#include "UserInputMapper.h" QString SAVE_DIRECTORY = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + BuildInfo::MODIFIED_ORGANIZATION + "/" + BuildInfo::INTERFACE_NAME + "/hifi-input-recordings/"; QString FILE_PREFIX_NAME = "input-recording-"; @@ -108,6 +110,7 @@ namespace controller { QJsonDocument saveData(object); QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); saveFile.write(compressedData); + saveFile.close(); } QJsonObject openFile(const QString& file, bool& status) { @@ -122,6 +125,7 @@ namespace controller { QJsonDocument jsonDoc = QJsonDocument::fromJson(compressedData); object = jsonDoc.object(); status = true; + openFile.close(); return object; } @@ -146,31 +150,43 @@ namespace controller { _actionStateList.clear(); } - void InputRecorder::saveRecording() { + QJsonObject InputRecorder::recordDataToJson() { QJsonObject data; data["frameCount"] = _framesRecorded; - + data["version"] = "1.0"; + QJsonArray actionArrayList; QJsonArray poseArrayList; for(const ActionStates actionState: _actionStateList) { QJsonArray actionArray; - for (const float value: actionState) { - actionArray.append(value); + for (const auto action: actionState) { + QJsonObject actionJson; + actionJson["name"] = action.first; + actionJson["value"] = action.second; + actionArray.append(actionJson); } actionArrayList.append(actionArray); } for (const PoseStates poseState: _poseStateList) { QJsonArray poseArray; - for (const Pose pose: poseState) { - poseArray.append(poseToJsonObject(pose)); + for (const auto pose: poseState) { + QJsonObject poseJson; + poseJson["name"] = pose.first; + poseJson["pose"] = poseToJsonObject(pose.second); + poseArray.append(poseJson); } poseArrayList.append(poseArray); } data["actionList"] = actionArrayList; data["poseList"] = poseArrayList; - exportToFile(data); + + return data; + } + + void InputRecorder::saveRecording() { + exportToFile(recordDataToJson()); } void InputRecorder::loadRecording(const QString& path) { @@ -181,8 +197,8 @@ namespace controller { resetFrame(); _poseStateList.clear(); _actionStateList.clear(); - QString filePath = path; - filePath.remove(0,8); + QUrl urlPath(path); + QString filePath = urlPath.toLocalFile(); QFileInfo info(filePath); QString extension = info.suffix(); if (extension != "gz") { @@ -190,8 +206,9 @@ namespace controller { return; } bool success = false; - QJsonObject data = openFile(info.absoluteFilePath(), success); - if (success) { + QJsonObject data = openFile(filePath, success); + auto keyValue = data.find("version"); + if (success && keyValue != data.end()) { _framesRecorded = data["frameCount"].toInt(); QJsonArray actionArrayList = data["actionList"].toArray(); QJsonArray poseArrayList = data["poseList"].toArray(); @@ -199,25 +216,71 @@ namespace controller { for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) { QJsonArray actionState = actionArrayList[actionIndex].toArray(); for (int index = 0; index < actionState.size(); index++) { - _currentFrameActions[index] = actionState[index].toDouble(); + QJsonObject actionObject = actionState[index].toObject();; + _currentFrameActions[actionObject["name"].toString()] = actionObject["value"].toDouble(); } _actionStateList.push_back(_currentFrameActions); - _currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS)); + _currentFrameActions.clear(); } for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) { QJsonArray poseState = poseArrayList[poseIndex].toArray(); for (int index = 0; index < poseState.size(); index++) { - _currentFramePoses[index] = jsonObjectToPose(poseState[index].toObject()); + QJsonObject poseObject = poseState[index].toObject(); + _currentFramePoses[poseObject["name"].toString()] = jsonObjectToPose(poseObject["pose"].toObject()); } _poseStateList.push_back(_currentFramePoses); - _currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS)); + _currentFramePoses.clear(); } + } else if (success) { + //convert recording to new reacording standard and rewrite file + auto userInputMapper = DependencyManager::get(); + _framesRecorded = data["frameCount"].toInt(); + QJsonArray actionArrayList = data["actionList"].toArray(); + QJsonArray poseArrayList = data["poseList"].toArray(); + + for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) { + QJsonArray actionState = actionArrayList[actionIndex].toArray(); + for (int index = 0; index < actionState.size(); index++) { + QString actionName = userInputMapper->getActionName(Action(index)); + _currentFrameActions[actionName] = actionState[index].toDouble(); + } + _actionStateList.push_back(_currentFrameActions); + _currentFrameActions.clear(); + } + + for (int poseIndex = 0; poseIndex < poseArrayList.size(); poseIndex++) { + QJsonArray poseState = poseArrayList[poseIndex].toArray(); + for (int index = 0; index < poseState.size(); index++) { + QString actionName = userInputMapper->getActionName(Action(index)); + _currentFramePoses[actionName] = jsonObjectToPose(poseState[index].toObject()); + } + _poseStateList.push_back(_currentFramePoses); + _currentFramePoses.clear(); + } + + convertFile(filePath); } _loading = false; } + void InputRecorder::convertFile(const QString& path) { + if (!QDir(SAVE_DIRECTORY).exists()) { + QDir().mkdir(SAVE_DIRECTORY); + } + + QJsonObject data = recordDataToJson(); + QFile saveFile (path); + if (!saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qWarning() << "could not open file: " << path; + return; + } + QJsonDocument saveData(data); + QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); + saveFile.write(compressedData); + } + void InputRecorder::stopRecording() { _recording = false; _framesRecorded = (int)_actionStateList.size(); @@ -234,41 +297,36 @@ namespace controller { _playCount = 0; } - void InputRecorder::setActionState(controller::Action action, float value) { + void InputRecorder::setActionState(const QString& action, float value) { if (_recording) { - _currentFrameActions[toInt(action)] += value; + _currentFrameActions[action] += value; } } - void InputRecorder::setActionState(controller::Action action, const controller::Pose pose) { + void InputRecorder::setActionState(const QString& action, const controller::Pose& pose) { if (_recording) { - _currentFramePoses[toInt(action)] = pose; + _currentFramePoses[action] = pose; } } void InputRecorder::resetFrame() { if (_recording) { - for(auto& channel : _currentFramePoses) { - channel = Pose(); - } - - for(auto& channel : _currentFrameActions) { - channel = 0.0f; - } + _currentFramePoses.clear(); + _currentFrameActions.clear(); } } - float InputRecorder::getActionState(controller::Action action) { + float InputRecorder::getActionState(const QString& action) { if (_actionStateList.size() > 0 ) { - return _actionStateList[_playCount][toInt(action)]; + return _actionStateList[_playCount][action]; } return 0.0f; } - controller::Pose InputRecorder::getPoseState(controller::Action action) { + controller::Pose InputRecorder::getPoseState(const QString& action) { if (_poseStateList.size() > 0) { - return _poseStateList[_playCount][toInt(action)]; + return _poseStateList[_playCount][action]; } return Pose(); diff --git a/libraries/controllers/src/controllers/InputRecorder.h b/libraries/controllers/src/controllers/InputRecorder.h index d1cc9a32eb..a8dd12724a 100644 --- a/libraries/controllers/src/controllers/InputRecorder.h +++ b/libraries/controllers/src/controllers/InputRecorder.h @@ -12,8 +12,10 @@ #include #include #include +#include #include +#include #include "Pose.h" #include "Actions.h" @@ -21,8 +23,8 @@ namespace controller { class InputRecorder { public: - using PoseStates = std::vector; - using ActionStates = std::vector; + using PoseStates = std::map; + using ActionStates = std::map; InputRecorder(); ~InputRecorder(); @@ -31,6 +33,7 @@ namespace controller { void saveRecording(); void loadRecording(const QString& path); + void convertFile(const QString& path); void startRecording(); void startPlayback(); void stopPlayback(); @@ -40,20 +43,21 @@ namespace controller { void resetFrame(); bool isRecording() { return _recording; } bool isPlayingback() { return (_playback && !_loading); } - void setActionState(controller::Action action, float value); - void setActionState(controller::Action action, const controller::Pose pose); - float getActionState(controller::Action action); - controller::Pose getPoseState(controller::Action action); + void setActionState(const QString& action, float value); + void setActionState(const QString& action, const controller::Pose& pose); + float getActionState(const QString& action); + controller::Pose getPoseState(const QString& action); QString getSaveDirectory(); void frameTick(); private: + QJsonObject recordDataToJson(); bool _recording { false }; bool _playback { false }; bool _loading { false }; std::vector _poseStateList = std::vector(); std::vector _actionStateList = std::vector(); - PoseStates _currentFramePoses = PoseStates(toInt(Action::NUM_ACTIONS)); - ActionStates _currentFrameActions = ActionStates(toInt(Action::NUM_ACTIONS)); + PoseStates _currentFramePoses; + ActionStates _currentFrameActions; int _framesRecorded { 0 }; int _playCount { 0 }; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp index 6c14533f02..eb834a0bf2 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -17,31 +17,33 @@ using namespace controller; void ActionEndpoint::apply(float newValue, const Pointer& source) { InputRecorder* inputRecorder = InputRecorder::getInstance(); + auto userInputMapper = DependencyManager::get(); + QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); if(inputRecorder->isPlayingback()) { - newValue = inputRecorder->getActionState(Action(_input.getChannel())); + newValue = inputRecorder->getActionState(actionName); } _currentValue += newValue; if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); } - inputRecorder->setActionState(Action(_input.getChannel()), newValue); + inputRecorder->setActionState(actionName, newValue); } void ActionEndpoint::apply(const Pose& value, const Pointer& source) { _currentPose = value; InputRecorder* inputRecorder = InputRecorder::getInstance(); - inputRecorder->setActionState(Action(_input.getChannel()), _currentPose); + auto userInputMapper = DependencyManager::get(); + QString actionName = userInputMapper->getActionName(Action(_input.getChannel())); + inputRecorder->setActionState(actionName, _currentPose); if (inputRecorder->isPlayingback()) { - _currentPose = inputRecorder->getPoseState(Action(_input.getChannel())); + _currentPose = inputRecorder->getPoseState(actionName); } if (!_currentPose.isValid()) { return; } if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); } } From 53701371abf335782d542aa418af626a411519d6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 31 May 2017 16:43:21 -0700 Subject: [PATCH 29/43] Fix some bugs :( --- scripts/system/html/js/SnapshotReview.js | 23 +++++++++++++---------- scripts/system/snapshot.js | 10 +++++++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 5fd884fba7..36f7124f93 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -21,6 +21,11 @@ var blastShareText = "Blast to my Connections", hifiAlreadySharedText = "Already Shared to Snaps Feed", facebookShareText = "Share to Facebook", twitterShareText = "Share to Twitter"; + +function fileExtensionMatches(filePath, extension) { + return filePath.split('.').pop().toLowerCase() === extension; +} + function showSetupInstructions() { var snapshotImagesDiv = document.getElementById("snapshot-images"); snapshotImagesDiv.className = "snapshotInstructions"; @@ -276,10 +281,10 @@ function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPrevi if (!image_data.localPath) { return; } - var id = "p" + (idCounter++), - imageContainer = document.createElement("DIV"), + var imageContainer = document.createElement("DIV"), img = document.createElement("IMG"), - isGif; + isGif = fileExtensionMatches(image_data.localPath, "gif"), + id = "p" + (isGif ? "1" : "0"); imageContainer.id = id; imageContainer.style.width = "95%"; imageContainer.style.height = "240px"; @@ -290,18 +295,17 @@ function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPrevi imageContainer.style.position = "relative"; img.id = id + "img"; img.src = image_data.localPath; - isGif = img.src.split('.').pop().toLowerCase() === "gif"; imageContainer.appendChild(img); document.getElementById("snapshot-images").appendChild(imageContainer); + paths.push(image_data.localPath); img.onload = function () { - paths.push(image_data.localPath); if (isGif) { imageContainer.innerHTML += 'GIF'; } if (!isGifLoading) { appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); } - if ((!isGifLoading && !isShowingPreviousImages) || (isShowingPreviousImages && !image_data.story_id)) { + if ((!isShowingPreviousImages && ((isGif && !isGifLoading) || !isGif)) || (isShowingPreviousImages && !image_data.story_id)) { shareForUrl(id); } if (isShowingPreviousImages && isLoggedIn && image_data.story_id) { @@ -638,9 +642,8 @@ window.onload = function () { // The last element of the message contents list contains a bunch of options, // including whether or not we can share stuff // The other elements of the list contain image paths. - - if (messageOptions.containsGif) { - if (messageOptions.processingGif) { + if (messageOptions.containsGif === true) { + if (messageOptions.processingGif === true) { imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon message.image_data.push({ localPath: messageOptions.loadingGifPath }); message.image_data.forEach(function (element, idx) { @@ -669,7 +672,7 @@ window.onload = function () { handleCaptureSetting(message.setting); break; case 'snapshotUploadComplete': - var isGif = message.image_url.split('.').pop().toLowerCase() === "gif"; + var isGif = fileExtensionMatches(message.image_url, "gif"); updateShareInfo(isGif ? "p1" : "p0", message.story_id); break; default: diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index a1ea8e81af..494ab245b1 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -58,6 +58,10 @@ function removeFromStoryIDsToMaybeDelete(story_id) { print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete)); } +function fileExtensionMatches(filePath, extension) { + return filePath.split('.').pop().toLowerCase() === extension; +} + function onMessage(message) { // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following: // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.) @@ -141,7 +145,7 @@ function onMessage(message) { if (isLoggedIn) { print('Sharing snapshot with audience "for_url":', message.data); Window.shareSnapshot(message.data, Settings.getValue("previousSnapshotHref")); - var isGif = message.data.split('.').pop().toLowerCase() === "gif"; + var isGif = fileExtensionMatches(message.data, "gif"); if (isGif) { numGifSnapshotUploadsPending++; } else { @@ -318,7 +322,7 @@ function snapshotUploaded(isError, reply) { var replyJson = JSON.parse(reply), storyID = replyJson.user_story.id, imageURL = replyJson.user_story.details.image_url, - isGif = imageURL.split('.').pop().toLowerCase() === "gif", + isGif = fileExtensionMatches(imageURL, "gif"), ignoreGifSnapshotData = false, ignoreStillSnapshotData = false; storyIDsToMaybeDelete.push(storyID); @@ -593,7 +597,7 @@ function onUsernameChanged() { snapshotToShareAfterLogin.forEach(function (element) { print('Uploading snapshot after login:', element.path); Window.shareSnapshot(element.path, element.href); - var isGif = element.path.split('.').pop().toLowerCase() === "gif"; + var isGif = fileExtensionMatches(element.path, "gif"); if (isGif) { numGifSnapshotUploadsPending++; } else { From d19956f93c4df01dee7b7017f7d879aa72c4efd7 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Wed, 31 May 2017 22:02:47 -0700 Subject: [PATCH 30/43] Fixing warnings --- assignment-client/src/Agent.cpp | 1 + .../src/controllers/impl/filters/LowVelocityFilter.h | 6 +++--- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 +- .../entities-renderer/src/RenderableZoneEntityItem.cpp | 1 - libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp | 4 ++++ libraries/ktx/src/ktx/Writer.cpp | 2 +- libraries/render-utils/src/BackgroundStage.cpp | 1 - libraries/render-utils/src/ZoneRenderer.cpp | 6 +++--- 8 files changed, 13 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 47836727fe..f517716b72 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -410,6 +410,7 @@ void Agent::executeScript() { bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed _audioGateOpen = audioGateOpen; + Q_UNUSED(openedInLastBlock); // the codec must be flushed to silence before sending silent packets, // so delay the transition to silent packets by one packet after becoming silent. diff --git a/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h b/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h index d870a5c551..b1c6be1f58 100644 --- a/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/LowVelocityFilter.h @@ -21,9 +21,9 @@ namespace controller { LowVelocityFilter(float rotationConstant, float translationConstant) : _translationConstant(translationConstant), _rotationConstant(rotationConstant) {} - virtual float apply(float value) const override { return value; } - virtual Pose apply(Pose newPose) const; - virtual bool parseParameters(const QJsonValue& parameters) override; + float apply(float value) const override { return value; } + Pose apply(Pose newPose) const override; + bool parseParameters(const QJsonValue& parameters) override; private: float _translationConstant { 0.1f }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 06227cdcfc..1b92adbc37 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -192,7 +192,7 @@ void EntityTreeRenderer::update() { tree->update(); // Handle enter/leave entity logic - bool updated = checkEnterLeaveEntities(); + checkEnterLeaveEntities(); // Even if we're not moving the mouse, if we started clicking on an entity and we have // not yet released the hold then this is still considered a holdingClickOnEntity event diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 3f7c0937e2..d3fd9a0980 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -367,7 +367,6 @@ void RenderableZoneEntityItem::sceneUpdateRenderItemFromEntity(render::Transacti bool sunChanged = _keyLightPropertiesChanged; bool backgroundChanged = _backgroundPropertiesChanged; - bool stageChanged = _stagePropertiesChanged; bool skyboxChanged = _skyboxPropertiesChanged; transaction.updateItem(_myMetaItem, [=](RenderableZoneEntityItemMeta& data) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 26ce56b387..ef9b6c4297 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -211,6 +211,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; case gpu::NUINT32: case gpu::NINT32: + case gpu::COMPRESSED: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); } @@ -484,6 +485,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_R8_SNORM; break; } + case gpu::COMPRESSED: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -527,6 +529,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_DEPTH_COMPONENT24; break; } + case gpu::COMPRESSED: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -641,6 +644,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::NUINT32: case gpu::NINT32: + case gpu::COMPRESSED: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); } diff --git a/libraries/ktx/src/ktx/Writer.cpp b/libraries/ktx/src/ktx/Writer.cpp index c94856e598..6d6cfa81a2 100644 --- a/libraries/ktx/src/ktx/Writer.cpp +++ b/libraries/ktx/src/ktx/Writer.cpp @@ -229,7 +229,7 @@ namespace ktx { } else { Image::FaceBytes faceBytes(NUM_CUBEMAPFACES); auto faceSize = srcImages[l]._faceSize; - for (int face = 0; face < NUM_CUBEMAPFACES; face++) { + for (uint32_t face = 0; face < NUM_CUBEMAPFACES; face++) { memcpy(currentPtr, srcImages[l]._faceBytes[face], faceSize); faceBytes[face] = currentPtr; currentPtr += faceSize; diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index 1a85a70863..5c2f55954a 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -112,7 +112,6 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, skybox->render(batch, args->getViewFrustum()); }); args->_batch = nullptr; - gpu::Batch& batch = *args->_batch; // break; } diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 3b4870fd3f..741487c824 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -169,7 +169,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I batch.setUniformBuffer(ZONE_DEFERRED_TRANSFORM_BUFFER, deferredTransform->getFrameTransformBuffer()); batch.setPipeline(getKeyLightPipeline()); - auto numKeys = keyLightStack.size(); + auto numKeys = (int) keyLightStack.size(); for (int i = numKeys - 1; i >= 0; i--) { model.setTranslation(glm::vec3(-4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0))); batch.setModelTransform(model); @@ -180,7 +180,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I } batch.setPipeline(getAmbientPipeline()); - auto numAmbients = ambientLightStack.size(); + auto numAmbients = (int) ambientLightStack.size(); for (int i = numAmbients - 1; i >= 0; i--) { model.setTranslation(glm::vec3(0.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0))); batch.setModelTransform(model); @@ -194,7 +194,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I } batch.setPipeline(getBackgroundPipeline()); - auto numBackgrounds = skyboxStack.size(); + auto numBackgrounds = (int) skyboxStack.size(); for (int i = numBackgrounds - 1; i >= 0; i--) { model.setTranslation(glm::vec3(4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0))); batch.setModelTransform(model); From 0372deebfffa19be483773d5cee5bb543b9c94ff Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Wed, 31 May 2017 22:09:30 -0700 Subject: [PATCH 31/43] Last warnings? --- tests/controllers/src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index e697bd501f..81f2f8d581 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -122,6 +122,10 @@ int main(int argc, char** argv) { glm::mat4(), glm::mat4(), glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), glm::mat4() }; @@ -144,6 +148,10 @@ int main(int argc, char** argv) { glm::mat4(), glm::mat4(), glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), + glm::mat4(), glm::mat4() }; From 03fdc1396c83d728f7df78a5358d97b4b26b474b Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Wed, 31 May 2017 23:20:10 -0700 Subject: [PATCH 32/43] Fixing the warnings --- interface/src/avatar/MyAvatar.cpp | 4 ++-- libraries/avatars/src/AvatarData.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 72a9281564..b9c4ac8d5e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2770,7 +2770,7 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const { auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex); return createMatFromQuatAndPos(leftFootRot, leftFootPos); } else { - return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_POS, DEFAULT_AVATAR_LEFTFOOT_POS); + return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS); } } @@ -2782,7 +2782,7 @@ glm::mat4 MyAvatar::getRightFootCalibrationMat() const { auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex); return createMatFromQuatAndPos(rightFootRot, rightFootPos); } else { - return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_POS, DEFAULT_AVATAR_RIGHTFOOT_POS); + return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS); } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d82068b8ac..196a284f51 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -850,7 +850,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { glm::quat sensorToWorldQuat; unpackOrientationQuatFromSixBytes(data->sensorToWorldQuat, sensorToWorldQuat); float sensorToWorldScale; - unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&data->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); + auto srcSensorToWorldScale = data->sensorToWorldScale; + unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&srcSensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); glm::vec3 sensorToWorldTrans(data->sensorToWorldTrans[0], data->sensorToWorldTrans[1], data->sensorToWorldTrans[2]); glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans); if (_sensorToWorldMatrixCache.get() != sensorToWorldMatrix) { From 3c9b14ee94863f00ae9a36d6759075bcb0c00258 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Wed, 31 May 2017 23:26:20 -0700 Subject: [PATCH 33/43] Oene more fix --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b9c4ac8d5e..966bca252e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2805,7 +2805,7 @@ glm::mat4 MyAvatar::getLeftArmCalibrationMat() const { auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex); return createMatFromQuatAndPos(leftArmRot, leftArmPos); } else { - return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS); + return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS); } } From 07eb1d89e1b1bb4ab2165aa915cfba8bfdcbfc95 Mon Sep 17 00:00:00 2001 From: Sam Cake Date: Wed, 31 May 2017 23:59:44 -0700 Subject: [PATCH 34/43] fixing the warning on macosx about the non portable file name --- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index b759a06aee..80c8698bb6 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include From a17886aa58e90b0a9ffd7b1aec9db0218084d4d2 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 1 Jun 2017 07:24:03 -0700 Subject: [PATCH 35/43] do not allow sorting on non-sortable columns in PAL --- interface/resources/qml/hifi/Pal.qml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 8f6b00f459..634da229d3 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -388,8 +388,13 @@ Rectangle { sortIndicatorColumn: settings.nearbySortIndicatorColumn; sortIndicatorOrder: settings.nearbySortIndicatorOrder; onSortIndicatorColumnChanged: { - settings.nearbySortIndicatorColumn = sortIndicatorColumn; - sortModel(); + if (sortIndicatorColumn > 1) { + // these are not sortable, switch back to last column + sortIndicatorColumn = settings.nearbySortIndicatorColumn; + } else { + settings.nearbySortIndicatorColumn = sortIndicatorColumn; + sortModel(); + } } onSortIndicatorOrderChanged: { settings.nearbySortIndicatorOrder = sortIndicatorOrder; From f79c9ea0218c3c50580caeb5b48ec177423d9e7a Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 1 Jun 2017 07:29:31 -0700 Subject: [PATCH 36/43] we can sort on ignored --- interface/resources/qml/hifi/Pal.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 634da229d3..2d6b21b219 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -388,7 +388,7 @@ Rectangle { sortIndicatorColumn: settings.nearbySortIndicatorColumn; sortIndicatorOrder: settings.nearbySortIndicatorOrder; onSortIndicatorColumnChanged: { - if (sortIndicatorColumn > 1) { + if (sortIndicatorColumn > 2) { // these are not sortable, switch back to last column sortIndicatorColumn = settings.nearbySortIndicatorColumn; } else { From 257813ec380ee1c4b71ca596e2d8ebc214bfaebf Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 1 Jun 2017 09:19:02 -0700 Subject: [PATCH 37/43] comment the code --- libraries/avatars/src/AvatarData.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 196a284f51..4407e12295 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -850,6 +850,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { glm::quat sensorToWorldQuat; unpackOrientationQuatFromSixBytes(data->sensorToWorldQuat, sensorToWorldQuat); float sensorToWorldScale; + // Grab a local copy of sensorToWorldScale to be able to use the unpack function with a pointer on it, + // a direct pointer on the struct attribute triggers warnings because of potential misalignement. auto srcSensorToWorldScale = data->sensorToWorldScale; unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&srcSensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX); glm::vec3 sensorToWorldTrans(data->sensorToWorldTrans[0], data->sensorToWorldTrans[1], data->sensorToWorldTrans[2]); From c686acc071002c14fe0410c8a0ca778cd8276824 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 18:20:56 +0100 Subject: [PATCH 38/43] fixing build issue --- libraries/controllers/src/controllers/InputRecorder.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index a5bd58196d..c0a3adb4b1 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -93,7 +93,7 @@ namespace controller { } - void exportToFile(QJsonObject& object) { + void exportToFile(const QJsonObject& object) { if (!QDir(SAVE_DIRECTORY).exists()) { QDir().mkdir(SAVE_DIRECTORY); } @@ -186,7 +186,8 @@ namespace controller { } void InputRecorder::saveRecording() { - exportToFile(recordDataToJson()); + QJsonObject jsonData = recordDataToJson(); + exportToFile(jsonData); } void InputRecorder::loadRecording(const QString& path) { From 0e82b33d6d4cf9cadccde4abd9b33a127d0003b3 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 19:00:31 +0100 Subject: [PATCH 39/43] final changes --- .../src/controllers/InputRecorder.cpp | 20 +------------------ .../src/controllers/InputRecorder.h | 1 - 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index c0a3adb4b1..e8bcd3a006 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -259,34 +259,16 @@ namespace controller { _poseStateList.push_back(_currentFramePoses); _currentFramePoses.clear(); } - - convertFile(filePath); } _loading = false; } - - void InputRecorder::convertFile(const QString& path) { - if (!QDir(SAVE_DIRECTORY).exists()) { - QDir().mkdir(SAVE_DIRECTORY); - } - - QJsonObject data = recordDataToJson(); - QFile saveFile (path); - if (!saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - qWarning() << "could not open file: " << path; - return; - } - QJsonDocument saveData(data); - QByteArray compressedData = qCompress(saveData.toJson(QJsonDocument::Compact)); - saveFile.write(compressedData); - } void InputRecorder::stopRecording() { _recording = false; _framesRecorded = (int)_actionStateList.size(); } - + void InputRecorder::startPlayback() { _playback = true; _recording = false; diff --git a/libraries/controllers/src/controllers/InputRecorder.h b/libraries/controllers/src/controllers/InputRecorder.h index a8dd12724a..9adb8e386f 100644 --- a/libraries/controllers/src/controllers/InputRecorder.h +++ b/libraries/controllers/src/controllers/InputRecorder.h @@ -33,7 +33,6 @@ namespace controller { void saveRecording(); void loadRecording(const QString& path); - void convertFile(const QString& path); void startRecording(); void startPlayback(); void stopPlayback(); From 3d98f1b3eebc605cc2bcbed823e3c172a573e208 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 Jun 2017 13:46:37 -0700 Subject: [PATCH 40/43] print metaverse session ID to debug log --- libraries/networking/src/AccountManager.cpp | 7 +++++++ libraries/networking/src/AccountManager.h | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 6266ad0f89..85232f9f61 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -201,6 +201,13 @@ void AccountManager::setAuthURL(const QUrl& authURL) { } } +void AccountManager::setSessionID(const QUuid& sessionID) { + if (_sessionID != sessionID) { + qCDebug(networking) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(sessionID); + _sessionID = sessionID; + } +} + void AccountManager::sendRequest(const QString& path, AccountManagerAuth::Type authType, QNetworkAccessManager::Operation operation, diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index dd2216957f..9a456ca7e8 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -90,7 +90,7 @@ public: static QJsonObject dataObjectFromResponse(QNetworkReply& requestReply); QUuid getSessionID() const { return _sessionID; } - void setSessionID(const QUuid& sessionID) { _sessionID = sessionID; } + void setSessionID(const QUuid& sessionID); void setTemporaryDomain(const QUuid& domainID, const QString& key); const QString& getTemporaryDomainKey(const QUuid& domainID) { return _accountInfo.getTemporaryDomainKey(domainID); } From 725388043f2cd1d7ebbf82f977a8fdfd82e2c331 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 1 Jun 2017 22:30:50 +0100 Subject: [PATCH 41/43] removed double semi-colons --- libraries/controllers/src/controllers/InputRecorder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/InputRecorder.cpp b/libraries/controllers/src/controllers/InputRecorder.cpp index e8bcd3a006..dbc2e4df52 100644 --- a/libraries/controllers/src/controllers/InputRecorder.cpp +++ b/libraries/controllers/src/controllers/InputRecorder.cpp @@ -217,7 +217,7 @@ namespace controller { for (int actionIndex = 0; actionIndex < actionArrayList.size(); actionIndex++) { QJsonArray actionState = actionArrayList[actionIndex].toArray(); for (int index = 0; index < actionState.size(); index++) { - QJsonObject actionObject = actionState[index].toObject();; + QJsonObject actionObject = actionState[index].toObject(); _currentFrameActions[actionObject["name"].toString()] = actionObject["value"].toDouble(); } _actionStateList.push_back(_currentFrameActions); From 06b3ecfdc3072a5a90dab3e21ae1915f3d2bddfc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 Jun 2017 15:50:56 -0700 Subject: [PATCH 42/43] also log metaverse session ID during ctor of AccountManager --- libraries/networking/src/AccountManager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 85232f9f61..ab336b0b31 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -90,6 +90,8 @@ AccountManager::AccountManager(UserAgentGetter userAgentGetter) : qRegisterMetaType("QHttpMultiPart*"); qRegisterMetaType(); + + qCDebug(networking) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(_sessionID); } const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; @@ -203,7 +205,7 @@ void AccountManager::setAuthURL(const QUrl& authURL) { void AccountManager::setSessionID(const QUuid& sessionID) { if (_sessionID != sessionID) { - qCDebug(networking) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(sessionID); + qCDebug(networking) << "Metaverse session ID changed to" << uuidStringWithoutCurlyBraces(sessionID); _sessionID = sessionID; } } From 0f22b07026be8558ba18041f8d545bf77f6f80b2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 1 Jun 2017 16:07:36 -0700 Subject: [PATCH 43/43] move session ID logging later in app startup --- interface/src/Application.cpp | 2 ++ libraries/networking/src/AccountManager.cpp | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 46c4c0bd4e..8c6bea0905 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1459,6 +1459,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo updateSystemTabletMode(); connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); + + qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID()); } void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index ab336b0b31..c6fffbfdbd 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -90,8 +90,6 @@ AccountManager::AccountManager(UserAgentGetter userAgentGetter) : qRegisterMetaType("QHttpMultiPart*"); qRegisterMetaType(); - - qCDebug(networking) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(_sessionID); } const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";