From 90b77030d5c7da0e88454b503de783bb7e85a080 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 7 Mar 2018 02:55:47 +0100 Subject: [PATCH 01/78] MyAvatar.hasScriptedBlendshapes property --- interface/src/avatar/MyAvatar.h | 4 ++++ interface/src/avatar/MyHead.cpp | 7 ++++--- libraries/avatars/src/AvatarData.cpp | 3 ++- libraries/avatars/src/AvatarData.h | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index fa5206e128..f2d1baf0aa 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -133,6 +133,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(AudioListenerMode audioListenerModeHead READ getAudioListenerModeHead) Q_PROPERTY(AudioListenerMode audioListenerModeCamera READ getAudioListenerModeCamera) Q_PROPERTY(AudioListenerMode audioListenerModeCustom READ getAudioListenerModeCustom) + Q_PROPERTY(bool hasScriptedBlendshapes READ getHasScriptedBlendshapes WRITE setHasScriptedBlendshapes) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) @@ -654,6 +655,8 @@ private: virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); } bool getShouldRenderLocally() const { return _shouldRender; } + void setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { _hasScriptedBlendShapes = hasScriptedBlendshapes; } + bool getHasScriptedBlendshapes() const override { return _hasScriptedBlendShapes; } bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; @@ -764,6 +767,7 @@ private: bool _hmdRollControlEnabled { true }; float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; + bool _hasScriptedBlendShapes { false }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index cad2f9e5d0..b7b8aa5e52 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -46,9 +46,10 @@ void MyHead::simulate(float deltaTime) { auto player = DependencyManager::get(); // Only use face trackers when not playing back a recording. if (!player->isPlaying()) { - FaceTracker* faceTracker = qApp->getActiveFaceTracker(); - _isFaceTrackerConnected = faceTracker != nullptr && !faceTracker->isMuted(); - if (_isFaceTrackerConnected) { + auto faceTracker = qApp->getActiveFaceTracker(); + bool hasActualFaceTrackerConnected = faceTracker && !faceTracker->isMuted(); + _isFaceTrackerConnected = hasActualFaceTrackerConnected || _owningAvatar->getHasScriptedBlendshapes(); + if (hasActualFaceTrackerConnected) { _transientBlendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1bbc8cc1a5..dcaae59a38 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -300,7 +300,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent tranlationChangedSince(lastSentTime) || parentInfoChangedSince(lastSentTime)); - hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime)); + hasFaceTrackerInfo = !dropFaceTracking && (hasFaceTracker() || getHasScriptedBlendshapes()) && + (sendAll || faceTrackerInfoChangedSince(lastSentTime)); hasJointData = sendAll || !sendMinimum; hasJointDefaultPoseFlags = hasJointData; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e927120b07..1f6d2e3630 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -497,6 +497,8 @@ public: float getDomainLimitedScale() const; + virtual bool getHasScriptedBlendshapes() const { return false; } + /**jsdoc * returns the minimum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. From b0187ecfd3dfdceb084884a72cc1a432f6018ebf Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 24 Apr 2018 19:24:23 +0200 Subject: [PATCH 02/78] add MyAvatar.hasAudioEnabledFaceMovement API property which is enabled by default --- interface/src/avatar/MyAvatar.h | 4 +++ interface/src/avatar/MyHead.cpp | 45 ++++++++++++++++-------------- libraries/avatars/src/AvatarData.h | 1 + 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b8c9e4d595..537b5de8d8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -134,6 +134,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(AudioListenerMode audioListenerModeCamera READ getAudioListenerModeCamera) Q_PROPERTY(AudioListenerMode audioListenerModeCustom READ getAudioListenerModeCustom) Q_PROPERTY(bool hasScriptedBlendshapes READ getHasScriptedBlendshapes WRITE setHasScriptedBlendshapes) + Q_PROPERTY(bool hasAudioEnabledFaceMovement READ getHasAudioEnabledFaceMovement WRITE setHasAudioEnabledFaceMovement) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) @@ -684,6 +685,8 @@ private: bool getShouldRenderLocally() const { return _shouldRender; } void setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { _hasScriptedBlendShapes = hasScriptedBlendshapes; } bool getHasScriptedBlendshapes() const override { return _hasScriptedBlendShapes; } + void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement) { _hasAudioEnabledFaceMovement = hasAudioEnabledFaceMovement; } + bool getHasAudioEnabledFaceMovement() const override { return _hasAudioEnabledFaceMovement; } bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; @@ -793,6 +796,7 @@ private: float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; bool _hasScriptedBlendShapes { false }; + bool _hasAudioEnabledFaceMovement { true }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index b7b8aa5e52..ed38fdd004 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -47,29 +47,32 @@ void MyHead::simulate(float deltaTime) { // Only use face trackers when not playing back a recording. if (!player->isPlaying()) { auto faceTracker = qApp->getActiveFaceTracker(); - bool hasActualFaceTrackerConnected = faceTracker && !faceTracker->isMuted(); + const bool hasActualFaceTrackerConnected = faceTracker && !faceTracker->isMuted(); _isFaceTrackerConnected = hasActualFaceTrackerConnected || _owningAvatar->getHasScriptedBlendshapes(); - if (hasActualFaceTrackerConnected) { - _transientBlendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); - - if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { - - if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { - calculateMouthShapes(deltaTime); - - const int JAW_OPEN_BLENDSHAPE = 21; - const int MMMM_BLENDSHAPE = 34; - const int FUNNEL_BLENDSHAPE = 40; - const int SMILE_LEFT_BLENDSHAPE = 28; - const int SMILE_RIGHT_BLENDSHAPE = 29; - _transientBlendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; - _transientBlendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; - _transientBlendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; - _transientBlendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; - _transientBlendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; - } - applyEyelidOffset(getFinalOrientationInWorldFrame()); + if (_isFaceTrackerConnected) { + if (hasActualFaceTrackerConnected) { + _transientBlendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + } else { + _transientBlendshapeCoefficients.fill(0, _blendshapeCoefficients.size()); } + + if (_owningAvatar->getHasAudioEnabledFaceMovement() || (faceTracker && (typeid(*faceTracker) == typeid(DdeFaceTracker)) + && Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth))) { + + calculateMouthShapes(deltaTime); + + const int JAW_OPEN_BLENDSHAPE = 21; + const int MMMM_BLENDSHAPE = 34; + const int FUNNEL_BLENDSHAPE = 40; + const int SMILE_LEFT_BLENDSHAPE = 28; + const int SMILE_RIGHT_BLENDSHAPE = 29; + _transientBlendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; + _transientBlendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; + _transientBlendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; + _transientBlendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; + _transientBlendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; + } + applyEyelidOffset(getFinalOrientationInWorldFrame()); } auto eyeTracker = DependencyManager::get(); _isEyeTrackerConnected = eyeTracker->isTracking(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fbef083dcd..fe4ff364ae 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -503,6 +503,7 @@ public: float getDomainLimitedScale() const; virtual bool getHasScriptedBlendshapes() const { return false; } + virtual bool getHasAudioEnabledFaceMovement() const { return false; } /**jsdoc * returns the minimum scale allowed for this avatar in the current domain. From 192e48a376026b565ce89b39992b6060fc48d1c6 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 25 Apr 2018 13:47:00 +0200 Subject: [PATCH 03/78] style fix + JSDocs --- interface/src/avatar/MyAvatar.h | 2 ++ interface/src/avatar/MyHead.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 537b5de8d8..9bccc93fbf 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -80,6 +80,8 @@ class MyAvatar : public Avatar { * MyAvatar.audioListenerModeCamera - the listener is relative to the camera. * MyAvatar.audioListenerModeCustom - the listener is at a custom location specified by the MyAvatar.customListenPosition * and MyAvatar.customListenOrientation properties. + * @property hasScriptedBlendshapes {bool} Blendshapes will be transmitted over the network if set to true. + * @property hasAudioEnabledFaceMovement {bool} If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. * @property customListenPosition {Vec3} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the position * of audio spatialization listener. * @property customListenOrientation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index ed38fdd004..44d679f3f8 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -72,7 +72,7 @@ void MyHead::simulate(float deltaTime) { _transientBlendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; _transientBlendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; } - applyEyelidOffset(getFinalOrientationInWorldFrame()); + applyEyelidOffset(getFinalOrientationInWorldFrame()); } auto eyeTracker = DependencyManager::get(); _isEyeTrackerConnected = eyeTracker->isTracking(); From 484e20ea55dc6027b06447e6ca56baeabed88f52 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 25 Apr 2018 19:01:18 +0200 Subject: [PATCH 04/78] JSDOC - change bool type to boolean, to match JS type --- interface/src/avatar/MyAvatar.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 9bccc93fbf..23faff88ee 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -80,8 +80,8 @@ class MyAvatar : public Avatar { * MyAvatar.audioListenerModeCamera - the listener is relative to the camera. * MyAvatar.audioListenerModeCustom - the listener is at a custom location specified by the MyAvatar.customListenPosition * and MyAvatar.customListenOrientation properties. - * @property hasScriptedBlendshapes {bool} Blendshapes will be transmitted over the network if set to true. - * @property hasAudioEnabledFaceMovement {bool} If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. + * @property hasScriptedBlendshapes {boolean} Blendshapes will be transmitted over the network if set to true. + * @property hasAudioEnabledFaceMovement {boolean} If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. * @property customListenPosition {Vec3} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the position * of audio spatialization listener. * @property customListenOrientation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation From f3f29bb6611146319b3e9860ccf10cb052014314 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 25 Apr 2018 12:13:46 -0700 Subject: [PATCH 05/78] allow avatar data to use full segment size --- .../src/avatars/AvatarMixerSlave.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index fb4b65726a..ece0ab9616 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -392,21 +392,26 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) quint64 end = usecTimestampNow(); _stats.toByteArrayElapsedTime += (end - start); - static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID); - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data"; + auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; + if (bytes.size() > maxAvatarDataBytes) { + qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() + << "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data"; dropFaceTracking = true; // first try dropping the facial data bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData"; + if (bytes.size() > maxAvatarDataBytes) { + qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() + << "without facial data resulted in very large buffer of" << bytes.size() + << "bytes - reducing to MinimumData"; bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther, hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!"; + if (bytes.size() > maxAvatarDataBytes) { + qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() + << "MinimumData resulted in very large buffer of" << bytes.size() + << "bytes - refusing to send avatar"; includeThisAvatar = false; } } From bbd31d590bb5f465fa5f906ac7dbf756abf7af53 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 26 Apr 2018 03:12:44 +0200 Subject: [PATCH 06/78] defaults --- interface/src/avatar/MyAvatar.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ef2b0cd042..512273aee6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -82,8 +82,8 @@ class MyAvatar : public Avatar { * @property {number} audioListenerModeCamera=1 - The audio listening position is at the camera. Read-only. * @property {number} audioListenerModeCustom=2 - The audio listening position is at a the position specified by set by the * customListenPosition and customListenOrientation property values. Read-only. - * @property hasScriptedBlendshapes {boolean} Blendshapes will be transmitted over the network if set to true. - * @property hasAudioEnabledFaceMovement {boolean} If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. + * @property {boolean} hasScriptedBlendshapes=false - Blendshapes will be transmitted over the network if set to true. + * @property {boolean} hasAudioEnabledFaceMovement=true - If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. * @property {Vec3} customListenPosition=Vec3.ZERO - The listening position used when the audioListenerMode * property value is audioListenerModeCustom. * @property {Quat} customListenOrientation=Quat.IDENTITY - The listening orientation used when the From 8b4298514e58530d9c5236cef450ad03431a7d0a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 15 May 2018 21:39:17 +0200 Subject: [PATCH 07/78] send out one last blendshape update when setting MyAvatar.hasScriptedBlendshapes to false --- interface/src/avatar/MyAvatar.cpp | 13 +++++++++++++ interface/src/avatar/MyAvatar.h | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6c6f6d4d41..e4d255f4ee 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2081,6 +2081,19 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { return !defaultMode || !firstPerson || !insideHead; } +void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { + if (hasScriptedBlendshapes == _hasScriptedBlendShapes) { + return; + } + if (!hasScriptedBlendshapes) { + // send a forced avatarData update to make sure the script can send neutal blendshapes on unload + // without having to wait for the update loop, make sure _hasScriptedBlendShapes is still true + // before sending the update, or else it won't send the neutal blendshapes to the receiving clients + sendAvatarDataPacket(true); + } + _hasScriptedBlendShapes = hasScriptedBlendshapes; +} + void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 76966b5ab3..46cdd7b8ae 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1345,7 +1345,7 @@ private: virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override; void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); } bool getShouldRenderLocally() const { return _shouldRender; } - void setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { _hasScriptedBlendShapes = hasScriptedBlendshapes; } + void setHasScriptedBlendshapes(bool hasScriptedBlendshapes); bool getHasScriptedBlendshapes() const override { return _hasScriptedBlendShapes; } void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement) { _hasAudioEnabledFaceMovement = hasAudioEnabledFaceMovement; } bool getHasAudioEnabledFaceMovement() const override { return _hasAudioEnabledFaceMovement; } @@ -1457,7 +1457,7 @@ private: bool _hmdRollControlEnabled { true }; float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; - bool _hasScriptedBlendShapes { false }; + std::atomic _hasScriptedBlendShapes { false }; bool _hasAudioEnabledFaceMovement { true }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access From adee5e9ef7a544e201fadf3a5e75ff71aa8d0287 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 24 May 2018 17:54:53 -0700 Subject: [PATCH 08/78] fix crash --- interface/src/ui/overlays/Web3DOverlay.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index c678e3d2a2..8af818edc6 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -259,7 +259,6 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this); _webSurface->getSurfaceContext()->setContextProperty("Window", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Reticle", qApp->getApplicationCompositor().getReticleInterface()); - _webSurface->getSurfaceContext()->setContextProperty("desktop", DependencyManager::get()->getDesktop()); _webSurface->getSurfaceContext()->setContextProperty("HiFiAbout", AboutUtil::getInstance()); // Override min fps for tablet UI, for silky smooth scrolling From 720b0a896323a309e71486d1bc0193e337aad3b3 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Fri, 25 May 2018 09:51:16 -0700 Subject: [PATCH 09/78] Adding fix for enabling saved front/back advanced controls. --- ...oggleAdvancedMovementForHandControllers.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index a1b96ac607..78b074573f 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -171,4 +171,24 @@ Messages.subscribe(HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL); Messages.messageReceived.connect(handleMessage); + function initializeControls() { + if(HMD.active) { + if (Controller.Hardware.Vive !== undefined || Controller.Hardware.OculusTouch !== undefined) { + if (MyAvatar.useAdvancedMovementControls) { + Controller.disableMapping(DRIVING_MAPPING_NAME); + } else { + Controller.enableMapping(DRIVING_MAPPING_NAME); + } + + if (MyAvatar.getFlyingEnabled()) { + Controller.disableMapping(FLYING_MAPPING_NAME); + } else { + Controller.enableMapping(FLYING_MAPPING_NAME); + } + }); + + } + } + + initializeControls(); }()); // END LOCAL_SCOPE From 68b18def2252f15f0e72fed81e2feb8e919ca6ac Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Fri, 25 May 2018 15:34:17 -0700 Subject: [PATCH 10/78] fix copy delete cut paste commands and menus --- interface/src/Menu.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f55c389a1f..42618796a5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -103,16 +103,32 @@ Menu::Menu() { editMenu->addSeparator(); // Edit > Cut - addActionToQMenuAndActionHash(editMenu, "Cut", Qt::CTRL | Qt::Key_X); + auto cutAction = addActionToQMenuAndActionHash(editMenu, "Cut", QKeySequence::Cut); + connect(cutAction, &QAction::triggered, [] { + QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier); + QCoreApplication::postEvent(QCoreApplication::instance(), keyEvent); + }); // Edit > Copy - addActionToQMenuAndActionHash(editMenu, "Copy", Qt::CTRL | Qt::Key_C); + auto copyAction = addActionToQMenuAndActionHash(editMenu, "Copy", QKeySequence::Copy); + connect(copyAction, &QAction::triggered, [] { + QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier); + QCoreApplication::postEvent(QCoreApplication::instance(), keyEvent); + }); // Edit > Paste - addActionToQMenuAndActionHash(editMenu, "Paste", Qt::CTRL | Qt::Key_V); + auto pasteAction = addActionToQMenuAndActionHash(editMenu, "Paste", QKeySequence::Paste); + connect(pasteAction, &QAction::triggered, [] { + QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier); + QCoreApplication::postEvent(QCoreApplication::instance(), keyEvent); + }); // Edit > Delete - addActionToQMenuAndActionHash(editMenu, "Delete", Qt::Key_Delete); + auto deleteAction =addActionToQMenuAndActionHash(editMenu, "Delete", QKeySequence::Delete); + connect(deleteAction, &QAction::triggered, [] { + QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::ControlModifier); + QCoreApplication::postEvent(QCoreApplication::instance(), keyEvent); + }); editMenu->addSeparator(); From 3942f397d006fb3aefc72f7e4af1c660301c1ba7 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 29 May 2018 17:38:02 -0700 Subject: [PATCH 11/78] adding back reload scripts --- interface/src/AboutUtil.cpp | 16 ++++++++-------- interface/src/AboutUtil.h | 19 ++++++++----------- interface/src/Application.cpp | 8 +++++++- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/interface/src/AboutUtil.cpp b/interface/src/AboutUtil.cpp index 5179897443..43b4ac1ff4 100644 --- a/interface/src/AboutUtil.cpp +++ b/interface/src/AboutUtil.cpp @@ -20,9 +20,9 @@ #include AboutUtil::AboutUtil(QObject *parent) : QObject(parent) { - QLocale locale_; - m_DateConverted = QDate::fromString(BuildInfo::BUILD_TIME, "dd/MM/yyyy"). - toString(locale_.dateFormat(QLocale::ShortFormat)); + QLocale locale; + _dateConverted = QDate::fromString(BuildInfo::BUILD_TIME, "dd/MM/yyyy"). + toString(locale.dateFormat(QLocale::ShortFormat)); } AboutUtil *AboutUtil::getInstance() @@ -31,17 +31,17 @@ AboutUtil *AboutUtil::getInstance() return &instance; } -QString AboutUtil::buildDate() const +QString AboutUtil::getBuildDate() const { - return m_DateConverted; + return _dateConverted; } -QString AboutUtil::buildVersion() const +QString AboutUtil::getBuildVersion() const { return BuildInfo::VERSION; } -QString AboutUtil::qtVersion() const +QString AboutUtil::getQtVersion() const { return qVersion(); } @@ -49,7 +49,7 @@ QString AboutUtil::qtVersion() const void AboutUtil::openUrl(const QString& url) const { auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"); auto hmd = DependencyManager::get(); auto offscreenUi = DependencyManager::get(); diff --git a/interface/src/AboutUtil.h b/interface/src/AboutUtil.h index 9b65b887b9..06c04cc9df 100644 --- a/interface/src/AboutUtil.h +++ b/interface/src/AboutUtil.h @@ -16,27 +16,24 @@ #include class AboutUtil : public QObject { - Q_OBJECT - Q_PROPERTY(QString buildDate READ buildDate CONSTANT) - Q_PROPERTY(QString buildVersion READ buildVersion CONSTANT) - Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT) - - AboutUtil(QObject* parent = nullptr); + Q_PROPERTY(QString buildDate READ getBuildDate CONSTANT) + Q_PROPERTY(QString buildVersion READ getBuildVersion CONSTANT) + Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT) public: static AboutUtil* getInstance(); ~AboutUtil() {} - QString buildDate() const; - QString buildVersion() const; - QString qtVersion() const; + QString getBuildDate() const; + QString getBuildVersion() const; + QString getQtVersion() const; public slots: void openUrl(const QString &url) const; private: - - QString m_DateConverted; + AboutUtil(QObject* parent = nullptr); + QString _dateConverted; }; #endif // hifi_AboutUtil_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6a102f418b..626ece9fe5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3642,7 +3642,6 @@ void Application::keyPressEvent(QKeyEvent* event) { _keysPressed.insert(event->key()); _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts - // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isKeyCaptured(event)) { return; @@ -3727,6 +3726,13 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; + case Qt::Key_R: + if (isMeta && !event->isAutoRepeat()) { + DependencyManager::get()->reloadAllScripts(); + DependencyManager::get()->clearCache(); + } + break; + case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; From 646b852aefa6d3c0643de54a4f6cab9d964b9c2d Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Tue, 29 May 2018 17:55:42 -0700 Subject: [PATCH 12/78] fix headers --- interface/src/AboutUtil.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/interface/src/AboutUtil.cpp b/interface/src/AboutUtil.cpp index 43b4ac1ff4..634e52b481 100644 --- a/interface/src/AboutUtil.cpp +++ b/interface/src/AboutUtil.cpp @@ -8,16 +8,18 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include "AboutUtil.h" #include #include -#include "AboutUtil.h" -#include "BuildInfo.h" #include +#include + +#include "BuildInfo.h" #include "DependencyManager.h" #include "scripting/HMDScriptingInterface.h" #include "Application.h" -#include AboutUtil::AboutUtil(QObject *parent) : QObject(parent) { QLocale locale; @@ -25,24 +27,20 @@ AboutUtil::AboutUtil(QObject *parent) : QObject(parent) { toString(locale.dateFormat(QLocale::ShortFormat)); } -AboutUtil *AboutUtil::getInstance() -{ +AboutUtil *AboutUtil::getInstance() { static AboutUtil instance; return &instance; } -QString AboutUtil::getBuildDate() const -{ +QString AboutUtil::getBuildDate() const { return _dateConverted; } -QString AboutUtil::getBuildVersion() const -{ +QString AboutUtil::getBuildVersion() const { return BuildInfo::VERSION; } -QString AboutUtil::getQtVersion() const -{ +QString AboutUtil::getQtVersion() const { return qVersion(); } From 19308c3a58bcbf8e3d6680fb6ad74463008ff318 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 30 May 2018 11:22:17 -0700 Subject: [PATCH 13/78] fixing typo in script file. --- .../toggleAdvancedMovementForHandControllers.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 78b074573f..92d079cff6 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -185,10 +185,9 @@ } else { Controller.enableMapping(FLYING_MAPPING_NAME); } - }); + } + } + } - } - } - - initializeControls(); + initializeControls(); }()); // END LOCAL_SCOPE From 6fa58fce1f22c1bd3efa838feff1566d41d00f2b Mon Sep 17 00:00:00 2001 From: Gabriel Date: Tue, 29 May 2018 16:42:09 -0300 Subject: [PATCH 14/78] Update Application.cpp Remove the mic bar for android --- interface/src/Application.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6a102f418b..b4372cf8c2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3011,9 +3011,11 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) { auto surfaceContext = DependencyManager::get()->getSurfaceContext(); surfaceContext->setContextProperty("Stats", Stats::getInstance()); +#if !defined(Q_OS_ANDROID) auto offscreenUi = DependencyManager::get(); auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml"); offscreenUi->show(qml, "AvatarInputsBar"); +#endif } void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { From 3f696df3a50fc26e2f3c6ef6a4ca57d42d362d8f Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Tue, 29 May 2018 17:21:44 -0300 Subject: [PATCH 15/78] Show stats button only in debug mode. Add isDebugMode to Script public interface --- libraries/script-engine/src/ScriptEngine.cpp | 8 ++++++++ libraries/script-engine/src/ScriptEngine.h | 6 ++++++ scripts/+android/defaultScripts.js | 17 +++++++++++++++-- scripts/system/+android/stats.js | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 23ffbabe77..72860acbc5 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -237,6 +237,14 @@ QString ScriptEngine::getContext() const { return "unknown"; } +bool ScriptEngine::isDebugMode() const { +#if defined(DEBUG) + return true; +#else + return false; +#endif +} + ScriptEngine::~ScriptEngine() { auto scriptEngines = DependencyManager::get(); if (scriptEngines) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 3001666b5d..c02a63ef3c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -232,6 +232,12 @@ public: */ Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; } + /**jsdoc + * @function Script.isDebugMode + * @returns {boolean} + */ + Q_INVOKABLE bool isDebugMode() const; + /**jsdoc * @function Script.isEntityClientScript * @returns {boolean} diff --git a/scripts/+android/defaultScripts.js b/scripts/+android/defaultScripts.js index 98fbb4b1a7..8950af808d 100644 --- a/scripts/+android/defaultScripts.js +++ b/scripts/+android/defaultScripts.js @@ -16,8 +16,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/+android/touchscreenvirtualpad.js", "system/+android/actionbar.js", "system/+android/audio.js" , - "system/+android/modes.js", - "system/+android/stats.js"/*, + "system/+android/modes.js"/*, "system/away.js", "system/controllers/controllerDisplayManager.js", "system/controllers/handControllerGrabAndroid.js", @@ -33,6 +32,10 @@ var DEFAULT_SCRIPTS_COMBINED = [ "developer/debugging/debugAndroidMouse.js"*/ ]; +var DEBUG_SCRIPTS = [ + "system/+android/stats.js" +]; + var DEFAULT_SCRIPTS_SEPARATE = [ ]; // add a menu item for debugging @@ -70,6 +73,11 @@ function runDefaultsTogether() { for (var i in DEFAULT_SCRIPTS_COMBINED) { Script.include(DEFAULT_SCRIPTS_COMBINED[i]); } + if (Script.isDebugMode()) { + for (var i in DEBUG_SCRIPTS) { + Script.include(DEBUG_SCRIPTS[i]); + } + } loadSeparateDefaults(); } @@ -77,6 +85,11 @@ function runDefaultsSeparately() { for (var i in DEFAULT_SCRIPTS_COMBINED) { Script.load(DEFAULT_SCRIPTS_COMBINED[i]); } + if (Script.isDebugMode()) { + for (var i in DEBUG_SCRIPTS) { + Script.load(DEBUG_SCRIPTS[i]); + } + } loadSeparateDefaults(); } diff --git a/scripts/system/+android/stats.js b/scripts/system/+android/stats.js index a93bcb5794..0731684291 100644 --- a/scripts/system/+android/stats.js +++ b/scripts/system/+android/stats.js @@ -30,7 +30,7 @@ function init() { text: "STATS" }); statsButton.clicked.connect(function() { - Menu.triggerOption("Stats"); + Menu.triggerOption("Show Statistics"); }); } From 785c0b634d1e98117815e280ab45260177ff32bd Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Fri, 25 May 2018 17:42:41 -0300 Subject: [PATCH 16/78] Change gradle configuration to sign release apk. Remove Daydream intent from AndroidManifest --- android/app/build.gradle | 9 +++++++++ android/app/src/main/AndroidManifest.xml | 6 ------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 70f7c622a0..5f92417ba4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -27,6 +27,14 @@ android { '-DDISABLE_KTX_CACHE=OFF' } } + signingConfigs { + release { + storeFile file(HIFI_ANDROID_KEYSTORE) + storePassword HIFI_ANDROID_KEYSTORE_PASSWORD + keyAlias HIFI_ANDROID_KEY_ALIAS + keyPassword HIFI_ANDROID_KEY_PASSWORD + } + } } compileOptions { @@ -38,6 +46,7 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e105f5bccf..0b52046057 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -49,12 +49,6 @@ android:label="@string/app_name" android:launchMode="singleTop" > - - - - - - From a0274f8b4bac07ae45f70efcbc670202c0f14078 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Wed, 30 May 2018 12:43:44 -0300 Subject: [PATCH 17/78] Make APK release signature optional --- android/app/build.gradle | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 5f92417ba4..46de9642d9 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -29,10 +29,10 @@ android { } signingConfigs { release { - storeFile file(HIFI_ANDROID_KEYSTORE) - storePassword HIFI_ANDROID_KEYSTORE_PASSWORD - keyAlias HIFI_ANDROID_KEY_ALIAS - keyPassword HIFI_ANDROID_KEY_PASSWORD + storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null + storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : '' + keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : '' + keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : '' } } } @@ -46,7 +46,10 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.release + signingConfig project.hasProperty("HIFI_ANDROID_KEYSTORE") && + project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") && + project.hasProperty("HIFI_ANDROID_KEY_ALIAS") && + project.hasProperty("HIFI_ANDROID_KEY_PASSWORD")? signingConfigs.release : null } } From 8cdbf460db8590487ece33be01a93888d55842be Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 30 May 2018 13:36:02 -0700 Subject: [PATCH 18/78] adding back notifcations --- scripts/defaultScripts.js | 1 + scripts/system/notifications.js | 49 --------------------------------- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index b0cbf0e246..59a51830be 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -23,6 +23,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/makeUserConnection.js", "system/tablet-goto.js", "system/marketplaces/marketplaces.js", + "system/notifications.js", "system/commerce/wallet.js", "system/edit.js", "system/dialTone.js", diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index a28f343ad3..0778e2a44b 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -79,11 +79,7 @@ var frame = 0; var ctrlIsPressed = false; var ready = true; - var MENU_NAME = 'Tools > Notifications'; - var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds"; var NOTIFICATION_MENU_ITEM_POST = " Notifications"; - var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds"; - var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_"; var NOTIFICATIONS_MESSAGE_CHANNEL = "Hifi-Notifications"; var NotificationType = { @@ -401,11 +397,6 @@ alpha: backgroundAlpha }; - if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) && - Menu.isOptionChecked(NotificationType.getMenuString(notificationType))) { - randomSounds.playRandom(); - } - return notify(noticeProperties, buttonProperties, height, imageProperties); } @@ -618,30 +609,6 @@ } } - function setup() { - var type; - Menu.addMenu(MENU_NAME); - var checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING); - checked = checked === '' ? true : checked; - Menu.addMenuItem({ - menuName: MENU_NAME, - menuItemName: PLAY_NOTIFICATION_SOUNDS_MENU_ITEM, - isCheckable: true, - isChecked: Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING) - }); - Menu.addSeparator(MENU_NAME, "Play sounds for:"); - for (type in NotificationType.properties) { - checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + (parseInt(type, 10) + 1)); - checked = checked === '' ? true : checked; - Menu.addMenuItem({ - menuName: MENU_NAME, - menuItemName: NotificationType.properties[type].text + NOTIFICATION_MENU_ITEM_POST, - isCheckable: true, - isChecked: checked - }); - } - } - // When our script shuts down, we should clean up all of our overlays function scriptEnding() { var notificationIndex; @@ -649,27 +616,14 @@ Overlays.deleteOverlay(notifications[notificationIndex]); Overlays.deleteOverlay(buttons[notificationIndex]); } - Menu.removeMenu(MENU_NAME); Messages.unsubscribe(NOTIFICATIONS_MESSAGE_CHANNEL); } - function menuItemEvent(menuItem) { - if (menuItem === PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) { - Settings.setValue(PLAY_NOTIFICATION_SOUNDS_SETTING, Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM)); - return; - } - var notificationType = NotificationType.getTypeFromMenuItem(menuItem); - if (notificationType !== notificationType.UNKNOWN) { - Settings.setValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + notificationType, Menu.isOptionChecked(menuItem)); - } - } - Controller.keyPressEvent.connect(keyPressEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); - Menu.menuItemEvent.connect(menuItemEvent); Window.domainConnectionRefused.connect(onDomainConnectionRefused); Window.stillSnapshotTaken.connect(onSnapshotTaken); Window.snapshot360Taken.connect(onSnapshotTaken); @@ -684,7 +638,4 @@ Messages.subscribe(NOTIFICATIONS_MESSAGE_CHANNEL); Messages.messageReceived.connect(onMessageReceived); - - setup(); - }()); // END LOCAL_SCOPE From 03bad0265c3a4259eddc4d97a160440c693b887e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 31 May 2018 11:09:51 +1200 Subject: [PATCH 19/78] Fix properties not populating particle explorer tab --- scripts/system/edit.js | 11 +++----- .../particle_explorer/particleExplorerTool.js | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index f549c7dd85..9285023ed8 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2366,22 +2366,19 @@ var selectedParticleEntity = 0; var selectedParticleEntityID = null; function selectParticleEntity(entityID) { - var properties = Entities.getEntityProperties(entityID); selectedParticleEntityID = entityID; + + var properties = Entities.getEntityProperties(entityID); if (properties.emitOrientation) { properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); } - var particleData = { - messageType: "particle_settings", - currentProperties: properties - }; + particleExplorerTool.destroyWebView(); particleExplorerTool.createWebView(); selectedParticleEntity = entityID; particleExplorerTool.setActiveParticleEntity(entityID); - - particleExplorerTool.webView.emitScriptEvent(JSON.stringify(particleData)); + particleExplorerTool.setActiveParticleProperties(properties); // Switch to particle explorer var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js index d85fc169b1..016691e2b6 100644 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ b/scripts/system/particle_explorer/particleExplorerTool.js @@ -16,37 +16,55 @@ var PARTICLE_EXPLORER_HTML_URL = Script.resolvePath('particleExplorer.html'); ParticleExplorerTool = function() { var that = {}; + that.activeParticleEntity = 0; + that.activeParticleProperties = {}; + that.createWebView = function() { that.webView = Tablet.getTablet("com.highfidelity.interface.tablet.system"); that.webView.setVisible = function(value) {}; that.webView.webEventReceived.connect(that.webEventReceived); - } + }; that.destroyWebView = function() { if (!that.webView) { return; } that.activeParticleEntity = 0; + that.activeParticleProperties = {}; var messageData = { messageType: "particle_close" }; that.webView.emitScriptEvent(JSON.stringify(messageData)); + }; + + function sendActiveParticleProperies() { + that.webView.emitScriptEvent(JSON.stringify({ + messageType: "particle_settings", + currentProperties: that.activeParticleProperties + })); } that.webEventReceived = function(data) { - var data = JSON.parse(data); + data = JSON.parse(data); if (data.messageType === "settings_update") { if (data.updatedSettings.emitOrientation) { data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation); } Entities.editEntity(that.activeParticleEntity, data.updatedSettings); + } else if (data.messageType === "page_loaded") { + sendActiveParticleProperies(); } - } + }; that.setActiveParticleEntity = function(id) { that.activeParticleEntity = id; - } + }; + + that.setActiveParticleProperties = function(properties) { + that.activeParticleProperties = properties; + sendActiveParticleProperies(); + }; return that; }; From 4efdba738283a58b7619bb001a18029c4fb4cb38 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 31 May 2018 11:10:54 +1200 Subject: [PATCH 20/78] Remove redundant variable --- scripts/system/edit.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 9285023ed8..05f5e3cb19 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2362,7 +2362,6 @@ var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); var particleExplorerTool = new ParticleExplorerTool(); -var selectedParticleEntity = 0; var selectedParticleEntityID = null; function selectParticleEntity(entityID) { @@ -2376,7 +2375,6 @@ function selectParticleEntity(entityID) { particleExplorerTool.destroyWebView(); particleExplorerTool.createWebView(); - selectedParticleEntity = entityID; particleExplorerTool.setActiveParticleEntity(entityID); particleExplorerTool.setActiveParticleProperties(properties); @@ -2401,13 +2399,13 @@ entityListTool.webView.webEventReceived.connect(function (data) { var ids = data.entityIds; if (ids.length === 1) { if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect") { - if (JSON.stringify(selectedParticleEntity) === JSON.stringify(ids[0])) { + if (JSON.stringify(selectedParticleEntityID) === JSON.stringify(ids[0])) { // This particle entity is already selected, so return return; } // Destroy the old particles web view first } else { - selectedParticleEntity = 0; + selectedParticleEntityID = 0; particleExplorerTool.destroyWebView(); } } From f99b2dd23fd1e0bc81b0d3afa1494f323ba4dac8 Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Thu, 31 May 2018 16:02:41 -0300 Subject: [PATCH 21/78] Android - Fix joystick and view control bug that makes it bounce --- .../src/input-plugins/TouchscreenVirtualPadDevice.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index 8d63b82911..32194e1b84 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -350,6 +350,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxMoveStartingPointCandidate != -1) { _moveCurrentTouchId = tPoints[idxMoveStartingPointCandidate].id(); _unusedTouches.erase(_moveCurrentTouchId); + thisPoint.x = tPoints[idxMoveStartingPointCandidate].pos().x(); + thisPoint.y = tPoints[idxMoveStartingPointCandidate].pos().y(); moveTouchBegin(thisPoint); } else { moveTouchEnd(); @@ -359,6 +361,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxViewStartingPointCandidate != -1) { _viewCurrentTouchId = tPoints[idxViewStartingPointCandidate].id(); _unusedTouches.erase(_viewCurrentTouchId); + thisPoint.x = tPoints[idxViewStartingPointCandidate].pos().x(); + thisPoint.y = tPoints[idxViewStartingPointCandidate].pos().y(); viewTouchBegin(thisPoint); } else { viewTouchEnd(); @@ -368,6 +372,8 @@ void TouchscreenVirtualPadDevice::touchUpdateEvent(const QTouchEvent* event) { if (idxJumpStartingPointCandidate != -1) { _jumpCurrentTouchId = tPoints[idxJumpStartingPointCandidate].id(); _unusedTouches.erase(_jumpCurrentTouchId); + thisPoint.x = tPoints[idxJumpStartingPointCandidate].pos().x(); + thisPoint.y = tPoints[idxJumpStartingPointCandidate].pos().y(); jumpTouchBegin(thisPoint); } else { if (_jumpHasValidTouch) { @@ -424,6 +430,7 @@ void TouchscreenVirtualPadDevice::moveTouchBegin(glm::vec2 touchPoint) { } else { _moveRefTouchPoint = touchPoint; } + _moveCurrentTouchPoint = touchPoint; _moveHasValidTouch = true; } } From 146f871bec463dcb62f621c741c4eabe36888167 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 1 Jun 2018 09:39:39 +1200 Subject: [PATCH 22/78] Don't modify function parameter --- scripts/system/particle_explorer/particleExplorerTool.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/particle_explorer/particleExplorerTool.js b/scripts/system/particle_explorer/particleExplorerTool.js index 016691e2b6..0543cfc56b 100644 --- a/scripts/system/particle_explorer/particleExplorerTool.js +++ b/scripts/system/particle_explorer/particleExplorerTool.js @@ -45,8 +45,8 @@ ParticleExplorerTool = function() { })); } - that.webEventReceived = function(data) { - data = JSON.parse(data); + that.webEventReceived = function(message) { + var data = JSON.parse(message); if (data.messageType === "settings_update") { if (data.updatedSettings.emitOrientation) { data.updatedSettings.emitOrientation = Quat.fromVec3Degrees(data.updatedSettings.emitOrientation); From 1a128abdfd098625914db983b817d28186425c08 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 31 May 2018 15:05:33 -0700 Subject: [PATCH 23/78] adding alternate solution. --- ...oggleAdvancedMovementForHandControllers.js | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js index 92d079cff6..92f72f8724 100644 --- a/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js +++ b/scripts/system/controllers/toggleAdvancedMovementForHandControllers.js @@ -28,7 +28,7 @@ var isDisabled = false; var previousFlyingState = MyAvatar.getFlyingEnabled(); - var previousDrivingState = MyAvatar.useAdvancedMovementControls; + var previousDrivingState = false; function rotate180() { var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.angleAxis(180, { @@ -100,7 +100,7 @@ Controller.enableMapping(DRIVING_MAPPING_NAME); } - if (MyAvatar.getFyingEnabled()) { + if (MyAvatar.getFlyingEnabled()) { Controller.disableMapping(FLYING_MAPPING_NAME); } else { Controller.enableMapping(FLYING_MAPPING_NAME); @@ -171,23 +171,4 @@ Messages.subscribe(HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL); Messages.messageReceived.connect(handleMessage); - function initializeControls() { - if(HMD.active) { - if (Controller.Hardware.Vive !== undefined || Controller.Hardware.OculusTouch !== undefined) { - if (MyAvatar.useAdvancedMovementControls) { - Controller.disableMapping(DRIVING_MAPPING_NAME); - } else { - Controller.enableMapping(DRIVING_MAPPING_NAME); - } - - if (MyAvatar.getFlyingEnabled()) { - Controller.disableMapping(FLYING_MAPPING_NAME); - } else { - Controller.enableMapping(FLYING_MAPPING_NAME); - } - } - } - } - - initializeControls(); }()); // END LOCAL_SCOPE From 3bda5bf6a0609f23067731cae51210a9e05631e7 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 31 May 2018 17:58:37 -0700 Subject: [PATCH 24/78] added MyAvatar.hasProceduralBlinkFaceMovement and MyAvatar.hasProceduralEyeFaceMovement properties to MyAvatar.h and cpp and HeadData.h --- interface/src/avatar/MyAvatar.cpp | 12 + interface/src/avatar/MyAvatar.h | 12 +- .../src/avatars-renderer/Head.cpp | 62 +-- libraries/avatars/src/AvatarData.h | 2 + libraries/avatars/src/HeadData.h | 23 ++ .../DefaultStylizedFemale_Clothed.fst | 139 +++++++ scripts/developer/facialExpressions.js | 374 ++++++++++++++++++ 7 files changed, 596 insertions(+), 28 deletions(-) create mode 100644 scripts/developer/DefaultStylizedFemale_Clothed.fst create mode 100644 scripts/developer/facialExpressions.js diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e231a42f92..0f6fe88ce9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2094,6 +2094,18 @@ void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { _hasScriptedBlendShapes = hasScriptedBlendshapes; } +void MyAvatar::setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement) { + _headData->setHasProceduralBlinkFaceMovement(hasProceduralBlinkFaceMovement); +} + +void MyAvatar::setHasProceduralEyeFaceMovement(bool hasProceduralEyeFaceMovement) { + _headData->setHasProceduralEyeFaceMovement(hasProceduralEyeFaceMovement); +} + +void MyAvatar::setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement) { + _headData->setHasAudioEnabledFaceMovement(hasAudioEnabledFaceMovement); +} + void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 55780b56ac..813dddcc98 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -87,6 +87,8 @@ class MyAvatar : public Avatar { * @property {number} audioListenerModeCustom=2 - The audio listening position is at a the position specified by set by the * customListenPosition and customListenOrientation property values. Read-only. * @property {boolean} hasScriptedBlendshapes=false - Blendshapes will be transmitted over the network if set to true. + * @property {boolean} hasProceduralBlinkFaceMovement=true - procedural blinking will be turned on if set to true. + * @property {boolean} hasProceduralEyeFaceMovement=true - procedural eye movement will be turned on if set to true. * @property {boolean} hasAudioEnabledFaceMovement=true - If set to true, voice audio will move the mouth Blendshapes while MyAvatar.hasScriptedBlendshapes is enabled. * @property {Vec3} customListenPosition=Vec3.ZERO - The listening position used when the audioListenerMode * property value is audioListenerModeCustom. @@ -187,6 +189,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(AudioListenerMode audioListenerModeCamera READ getAudioListenerModeCamera) Q_PROPERTY(AudioListenerMode audioListenerModeCustom READ getAudioListenerModeCustom) Q_PROPERTY(bool hasScriptedBlendshapes READ getHasScriptedBlendshapes WRITE setHasScriptedBlendshapes) + Q_PROPERTY(bool hasProceduralBlinkFaceMovement READ getHasProceduralBlinkFaceMovement WRITE setHasProceduralBlinkFaceMovement) + Q_PROPERTY(bool hasProceduralEyeFaceMovement READ getHasProceduralEyeFaceMovement WRITE setHasProceduralEyeFaceMovement) Q_PROPERTY(bool hasAudioEnabledFaceMovement READ getHasAudioEnabledFaceMovement WRITE setHasAudioEnabledFaceMovement) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) @@ -1347,8 +1351,12 @@ private: bool getShouldRenderLocally() const { return _shouldRender; } void setHasScriptedBlendshapes(bool hasScriptedBlendshapes); bool getHasScriptedBlendshapes() const override { return _hasScriptedBlendShapes; } - void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement) { _hasAudioEnabledFaceMovement = hasAudioEnabledFaceMovement; } - bool getHasAudioEnabledFaceMovement() const override { return _hasAudioEnabledFaceMovement; } + void setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement); + bool getHasProceduralBlinkFaceMovement() const override { return _headData->getHasProceduralBlinkFaceMovement(); } + void setHasProceduralEyeFaceMovement(bool hasProceduralEyeFaceMovement); + bool getHasProceduralEyeFaceMovement() const override { return _headData->getHasProceduralEyeFaceMovement(); } + void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement); + bool getHasAudioEnabledFaceMovement() const override { return _headData->getHasAudioEnabledFaceMovement(); } bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 256b3bf8a6..b0707922ea 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -100,41 +100,51 @@ void Head::simulate(float deltaTime) { } _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); + const float BLINK_SPEED = 10.0f; const float BLINK_SPEED_VARIABILITY = 1.0f; const float BLINK_START_VARIABILITY = 0.25f; const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; - if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { - // no blinking when brows are raised; blink less with increasing loudness - const float BASE_BLINK_RATE = 15.0f / 60.0f; - const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; - if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * + if (getHasProceduralBlinkFaceMovement()) { + if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { + // no blinking when brows are raised; blink less with increasing loudness + const float BASE_BLINK_RATE = 15.0f / 60.0f; + const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; + if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { - _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - if (randFloat() < 0.5f) { - _leftEyeBlink = BLINK_START_VARIABILITY; - } else { - _rightEyeBlink = BLINK_START_VARIABILITY; + _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + if (randFloat() < 0.5f) { + _leftEyeBlink = BLINK_START_VARIABILITY; + } + else { + _rightEyeBlink = BLINK_START_VARIABILITY; + } + } + } + else { + _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); + _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); + + if (_leftEyeBlink == FULLY_CLOSED) { + _leftEyeBlinkVelocity = -BLINK_SPEED; + + } + else if (_leftEyeBlink == FULLY_OPEN) { + _leftEyeBlinkVelocity = 0.0f; + } + if (_rightEyeBlink == FULLY_CLOSED) { + _rightEyeBlinkVelocity = -BLINK_SPEED; + + } + else if (_rightEyeBlink == FULLY_OPEN) { + _rightEyeBlinkVelocity = 0.0f; } } } else { - _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - - if (_leftEyeBlink == FULLY_CLOSED) { - _leftEyeBlinkVelocity = -BLINK_SPEED; - - } else if (_leftEyeBlink == FULLY_OPEN) { - _leftEyeBlinkVelocity = 0.0f; - } - if (_rightEyeBlink == FULLY_CLOSED) { - _rightEyeBlinkVelocity = -BLINK_SPEED; - - } else if (_rightEyeBlink == FULLY_OPEN) { - _rightEyeBlinkVelocity = 0.0f; - } + _rightEyeBlink = FULLY_OPEN; + _leftEyeBlink = FULLY_OPEN; } // use data to update fake Faceshift blendshape coefficients diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 568c48dd62..2d8ee52ea1 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -502,6 +502,8 @@ public: float getDomainLimitedScale() const; virtual bool getHasScriptedBlendshapes() const { return false; } + virtual bool getHasProceduralBlinkFaceMovement() const { return true; } + virtual bool getHasProceduralEyeFaceMovement() const { return true; } virtual bool getHasAudioEnabledFaceMovement() const { return false; } /**jsdoc diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index bcc2cacde5..f8eca0915e 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -69,6 +69,24 @@ public: } bool lookAtPositionChangedSince(quint64 time) { return _lookAtPositionChanged >= time; } + bool getHasProceduralEyeFaceMovement() const { return _hasProceduralEyeFaceMovement; } + + void setHasProceduralEyeFaceMovement(const bool hasProceduralEyeFaceMovement) { + _hasProceduralEyeFaceMovement = hasProceduralEyeFaceMovement; + } + + bool getHasProceduralBlinkFaceMovement() const { return _hasProceduralBlinkFaceMovement; } + + void setHasProceduralBlinkFaceMovement(const bool hasProceduralBlinkFaceMovement) { + _hasProceduralBlinkFaceMovement = hasProceduralBlinkFaceMovement; + } + + bool getHasAudioEnabledFaceMovement() const { return _hasAudioEnabledFaceMovement; } + + void setHasAudioEnabledFaceMovement(const bool hasAudioEnabledFaceMovement) { + _hasAudioEnabledFaceMovement = hasAudioEnabledFaceMovement; + } + friend class AvatarData; QJsonObject toJson() const; @@ -83,6 +101,11 @@ protected: glm::vec3 _lookAtPosition; quint64 _lookAtPositionChanged { 0 }; + //std::atomic _hasProceduralBlinkFaceMovement{ true }; + //std::atomic _hasProceduralEyeFaceMovement{ true }; + bool _hasAudioEnabledFaceMovement { true }; + bool _hasProceduralBlinkFaceMovement{ true }; + bool _hasProceduralEyeFaceMovement{ true }; bool _isFaceTrackerConnected { false }; bool _isEyeTrackerConnected { false }; float _leftEyeBlink { 0.0f }; diff --git a/scripts/developer/DefaultStylizedFemale_Clothed.fst b/scripts/developer/DefaultStylizedFemale_Clothed.fst new file mode 100644 index 0000000000..3e46d6a15c --- /dev/null +++ b/scripts/developer/DefaultStylizedFemale_Clothed.fst @@ -0,0 +1,139 @@ +name = DefaultStylizedFemale_Clothed +type = body+head +scale = 1 +filename = DefaultStylizedFemale_Clothed/DefaultStylizedFemale_Clothed.fbx +texdir = DefaultStylizedFemale_Clothed/textures +joint = jointLean = Spine +joint = jointRightHand = RightHand +joint = jointEyeLeft = LeftEye +joint = jointHead = HeadTop_End +joint = jointNeck = Neck +joint = jointRoot = Hips +joint = jointEyeRight = RightEye +joint = jointLeftHand = LeftHand +freeJoint = LeftArm +freeJoint = LeftForeArm +freeJoint = RightArm +freeJoint = RightForeArm +bs = EyeBlink_L = Blink_Left = 1 +bs = Sneer = Squint_Right = 0.5 +bs = Sneer = Squint_Left = 0.5 +bs = Sneer = NoseScrunch_Right = 0.75 +bs = Sneer = NoseScrunch_Left = 0.75 +bs = ChinLowerRaise = Jaw_Up = 1 +bs = EyeSquint_R = Squint_Right = 1 +bs = MouthSmile_R = Smile_Right = 1 +bs = ChinUpperRaise = UpperLipUp_Right = 0.5 +bs = ChinUpperRaise = UpperLipUp_Left = 0.5 +bs = LipsLowerOpen = LowerLipOut = 1 +bs = LipsLowerDown = LowerLipDown_Right = 0.7 +bs = LipsLowerDown = LowerLipDown_Left = 0.7 +bs = BrowsU_L = BrowsUp_Left = 1 +bs = MouthRight = Midmouth_Right = 1 +bs = MouthDimple_R = Smile_Right = 0.25 +bs = LipsPucker = MouthNarrow_Right = 1 +bs = LipsPucker = MouthNarrow_Left = 1 +bs = Puff = CheekPuff_Right = 1 +bs = Puff = CheekPuff_Left = 1 +bs = JawFwd = JawForeward = 1 +bs = BrowsD_L = BrowsDown_Left = 1 +bs = LipsFunnel = TongueUp = 1 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Right = 0.5 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Left = 0.5 +bs = LipsFunnel = MouthNarrow_Right = 1 +bs = LipsFunnel = MouthNarrow_Left = 1 +bs = LipsFunnel = Jaw_Down = 0.36 +bs = LipsFunnel = JawForeward = 0.39 +bs = LipsUpperOpen = UpperLipOut = 1 +bs = EyeSquint_L = Squint_Left = 1 +bs = MouthDimple_L = Smile_Left = 0.25 +bs = LipsLowerClose = LowerLipIn = 1 +bs = MouthFrown_R = Frown_Right = 1 +bs = MouthFrown_L = Frown_Left = 1 +bs = BrowsU_R = BrowsUp_Right = 1 +bs = JawOpen = MouthOpen = 0.7 +bs = JawRight = Jaw_Right = 1 +bs = MouthLeft = Midmouth_Left = 1 +bs = BrowsU_C = BrowsUp_Right = 1 +bs = BrowsU_C = BrowsUp_Left = 1 +bs = LipsUpperUp = UpperLipUp_Right = 0.7 +bs = LipsUpperUp = UpperLipUp_Left = 0.7 +bs = EyeBlink_R = Blink_Right = 1 +bs = EyeOpen_R = EyesWide_Right = 1 +bs = LipsUpperClose = UpperLipIn = 1 +bs = MouthSmile_L = Smile_Left = 1 +bs = EyeOpen_L = EyesWide_Left = 1 +bs = JawLeft = JawRotateY_Left = 0.5 +bs = BrowsD_R = BrowsDown_Right = 1 +jointIndex = RightHandThumb4 = 21 +jointIndex = Neck = 62 +jointIndex = LeftHandIndex4 = 57 +jointIndex = Body = 71 +jointIndex = LeftHandIndex1 = 54 +jointIndex = RightHand = 17 +jointIndex = RightHandMiddle1 = 26 +jointIndex = Spine = 11 +jointIndex = RightHandRing2 = 31 +jointIndex = RightArm = 15 +jointIndex = RightHandPinky2 = 35 +jointIndex = LeftToeBase = 9 +jointIndex = RightHandIndex3 = 24 +jointIndex = RightHandRing1 = 30 +jointIndex = RightHandPinky1 = 34 +jointIndex = RightEye = 66 +jointIndex = LeftHandRing4 = 49 +jointIndex = LeftHandRing2 = 47 +jointIndex = RightHandMiddle2 = 27 +jointIndex = Head = 63 +jointIndex = LeftHandMiddle4 = 53 +jointIndex = LeftLeg = 7 +jointIndex = LeftHandPinky2 = 43 +jointIndex = LeftHandThumb1 = 58 +jointIndex = LeftHandPinky4 = 45 +jointIndex = RightHandIndex1 = 22 +jointIndex = Tops = 67 +jointIndex = Hips = 0 +jointIndex = LeftUpLeg = 6 +jointIndex = RightShoulder = 14 +jointIndex = Spine2 = 13 +jointIndex = RightHandRing4 = 33 +jointIndex = RightHandThumb3 = 20 +jointIndex = RightHandIndex4 = 25 +jointIndex = LeftFoot = 8 +jointIndex = LeftHandRing3 = 48 +jointIndex = LeftHand = 41 +jointIndex = LeftForeArm = 40 +jointIndex = LeftToe_End = 10 +jointIndex = Bottoms = 68 +jointIndex = RightFoot = 3 +jointIndex = LeftHandMiddle2 = 51 +jointIndex = LeftHandThumb3 = 60 +jointIndex = RightHandPinky3 = 36 +jointIndex = LeftEye = 65 +jointIndex = LeftHandIndex2 = 55 +jointIndex = RightHandIndex2 = 23 +jointIndex = LeftHandPinky1 = 42 +jointIndex = LeftHandMiddle3 = 52 +jointIndex = RightHandMiddle4 = 29 +jointIndex = LeftHandThumb2 = 59 +jointIndex = Shoes = 69 +jointIndex = RightHandThumb1 = 18 +jointIndex = RightToe_End = 5 +jointIndex = RightHandThumb2 = 19 +jointIndex = RightUpLeg = 1 +jointIndex = RightLeg = 2 +jointIndex = LeftHandMiddle1 = 50 +jointIndex = LeftHandIndex3 = 56 +jointIndex = LeftHandThumb4 = 61 +jointIndex = RightHandRing3 = 32 +jointIndex = Hair = 70 +jointIndex = Spine1 = 12 +jointIndex = LeftHandRing1 = 46 +jointIndex = LeftArm = 39 +jointIndex = LeftShoulder = 38 +jointIndex = RightForeArm = 16 +jointIndex = HeadTop_End = 64 +jointIndex = RightHandPinky4 = 37 +jointIndex = LeftHandPinky3 = 44 +jointIndex = RightToeBase = 4 +jointIndex = RightHandMiddle3 = 28 diff --git a/scripts/developer/facialExpressions.js b/scripts/developer/facialExpressions.js new file mode 100644 index 0000000000..84e3966c4a --- /dev/null +++ b/scripts/developer/facialExpressions.js @@ -0,0 +1,374 @@ +// +// facialExpressions.js +// A script to set different emotions using blend shapes +// +// Author: Elisa Lupin-Jimenez +// Copyright High Fidelity 2018 +// +// Licensed under the Apache 2.0 License +// See accompanying license file or http://apache.org/ +// +// All assets are under CC Attribution Non-Commerical +// http://creativecommons.org/licenses/ +// + +(function() { + + var TABLET_BUTTON_NAME = "EMOTIONS"; + // TODO: ADD HTML LANDING PAGE + + var TRANSITION_TIME_SECONDS = 0.25; + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var icon = "https://hifi-content.s3.amazonaws.com/elisalj/emoji_scripts/icons/emoji-i.svg"; + var activeIcon = "https://hifi-content.s3.amazonaws.com/elisalj/emoji_scripts/icons/emoji-a.svg"; + var isActive = true; + + var controllerMappingName; + var controllerMapping; + + var tabletButton = tablet.addButton({ + icon: icon, + activeIcon: activeIcon, + text: TABLET_BUTTON_NAME, + isActive: true + }); + + var toggle = function() { + isActive = !isActive; + tabletButton.editProperties({isActive: isActive}); + if (isActive) { + Controller.enableMapping(controllerMappingName); + } else { + setEmotion(DEFAULT); + Controller.disableMapping(controllerMappingName); + } + }; + + tabletButton.clicked.connect(toggle); + + var DEFAULT = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.00, + "EyeBlink_R": 0.00, + "EyeSquint_L": 0.00, + "EyeSquint_R": 0.00, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var SMILE = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.30, + "EyeBlink_R": 0.30, + "EyeSquint_L": 0.90, + "EyeSquint_R": 0.90, + "BrowsD_L": 1.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 1.00, + "MouthSmile_R": 1.00, + "MouthDimple_L": 1.00, + "MouthDimple_R": 1.00, + "LipsUpperClose": 0.40, + "LipsLowerClose": 0.30, + "LipsLowerOpen": 0.25, + "ChinUpperRaise": 0.35, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var LAUGH = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.45, + "EyeBlink_R": 0.45, + "EyeSquint_L": 0.75, + "EyeSquint_R": 0.75, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.50, + "JawOpen": 0.50, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 1.00, + "MouthSmile_R": 1.00, + "MouthDimple_L": 1.00, + "MouthDimple_R": 1.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.30, + "Sneer": 1.00, + "Puff": 0.30 + }; + + var FLIRT = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.50, + "EyeBlink_R": 0.50, + "EyeSquint_L": 0.25, + "EyeSquint_R": 0.25, + "BrowsD_L": 0.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.55, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.00, + "MouthFrown_R": 0.00, + "MouthSmile_L": 0.50, + "MouthSmile_R": 0.00, + "MouthDimple_L": 1.00, + "MouthDimple_R": 1.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var SAD = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.30, + "EyeBlink_R": 0.30, + "EyeSquint_L": 0.30, + "EyeSquint_R": 0.30, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.50, + "JawOpen": 0.00, + "JawFwd": 0.80, + "MouthFrown_L": 0.80, + "MouthFrown_R": 0.80, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.50, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var ANGRY = { + "EyeOpen_L": 1.00, + "EyeOpen_R": 1.00, + "EyeBlink_L": 0.00, + "EyeBlink_R": 0.00, + "EyeSquint_L": 1.00, + "EyeSquint_R": 1.00, + "BrowsD_L": 1.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 0.50, + "MouthFrown_R": 0.50, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.50, + "LipsLowerClose": 0.50, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.50, + "Puff": 0.00 + }; + + var FEAR = { + "EyeOpen_L": 1.00, + "EyeOpen_R": 1.00, + "EyeBlink_L": 0.00, + "EyeBlink_R": 0.00, + "EyeSquint_L": 0.00, + "EyeSquint_R": 0.00, + "BrowsD_L": 0.00, + "BrowsD_R": 0.00, + "BrowsU_L": 0.00, + "BrowsU_C": 1.00, + "JawOpen": 0.15, + "JawFwd": 0.00, + "MouthFrown_L": 0.30, + "MouthFrown_R": 0.30, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.00, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.00, + "Sneer": 0.00, + "Puff": 0.00 + }; + + var DISGUST = { + "EyeOpen_L": 0.00, + "EyeOpen_R": 0.00, + "EyeBlink_L": 0.25, + "EyeBlink_R": 0.25, + "EyeSquint_L": 1.00, + "EyeSquint_R": 1.00, + "BrowsD_L": 1.00, + "BrowsD_R": 1.00, + "BrowsU_L": 0.00, + "BrowsU_C": 0.00, + "JawOpen": 0.00, + "JawFwd": 0.00, + "MouthFrown_L": 1.00, + "MouthFrown_R": 1.00, + "MouthSmile_L": 0.00, + "MouthSmile_R": 0.00, + "MouthDimple_L": 0.00, + "MouthDimple_R": 0.00, + "LipsUpperClose": 0.00, + "LipsLowerClose": 0.75, + "LipsLowerOpen": 0.00, + "ChinUpperRaise": 0.75, + "Sneer": 1.00, + "Puff": 0.00 + }; + + + function mixValue(valueA, valueB, percentage) { + return valueA + ((valueB - valueA) * percentage); + } + + var lastEmotionUsed = DEFAULT; + var emotion = DEFAULT; + var isChangingEmotion = false; + var changingEmotionPercentage = 0.0; + + Script.update.connect(function(deltaTime) { + if (!isChangingEmotion) { + return; + } + changingEmotionPercentage += deltaTime / TRANSITION_TIME_SECONDS; + if (changingEmotionPercentage >= 1.0) { + changingEmotionPercentage = 1.0; + isChangingEmotion = false; + if (emotion === DEFAULT) { + MyAvatar.hasScriptedBlendshapes = false; + } + } + for (var blendshape in emotion) { + MyAvatar.setBlendshape(blendshape, + mixValue(lastEmotionUsed[blendshape], emotion[blendshape], changingEmotionPercentage)); + } + }); + + function setEmotion(currentEmotion) { + if (emotion !== lastEmotionUsed) { + lastEmotionUsed = emotion; + } + if (currentEmotion !== lastEmotionUsed) { + changingEmotionPercentage = 0.0; + emotion = currentEmotion; + isChangingEmotion = true; + MyAvatar.hasScriptedBlendshapes = true; + } + } + + + controllerMappingName = 'Hifi-FacialExpressions-Mapping'; + controllerMapping = Controller.newMapping(controllerMappingName); + + controllerMapping.from(Controller.Hardware.Keyboard.H).to(function(value) { + if (value !== 0) { + setEmotion(SMILE); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.J).to(function(value) { + if (value !== 0) { + setEmotion(LAUGH); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.K).to(function(value) { + if (value !== 0) { + setEmotion(FLIRT); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.L).to(function(value) { + if (value !== 0) { + setEmotion(SAD); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.V).to(function(value) { + if (value !== 0) { + setEmotion(ANGRY); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.B).to(function(value) { + if (value !== 0) { + setEmotion(FEAR); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.M).to(function(value) { + if (value !== 0) { + setEmotion(DISGUST); + } + }); + + controllerMapping.from(Controller.Hardware.Keyboard.N).to(function(value) { + if (value !== 0) { + setEmotion(DEFAULT); + } + }); + + Controller.enableMapping(controllerMappingName); + + Script.scriptEnding.connect(function() { + tabletButton.clicked.disconnect(toggle); + tablet.removeButton(tabletButton); + Controller.disableMapping(controllerMappingName); + + if (emotion !== DEFAULT || isChangingEmotion) { + isChangingEmotion = false; + for (var blendshape in DEFAULT) { + MyAvatar.setBlendshape(blendshape, DEFAULT[blendshape]); + } + MyAvatar.hasScriptedBlendshapes = false; + } + }); + +}()); \ No newline at end of file From 01848fafe0b716872a8dd781c7c7d3f6faacc2ff Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 1 Jun 2018 11:37:11 -0700 Subject: [PATCH 25/78] made all changes except those to AvatarData toByteArray function also to do: verify that transientBlendshapes are set to 0 in headData simulate when there are blendshapes from script but no face tracker present --- interface/src/avatar/MyHead.cpp | 2 +- .../src/avatars-renderer/Head.cpp | 30 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 44d679f3f8..13d635bdd8 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -51,7 +51,7 @@ void MyHead::simulate(float deltaTime) { _isFaceTrackerConnected = hasActualFaceTrackerConnected || _owningAvatar->getHasScriptedBlendshapes(); if (_isFaceTrackerConnected) { if (hasActualFaceTrackerConnected) { - _transientBlendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); } else { _transientBlendshapeCoefficients.fill(0, _blendshapeCoefficients.size()); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index b0707922ea..06b52c8d44 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -148,18 +148,28 @@ void Head::simulate(float deltaTime) { } // use data to update fake Faceshift blendshape coefficients - calculateMouthShapes(deltaTime); + if (getHasAudioEnabledFaceMovement()) { + calculateMouthShapes(deltaTime); + } else { + _audioJawOpen = 0.0f; + _browAudioLift = 0.0f; + _mouth2 = 0.0f; + _mouth3 = 0.0f; + _mouth4 = 0.0f; + _mouthTime = 0.0f; + } FaceTracker::updateFakeCoefficients(_leftEyeBlink, - _rightEyeBlink, - _browAudioLift, - _audioJawOpen, - _mouth2, - _mouth3, - _mouth4, - _transientBlendshapeCoefficients); - - applyEyelidOffset(getOrientation()); + _rightEyeBlink, + _browAudioLift, + _audioJawOpen, + _mouth2, + _mouth3, + _mouth4, + _transientBlendshapeCoefficients); + if (getHasProceduralEyeFaceMovement()) { + applyEyelidOffset(getOrientation()); + } } else { _saccade = glm::vec3(); } From 89c4b1ecdd65747b15b993fe627bcad0e4b880dc Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Fri, 1 Jun 2018 15:00:00 -0300 Subject: [PATCH 26/78] Workaround for android buffer corruption issue --- .../hifiinterface/InterfaceActivity.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index 2165339918..e850ec639b 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -20,6 +20,9 @@ import android.os.Vibrator; import android.view.HapticFeedbackConstants; import android.view.WindowManager; import android.util.Log; + +import org.qtproject.qt5.android.QtLayout; +import org.qtproject.qt5.android.QtSurface; import org.qtproject.qt5.android.bindings.QtActivity; /*import com.google.vr.cardboard.DisplaySynchronizer; @@ -31,6 +34,9 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.view.View; +import android.widget.FrameLayout; + +import java.lang.reflect.Field; public class InterfaceActivity extends QtActivity { @@ -134,6 +140,7 @@ public class InterfaceActivity extends QtActivity { protected void onResume() { super.onResume(); nativeEnterForeground(); + surfacesWorkaround(); //gvrApi.resumeTracking(); } @@ -158,6 +165,31 @@ public class InterfaceActivity extends QtActivity { Log.w("[VR]", "Portrait detected but not in VR mode. Should not happen"); } } + surfacesWorkaround(); + } + + private void surfacesWorkaround() { + FrameLayout fl = findViewById(android.R.id.content); + QtLayout qtLayout = (QtLayout) fl.getChildAt(0); + QtSurface s1 = (QtSurface) qtLayout.getChildAt(0); + QtSurface s2 = (QtSurface) qtLayout.getChildAt(1); + Integer subLayer1 = 0; + Integer subLayer2 = 0; + try { + Field f = s1.getClass().getSuperclass().getDeclaredField("mSubLayer"); + f.setAccessible(true); + subLayer1 = (Integer) f.get(s1); + subLayer2 = (Integer) f.get(s2); + if (subLayer1 < subLayer2) { + s1.setVisibility(View.VISIBLE); + s2.setVisibility(View.INVISIBLE); + } else { + s1.setVisibility(View.INVISIBLE); + s2.setVisibility(View.VISIBLE); + } + } catch (ReflectiveOperationException e) { + Log.e(TAG, "Workaround failed"); + } } public void openUrlInAndroidWebView(String urlString) { From 61953d0070b60d8f0a71748ead97b2cccd0e0204 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Fri, 1 Jun 2018 15:48:57 -0300 Subject: [PATCH 27/78] Fix NullPointerException introduced with the workaround --- .../hifiinterface/InterfaceActivity.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index e850ec639b..da938ab85b 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -170,25 +170,29 @@ public class InterfaceActivity extends QtActivity { private void surfacesWorkaround() { FrameLayout fl = findViewById(android.R.id.content); - QtLayout qtLayout = (QtLayout) fl.getChildAt(0); - QtSurface s1 = (QtSurface) qtLayout.getChildAt(0); - QtSurface s2 = (QtSurface) qtLayout.getChildAt(1); - Integer subLayer1 = 0; - Integer subLayer2 = 0; - try { - Field f = s1.getClass().getSuperclass().getDeclaredField("mSubLayer"); - f.setAccessible(true); - subLayer1 = (Integer) f.get(s1); - subLayer2 = (Integer) f.get(s2); - if (subLayer1 < subLayer2) { - s1.setVisibility(View.VISIBLE); - s2.setVisibility(View.INVISIBLE); - } else { - s1.setVisibility(View.INVISIBLE); - s2.setVisibility(View.VISIBLE); + if (fl.getChildCount() > 0) { + QtLayout qtLayout = (QtLayout) fl.getChildAt(0); + if (qtLayout.getChildCount() > 1) { + QtSurface s1 = (QtSurface) qtLayout.getChildAt(0); + QtSurface s2 = (QtSurface) qtLayout.getChildAt(1); + Integer subLayer1 = 0; + Integer subLayer2 = 0; + try { + Field f = s1.getClass().getSuperclass().getDeclaredField("mSubLayer"); + f.setAccessible(true); + subLayer1 = (Integer) f.get(s1); + subLayer2 = (Integer) f.get(s2); + if (subLayer1 < subLayer2) { + s1.setVisibility(View.VISIBLE); + s2.setVisibility(View.INVISIBLE); + } else { + s1.setVisibility(View.INVISIBLE); + s2.setVisibility(View.VISIBLE); + } + } catch (ReflectiveOperationException e) { + Log.e(TAG, "Workaround failed"); + } } - } catch (ReflectiveOperationException e) { - Log.e(TAG, "Workaround failed"); } } From 44027f74fc4558b4d67f546af70d3f9611590f3b Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Fri, 1 Jun 2018 17:11:14 -0300 Subject: [PATCH 28/78] Workaround for android < Oreo --- .../io/highfidelity/hifiinterface/InterfaceActivity.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index da938ab85b..28acc77609 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -14,6 +14,7 @@ package io.highfidelity.hifiinterface; import android.content.Intent; import android.content.res.AssetManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Vibrator; @@ -178,7 +179,13 @@ public class InterfaceActivity extends QtActivity { Integer subLayer1 = 0; Integer subLayer2 = 0; try { - Field f = s1.getClass().getSuperclass().getDeclaredField("mSubLayer"); + String field; + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + field = "mSubLayer"; + } else { + field = "mWindowType"; + } + Field f = s1.getClass().getSuperclass().getDeclaredField(field); f.setAccessible(true); subLayer1 = (Integer) f.get(s1); subLayer2 = (Integer) f.get(s2); From 2879eeb687d4885cf220799bbade8fcd3960aaf1 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 1 Jun 2018 17:18:48 -0700 Subject: [PATCH 29/78] added flags for procedural face movement AvatarData.cpp toByteArray and parseDataFromBuffer --- libraries/avatars/src/AvatarData.cpp | 54 ++++++++++++++++++++-------- libraries/avatars/src/AvatarData.h | 7 ++-- libraries/shared/src/SharedUtil.cpp | 17 ++++++--- libraries/shared/src/SharedUtil.h | 6 ++-- 4 files changed, 61 insertions(+), 23 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 649460d6c7..247fc2a76a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -308,7 +308,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + - (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getNumSummedBlendshapeCoefficients()) : 0) + + (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) : 0) + (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0) + (hasJointDefaultPoseFlags ? AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) : 0); @@ -443,7 +443,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent auto startSection = destinationBuffer; auto data = reinterpret_cast(destinationBuffer); - uint8_t flags { 0 }; + uint16_t flags { 0 }; setSemiNibbleAt(flags, KEY_STATE_START_BIT, _keyState); @@ -451,20 +451,33 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent bool isFingerPointing = _handState & IS_FINGER_POINTING_FLAG; setSemiNibbleAt(flags, HAND_STATE_START_BIT, _handState & ~IS_FINGER_POINTING_FLAG); if (isFingerPointing) { - setAtBit(flags, HAND_STATE_FINGER_POINTING_BIT); + setAtBit16(flags, HAND_STATE_FINGER_POINTING_BIT); } // face tracker state if (_headData->_isFaceTrackerConnected) { - setAtBit(flags, IS_FACE_TRACKER_CONNECTED); + setAtBit16(flags, IS_FACE_TRACKER_CONNECTED); } // eye tracker state if (_headData->_isEyeTrackerConnected) { - setAtBit(flags, IS_EYE_TRACKER_CONNECTED); + setAtBit16(flags, IS_EYE_TRACKER_CONNECTED); } // referential state if (!parentID.isNull()) { - setAtBit(flags, HAS_REFERENTIAL); + setAtBit16(flags, HAS_REFERENTIAL); } + // audio face movement + if (_headData->getHasAudioEnabledFaceMovement()) { + setAtBit16(flags, AUDIO_ENABLED_FACE_MOVEMENT); + } + // procedural eye face movement + if (_headData->getHasProceduralEyeFaceMovement()) { + setAtBit16(flags, PROCEDURAL_EYE_FACE_MOVEMENT); + } + // procedural blink face movement + if (_headData->getHasProceduralBlinkFaceMovement()) { + setAtBit16(flags, PROCEDURAL_BLINK_FACE_MOVEMENT); + } + data->flags = flags; destinationBuffer += sizeof(AvatarDataPacket::AdditionalFlags); @@ -507,7 +520,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasFaceTrackerInfo) { auto startSection = destinationBuffer; auto faceTrackerInfo = reinterpret_cast(destinationBuffer); - const auto& blendshapeCoefficients = _headData->getSummedBlendshapeCoefficients(); + const auto& blendshapeCoefficients = _headData->getBlendshapeCoefficients(); faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; faceTrackerInfo->rightEyeBlink = _headData->_rightEyeBlink; @@ -973,7 +986,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AdditionalFlags, sizeof(AvatarDataPacket::AdditionalFlags)); auto data = reinterpret_cast(sourceBuffer); - uint8_t bitItems = data->flags; + uint16_t bitItems = data->flags; // key state, stored as a semi-nibble in the bitItems auto newKeyState = (KeyState)getSemiNibbleAt(bitItems, KEY_STATE_START_BIT); @@ -981,26 +994,37 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // hand state, stored as a semi-nibble plus a bit in the bitItems // we store the hand state as well as other items in a shared bitset. The hand state is an octal, but is split // into two sections to maintain backward compatibility. The bits are ordered as such (0-7 left to right). - // +---+-----+-----+--+ - // |x,x|H0,H1|x,x,x|H2| - // +---+-----+-----+--+ + // AA 6/1/18 added three more flags here for procedural audio, blink, and eye saccade enabled + // +---+-----+-----+--+--+--+--+-----+ + // |x,x|H0,H1|x,x,x|H2|Au|Bl|Ey|xxxxx| + // +---+-----+-----+--+--+--+--+-----+ // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits auto newHandState = getSemiNibbleAt(bitItems, HAND_STATE_START_BIT) - + (oneAtBit(bitItems, HAND_STATE_FINGER_POINTING_BIT) ? IS_FINGER_POINTING_FLAG : 0); + + (oneAtBit16(bitItems, HAND_STATE_FINGER_POINTING_BIT) ? IS_FINGER_POINTING_FLAG : 0); - auto newFaceTrackerConnected = oneAtBit(bitItems, IS_FACE_TRACKER_CONNECTED); - auto newEyeTrackerConnected = oneAtBit(bitItems, IS_EYE_TRACKER_CONNECTED); + auto newFaceTrackerConnected = oneAtBit16(bitItems, IS_FACE_TRACKER_CONNECTED); + auto newEyeTrackerConnected = oneAtBit16(bitItems, IS_EYE_TRACKER_CONNECTED); + + auto newHasAudioEnabledFaceMovement = oneAtBit16(bitItems, AUDIO_ENABLED_FACE_MOVEMENT); + auto newHasProceduralEyeFaceMovement = oneAtBit16(bitItems, PROCEDURAL_EYE_FACE_MOVEMENT); + auto newHasProceduralBlinkFaceMovement = oneAtBit16(bitItems, PROCEDURAL_BLINK_FACE_MOVEMENT); bool keyStateChanged = (_keyState != newKeyState); bool handStateChanged = (_handState != newHandState); bool faceStateChanged = (_headData->_isFaceTrackerConnected != newFaceTrackerConnected); bool eyeStateChanged = (_headData->_isEyeTrackerConnected != newEyeTrackerConnected); - bool somethingChanged = keyStateChanged || handStateChanged || faceStateChanged || eyeStateChanged; + bool audioEnableFaceMovementChanged = (_headData->getHasAudioEnabledFaceMovement() != newHasAudioEnabledFaceMovement); + bool proceduralEyeFaceMovementChanged = (_headData->getHasProceduralEyeFaceMovement() != newHasProceduralEyeFaceMovement); + bool proceduralBlinkFaceMovementChanged = (_headData->getHasProceduralBlinkFaceMovement() != newHasProceduralBlinkFaceMovement); + bool somethingChanged = keyStateChanged || handStateChanged || faceStateChanged || eyeStateChanged || audioEnableFaceMovementChanged || proceduralEyeFaceMovementChanged || proceduralBlinkFaceMovementChanged; _keyState = newKeyState; _handState = newHandState; _headData->_isFaceTrackerConnected = newFaceTrackerConnected; _headData->_isEyeTrackerConnected = newEyeTrackerConnected; + _headData->setHasAudioEnabledFaceMovement(newHasAudioEnabledFaceMovement); + _headData->setHasProceduralEyeFaceMovement(newHasProceduralEyeFaceMovement); + _headData->setHasProceduralBlinkFaceMovement(newHasProceduralBlinkFaceMovement); sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 2d8ee52ea1..ad304d5796 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -93,6 +93,9 @@ const int IS_FACE_TRACKER_CONNECTED = 4; // 5th bit const int IS_EYE_TRACKER_CONNECTED = 5; // 6th bit (was CHAT_CIRCLING) const int HAS_REFERENTIAL = 6; // 7th bit const int HAND_STATE_FINGER_POINTING_BIT = 7; // 8th bit +const int AUDIO_ENABLED_FACE_MOVEMENT = 8; // 9th bit +const int PROCEDURAL_EYE_FACE_MOVEMENT = 9; // 10th bit +const int PROCEDURAL_BLINK_FACE_MOVEMENT = 10; // 11th bit const char HAND_STATE_NULL = 0; @@ -200,9 +203,9 @@ namespace AvatarDataPacket { static_assert(sizeof(SensorToWorldMatrix) == SENSOR_TO_WORLD_SIZE, "AvatarDataPacket::SensorToWorldMatrix size doesn't match."); PACKED_BEGIN struct AdditionalFlags { - uint8_t flags; // additional flags: hand state, key state, eye tracking + uint16_t flags; // additional flags: hand state, key state, eye tracking } PACKED_END; - const size_t ADDITIONAL_FLAGS_SIZE = 1; + const size_t ADDITIONAL_FLAGS_SIZE = 2; static_assert(sizeof(AdditionalFlags) == ADDITIONAL_FLAGS_SIZE, "AvatarDataPacket::AdditionalFlags size doesn't match."); // only present if HAS_REFERENTIAL flag is set in AvatarInfo.flags diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 7c4a9b6d6c..35aab3fac1 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -297,14 +297,23 @@ void setAtBit(unsigned char& byte, int bitIndex) { byte |= (1 << (7 - bitIndex)); } +bool oneAtBit16(unsigned short word, int bitIndex) { + return (word >> (7 - bitIndex) & 1); +} + +void setAtBit16(unsigned short& word, int bitIndex) { + word |= (1 << (7 - bitIndex)); +} + + void clearAtBit(unsigned char& byte, int bitIndex) { if (oneAtBit(byte, bitIndex)) { byte -= (1 << (7 - bitIndex)); } } -int getSemiNibbleAt(unsigned char byte, int bitIndex) { - return (byte >> (6 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11 +int getSemiNibbleAt(unsigned short word, int bitIndex) { + return (word >> (6 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11 } int getNthBit(unsigned char byte, int ordinal) { @@ -326,9 +335,9 @@ int getNthBit(unsigned char byte, int ordinal) { return ERROR_RESULT; } -void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value) { +void setSemiNibbleAt(unsigned short& word, int bitIndex, int value) { //assert(value <= 3 && value >= 0); - byte |= ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 + word |= ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 } bool isInEnvironment(const char* environment) { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 9875314aa4..db9fff3e93 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -163,9 +163,11 @@ void printVoxelCode(unsigned char* voxelCode); int numberOfOnes(unsigned char byte); bool oneAtBit(unsigned char byte, int bitIndex); void setAtBit(unsigned char& byte, int bitIndex); +bool oneAtBit16(unsigned short word, int bitIndex); +void setAtBit16(unsigned short& word, int bitIndex); void clearAtBit(unsigned char& byte, int bitIndex); -int getSemiNibbleAt(unsigned char byte, int bitIndex); -void setSemiNibbleAt(unsigned char& byte, int bitIndex, int value); +int getSemiNibbleAt(unsigned short word, int bitIndex); +void setSemiNibbleAt(unsigned short& word, int bitIndex, int value); int getNthBit(unsigned char byte, int ordinal); /// determines the bit placement 0-7 of the ordinal set bit From beaea7abe6bcf2f76c0ef24962e3df3c12217980 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Sat, 2 Jun 2018 14:39:30 +0100 Subject: [PATCH 30/78] changed max face tracker info size to reflect summed coeff --- libraries/avatars/src/AvatarData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 247fc2a76a..4a3967a938 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -308,7 +308,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + - (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) : 0) + + (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getNumSummedBlendshapeCoefficients()) : 0) + (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0) + (hasJointDefaultPoseFlags ? AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) : 0); From 606b6d8947b87f646166d4cd8c0630dc65933330 Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Mon, 4 Jun 2018 09:16:39 -0700 Subject: [PATCH 31/78] fix code standard issue --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 42618796a5..e9de94ac57 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -124,7 +124,7 @@ Menu::Menu() { }); // Edit > Delete - auto deleteAction =addActionToQMenuAndActionHash(editMenu, "Delete", QKeySequence::Delete); + auto deleteAction = addActionToQMenuAndActionHash(editMenu, "Delete", QKeySequence::Delete); connect(deleteAction, &QAction::triggered, [] { QKeyEvent* keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Delete, Qt::ControlModifier); QCoreApplication::postEvent(QCoreApplication::instance(), keyEvent); From 61592cdadac1277e129ec378160c63aa80578586 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 4 Jun 2018 16:37:47 -0700 Subject: [PATCH 32/78] completed first try at all changes for scripted blendshapes --- interface/src/avatar/MyHead.cpp | 4 +++- libraries/avatars/src/AvatarData.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 13d635bdd8..8f53522b5a 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -72,7 +72,9 @@ void MyHead::simulate(float deltaTime) { _transientBlendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; _transientBlendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; } - applyEyelidOffset(getFinalOrientationInWorldFrame()); + if (_owningAvatar->getHasProceduralEyeFaceMovement()) { + applyEyelidOffset(getFinalOrientationInWorldFrame()); + } } auto eyeTracker = DependencyManager::get(); _isEyeTrackerConnected = eyeTracker->isTracking(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 247fc2a76a..7ae9ba1257 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1102,6 +1102,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { int numBytesRead = sourceBuffer - startSection; _faceTrackerRate.increment(numBytesRead); _faceTrackerUpdateRate.increment(); + } else { + _headData->_blendshapeCoefficients.fill(0, _headData->_blendshapeCoefficients.size()); } if (hasJointData) { From f87775c269475158c10c7c28b59025324b1a6dde Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 4 Jun 2018 14:39:48 -0700 Subject: [PATCH 33/78] Update serverless content to v2 --- cmake/externals/serverless-content/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/serverless-content/CMakeLists.txt b/cmake/externals/serverless-content/CMakeLists.txt index aa1c59a86b..81b82e8651 100644 --- a/cmake/externals/serverless-content/CMakeLists.txt +++ b/cmake/externals/serverless-content/CMakeLists.txt @@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68.zip - URL_MD5 a068f74d4045e257cfa7926fe6e38ad5 + URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC68-v2.zip + URL_MD5 f7d290471baf7f5694c147217b8fc548 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From 5132466ca0c4d3f5ac623643f6ee3b707010167b Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 5 Jun 2018 13:32:29 -0700 Subject: [PATCH 34/78] kept the transient coeff. and sending the blendshapes accross the network. and removed procedural updates to face from myhead.cpp --- interface/src/avatar/MyHead.cpp | 29 +--- .../src/avatars-renderer/Head.cpp | 160 +++++++++--------- 2 files changed, 81 insertions(+), 108 deletions(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 8f53522b5a..55db478d85 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -50,32 +50,11 @@ void MyHead::simulate(float deltaTime) { const bool hasActualFaceTrackerConnected = faceTracker && !faceTracker->isMuted(); _isFaceTrackerConnected = hasActualFaceTrackerConnected || _owningAvatar->getHasScriptedBlendshapes(); if (_isFaceTrackerConnected) { - if (hasActualFaceTrackerConnected) { - _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); - } else { - _transientBlendshapeCoefficients.fill(0, _blendshapeCoefficients.size()); - } - - if (_owningAvatar->getHasAudioEnabledFaceMovement() || (faceTracker && (typeid(*faceTracker) == typeid(DdeFaceTracker)) - && Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth))) { - - calculateMouthShapes(deltaTime); - - const int JAW_OPEN_BLENDSHAPE = 21; - const int MMMM_BLENDSHAPE = 34; - const int FUNNEL_BLENDSHAPE = 40; - const int SMILE_LEFT_BLENDSHAPE = 28; - const int SMILE_RIGHT_BLENDSHAPE = 29; - _transientBlendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; - _transientBlendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; - _transientBlendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; - _transientBlendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; - _transientBlendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; - } - if (_owningAvatar->getHasProceduralEyeFaceMovement()) { - applyEyelidOffset(getFinalOrientationInWorldFrame()); - } + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + } else { + _blendshapeCoefficients.fill(0, _blendshapeCoefficients.size()); } + auto eyeTracker = DependencyManager::get(); _isEyeTrackerConnected = eyeTracker->isTracking(); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 06b52c8d44..81b58c7aa1 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -58,24 +58,32 @@ void Head::simulate(float deltaTime) { _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); } - if (!_isFaceTrackerConnected) { - if (!_isEyeTrackerConnected) { - // Update eye saccades - const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; - const float AVERAGE_SACCADE_INTERVAL = 6.0f; - const float MICROSACCADE_MAGNITUDE = 0.002f; - const float SACCADE_MAGNITUDE = 0.04f; - const float NOMINAL_FRAME_RATE = 60.0f; + //if (!_isFaceTrackerConnected) { + if (!_isEyeTrackerConnected) { + // Update eye saccades + const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; + const float AVERAGE_SACCADE_INTERVAL = 6.0f; + const float MICROSACCADE_MAGNITUDE = 0.002f; + const float SACCADE_MAGNITUDE = 0.04f; + const float NOMINAL_FRAME_RATE = 60.0f; - if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { - _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); - } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { - _saccadeTarget = SACCADE_MAGNITUDE * randVector(); - } - _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime); - } else { - _saccade = glm::vec3(); + if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { + _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); + } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { + _saccadeTarget = SACCADE_MAGNITUDE * randVector(); } + _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime); + } else { + _saccade = glm::vec3(); + } + + + const float BLINK_SPEED = 10.0f; + const float BLINK_SPEED_VARIABILITY = 1.0f; + const float BLINK_START_VARIABILITY = 0.25f; + const float FULLY_OPEN = 0.0f; + const float FULLY_CLOSED = 1.0f; + if (getHasProceduralBlinkFaceMovement()) { // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; @@ -88,26 +96,7 @@ void Head::simulate(float deltaTime) { forceBlink = true; } - // Update audio attack data for facial animation (eyebrows and mouth) - float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz - _audioAttack = audioAttackAveragingRate * _audioAttack + - (1.0f - audioAttackAveragingRate) * fabs((audioLoudness - _longTermAverageLoudness) - _lastLoudness); - _lastLoudness = (audioLoudness - _longTermAverageLoudness); - - const float BROW_LIFT_THRESHOLD = 100.0f; - if (_audioAttack > BROW_LIFT_THRESHOLD) { - _browAudioLift += sqrtf(_audioAttack) * 0.01f; - } - _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); - - - const float BLINK_SPEED = 10.0f; - const float BLINK_SPEED_VARIABILITY = 1.0f; - const float BLINK_START_VARIABILITY = 0.25f; - const float FULLY_OPEN = 0.0f; - const float FULLY_CLOSED = 1.0f; - if (getHasProceduralBlinkFaceMovement()) { - if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { + if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { // no blinking when brows are raised; blink less with increasing loudness const float BASE_BLINK_RATE = 15.0f / 60.0f; const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; @@ -117,63 +106,68 @@ void Head::simulate(float deltaTime) { _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; if (randFloat() < 0.5f) { _leftEyeBlink = BLINK_START_VARIABILITY; - } - else { + } else { _rightEyeBlink = BLINK_START_VARIABILITY; } } - } - else { - _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - - if (_leftEyeBlink == FULLY_CLOSED) { - _leftEyeBlinkVelocity = -BLINK_SPEED; - - } - else if (_leftEyeBlink == FULLY_OPEN) { - _leftEyeBlinkVelocity = 0.0f; - } - if (_rightEyeBlink == FULLY_CLOSED) { - _rightEyeBlinkVelocity = -BLINK_SPEED; - - } - else if (_rightEyeBlink == FULLY_OPEN) { - _rightEyeBlinkVelocity = 0.0f; - } - } } else { - _rightEyeBlink = FULLY_OPEN; - _leftEyeBlink = FULLY_OPEN; - } + _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); + _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); - // use data to update fake Faceshift blendshape coefficients - if (getHasAudioEnabledFaceMovement()) { - calculateMouthShapes(deltaTime); - } else { - _audioJawOpen = 0.0f; - _browAudioLift = 0.0f; - _mouth2 = 0.0f; - _mouth3 = 0.0f; - _mouth4 = 0.0f; - _mouthTime = 0.0f; - } - FaceTracker::updateFakeCoefficients(_leftEyeBlink, - _rightEyeBlink, - _browAudioLift, - _audioJawOpen, - _mouth2, - _mouth3, - _mouth4, - _transientBlendshapeCoefficients); + if (_leftEyeBlink == FULLY_CLOSED) { + _leftEyeBlinkVelocity = -BLINK_SPEED; - if (getHasProceduralEyeFaceMovement()) { - applyEyelidOffset(getOrientation()); + } else if (_leftEyeBlink == FULLY_OPEN) { + _leftEyeBlinkVelocity = 0.0f; + } + if (_rightEyeBlink == FULLY_CLOSED) { + _rightEyeBlinkVelocity = -BLINK_SPEED; + + } else if (_rightEyeBlink == FULLY_OPEN) { + _rightEyeBlinkVelocity = 0.0f; + } } } else { - _saccade = glm::vec3(); + _rightEyeBlink = FULLY_OPEN; + _leftEyeBlink = FULLY_OPEN; } + // use data to update fake Faceshift blendshape coefficients + if (getHasAudioEnabledFaceMovement()) { + // Update audio attack data for facial animation (eyebrows and mouth) + float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz + _audioAttack = audioAttackAveragingRate * _audioAttack + + (1.0f - audioAttackAveragingRate) * fabs((audioLoudness - _longTermAverageLoudness) - _lastLoudness); + _lastLoudness = (audioLoudness - _longTermAverageLoudness); + const float BROW_LIFT_THRESHOLD = 100.0f; + if (_audioAttack > BROW_LIFT_THRESHOLD) { + _browAudioLift += sqrtf(_audioAttack) * 0.01f; + } + _browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f); + calculateMouthShapes(deltaTime); + + } else { + _audioJawOpen = 0.0f; + _browAudioLift = 0.0f; + _mouth2 = 0.0f; + _mouth3 = 0.0f; + _mouth4 = 0.0f; + _mouthTime = 0.0f; + } + + FaceTracker::updateFakeCoefficients(_leftEyeBlink, + _rightEyeBlink, + _browAudioLift, + _audioJawOpen, + _mouth2, + _mouth3, + _mouth4, + _transientBlendshapeCoefficients); + + if (getHasProceduralEyeFaceMovement()) { + applyEyelidOffset(getOrientation()); + } + _leftEyePosition = _rightEyePosition = getPosition(); if (_owningAvatar) { auto skeletonModel = static_cast(_owningAvatar)->getSkeletonModel(); From f21431ca88c17020f09932e9ee2fb9985029d4b9 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 5 Jun 2018 16:33:28 -0700 Subject: [PATCH 35/78] added check for the actual face tracker in MyHead.cpp to prevent crash --- interface/src/avatar/MyHead.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 55db478d85..eb365c20fb 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -50,9 +50,9 @@ void MyHead::simulate(float deltaTime) { const bool hasActualFaceTrackerConnected = faceTracker && !faceTracker->isMuted(); _isFaceTrackerConnected = hasActualFaceTrackerConnected || _owningAvatar->getHasScriptedBlendshapes(); if (_isFaceTrackerConnected) { - _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); - } else { - _blendshapeCoefficients.fill(0, _blendshapeCoefficients.size()); + if (hasActualFaceTrackerConnected) { + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + } } auto eyeTracker = DependencyManager::get(); From 0c879d85b5d89b00ff3daf4bbed090e64bd97381 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 5 Jun 2018 17:33:32 -0700 Subject: [PATCH 36/78] added print statements and removed summed coeff from avatarData.cpp --- libraries/avatars-renderer/src/avatars-renderer/Head.cpp | 5 ++++- libraries/avatars/src/AvatarData.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 81b58c7aa1..39b50b9255 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "Logging.h" #include "Avatar.h" @@ -84,7 +85,7 @@ void Head::simulate(float deltaTime) { const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; if (getHasProceduralBlinkFaceMovement()) { - + qCDebug(avatars_renderer) << "in the blink code"; // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; const float TALKING_LOUDNESS = 100.0f; @@ -134,6 +135,7 @@ void Head::simulate(float deltaTime) { // use data to update fake Faceshift blendshape coefficients if (getHasAudioEnabledFaceMovement()) { + qCDebug(avatars_renderer) << "in the audio face code"; // Update audio attack data for facial animation (eyebrows and mouth) float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz _audioAttack = audioAttackAveragingRate * _audioAttack + @@ -165,6 +167,7 @@ void Head::simulate(float deltaTime) { _transientBlendshapeCoefficients); if (getHasProceduralEyeFaceMovement()) { + qCDebug(avatars_renderer) << "in the eye face code"; applyEyelidOffset(getOrientation()); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d72abe592b..7ae9ba1257 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -308,7 +308,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + - (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getNumSummedBlendshapeCoefficients()) : 0) + + (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) : 0) + (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0) + (hasJointDefaultPoseFlags ? AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) : 0); From 9b91f7bb5af849d3b2a359ab6285d803473de04e Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 6 Jun 2018 03:43:27 +0300 Subject: [PATCH 37/78] FB15569 - Debug default scripts no longer on developer menu --- scripts/defaultScripts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 59a51830be..045dff1295 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -57,7 +57,6 @@ if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_I menuItemName: MENU_ITEM, isCheckable: true, isChecked: previousSetting, - grouping: "Advanced" }); } From dca93ca61f8fe2ef51f6ba0a809a3dd59a7e364a Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 6 Jun 2018 16:44:12 -0700 Subject: [PATCH 38/78] fixed setatone16 function to return the correct result for additional flags --- .../avatars-renderer/src/avatars-renderer/Head.cpp | 14 +++++++++++--- libraries/avatars/src/AvatarData.cpp | 11 +++++++++++ libraries/shared/src/SharedUtil.cpp | 4 ++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 39b50b9255..9b6333a4b5 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -45,6 +45,13 @@ void Head::reset() { void Head::simulate(float deltaTime) { const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for + qCDebug(avatars_renderer) << "name " << _owningAvatar->getName(); + if (_owningAvatar->isMyAvatar()) { + qCDebug(avatars_renderer) << "my avatar"; + } else { + qCDebug(avatars_renderer) << "not my avatar " << _owningAvatar->getAudioLoudness(); + } + // grab the audio loudness from the owning avatar, if we have one float audioLoudness = _owningAvatar ? _owningAvatar->getAudioLoudness() : 0.0f; @@ -78,6 +85,7 @@ void Head::simulate(float deltaTime) { _saccade = glm::vec3(); } + const float BLINK_SPEED = 10.0f; const float BLINK_SPEED_VARIABILITY = 1.0f; @@ -85,7 +93,7 @@ void Head::simulate(float deltaTime) { const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; if (getHasProceduralBlinkFaceMovement()) { - qCDebug(avatars_renderer) << "in the blink code"; + qCDebug(avatars_renderer) << "in the blink code " << _owningAvatar->getName(); // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; const float TALKING_LOUDNESS = 100.0f; @@ -135,7 +143,7 @@ void Head::simulate(float deltaTime) { // use data to update fake Faceshift blendshape coefficients if (getHasAudioEnabledFaceMovement()) { - qCDebug(avatars_renderer) << "in the audio face code"; + //qCDebug(avatars_renderer) << "in the audio face code"; // Update audio attack data for facial animation (eyebrows and mouth) float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz _audioAttack = audioAttackAveragingRate * _audioAttack + @@ -167,7 +175,7 @@ void Head::simulate(float deltaTime) { _transientBlendshapeCoefficients); if (getHasProceduralEyeFaceMovement()) { - qCDebug(avatars_renderer) << "in the eye face code"; + //qCDebug(avatars_renderer) << "in the eye face code"; applyEyelidOffset(getOrientation()); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 7ae9ba1257..43b490719f 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -522,12 +522,17 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent auto faceTrackerInfo = reinterpret_cast(destinationBuffer); const auto& blendshapeCoefficients = _headData->getBlendshapeCoefficients(); + //for (int i = 0; i < blendshapeCoefficients.size(); i++) { + // qCWarning(avatars) << "blend coeff " << i << " " << blendshapeCoefficients[i]; + //} + faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; faceTrackerInfo->rightEyeBlink = _headData->_rightEyeBlink; faceTrackerInfo->averageLoudness = _headData->_averageLoudness; faceTrackerInfo->browAudioLift = _headData->_browAudioLift; faceTrackerInfo->numBlendshapeCoefficients = blendshapeCoefficients.size(); destinationBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); + qCWarning(avatars) << "face tracker info left eye blink " << faceTrackerInfo->leftEyeBlink; memcpy(destinationBuffer, blendshapeCoefficients.data(), blendshapeCoefficients.size() * sizeof(float)); destinationBuffer += blendshapeCoefficients.size() * sizeof(float); @@ -1009,6 +1014,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto newHasProceduralEyeFaceMovement = oneAtBit16(bitItems, PROCEDURAL_EYE_FACE_MOVEMENT); auto newHasProceduralBlinkFaceMovement = oneAtBit16(bitItems, PROCEDURAL_BLINK_FACE_MOVEMENT); + if (newHasAudioEnabledFaceMovement) { + qCWarning(avatars) << "name " << getName() << "audio enabled flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "audio enabled flag is false"; + } bool keyStateChanged = (_keyState != newKeyState); bool handStateChanged = (_handState != newHandState); bool faceStateChanged = (_headData->_isFaceTrackerConnected != newFaceTrackerConnected); @@ -1086,6 +1096,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(FaceTrackerInfo, sizeof(AvatarDataPacket::FaceTrackerInfo)); auto faceTrackerInfo = reinterpret_cast(sourceBuffer); sourceBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); + qCWarning(avatars) << "parse data left eye blink " << faceTrackerInfo->leftEyeBlink; _headData->_leftEyeBlink = faceTrackerInfo->leftEyeBlink; _headData->_rightEyeBlink = faceTrackerInfo->rightEyeBlink; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 35aab3fac1..c0203106c0 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -298,11 +298,11 @@ void setAtBit(unsigned char& byte, int bitIndex) { } bool oneAtBit16(unsigned short word, int bitIndex) { - return (word >> (7 - bitIndex) & 1); + return (word >> (16 - bitIndex) & 1); } void setAtBit16(unsigned short& word, int bitIndex) { - word |= (1 << (7 - bitIndex)); + word |= (1 << (16 - bitIndex)); } From 6a11ddc3497b9cd3a393e24ed8ae6bc96f1c97da Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 6 Jun 2018 17:50:50 -0700 Subject: [PATCH 39/78] removed print statements and removed the setting of procedural head variables in avatarData.cpp --- .../src/avatars-renderer/Head.cpp | 14 ++--- libraries/avatars/src/AvatarData.cpp | 55 +++++++++++++++---- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 9b6333a4b5..538c928902 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -45,12 +45,12 @@ void Head::reset() { void Head::simulate(float deltaTime) { const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for - qCDebug(avatars_renderer) << "name " << _owningAvatar->getName(); - if (_owningAvatar->isMyAvatar()) { - qCDebug(avatars_renderer) << "my avatar"; - } else { - qCDebug(avatars_renderer) << "not my avatar " << _owningAvatar->getAudioLoudness(); - } + //qCDebug(avatars_renderer) << "name " << _owningAvatar->getName(); + //if (_owningAvatar->isMyAvatar()) { + // qCDebug(avatars_renderer) << "my avatar"; + //} else { + // qCDebug(avatars_renderer) << "not my avatar " << _owningAvatar->getAudioLoudness(); + //} // grab the audio loudness from the owning avatar, if we have one float audioLoudness = _owningAvatar ? _owningAvatar->getAudioLoudness() : 0.0f; @@ -93,7 +93,7 @@ void Head::simulate(float deltaTime) { const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; if (getHasProceduralBlinkFaceMovement()) { - qCDebug(avatars_renderer) << "in the blink code " << _owningAvatar->getName(); + //qCDebug(avatars_renderer) << "in the blink code " << _owningAvatar->getName(); // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; const float TALKING_LOUDNESS = 100.0f; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 43b490719f..feb73fd0fb 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -532,7 +532,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent faceTrackerInfo->browAudioLift = _headData->_browAudioLift; faceTrackerInfo->numBlendshapeCoefficients = blendshapeCoefficients.size(); destinationBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); - qCWarning(avatars) << "face tracker info left eye blink " << faceTrackerInfo->leftEyeBlink; + //qCWarning(avatars) << "face tracker info left eye blink " << faceTrackerInfo->leftEyeBlink; memcpy(destinationBuffer, blendshapeCoefficients.data(), blendshapeCoefficients.size() * sizeof(float)); destinationBuffer += blendshapeCoefficients.size() * sizeof(float); @@ -1014,11 +1014,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto newHasProceduralEyeFaceMovement = oneAtBit16(bitItems, PROCEDURAL_EYE_FACE_MOVEMENT); auto newHasProceduralBlinkFaceMovement = oneAtBit16(bitItems, PROCEDURAL_BLINK_FACE_MOVEMENT); - if (newHasAudioEnabledFaceMovement) { - qCWarning(avatars) << "name " << getName() << "audio enabled flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "audio enabled flag is false"; - } + bool keyStateChanged = (_keyState != newKeyState); bool handStateChanged = (_handState != newHandState); bool faceStateChanged = (_headData->_isFaceTrackerConnected != newFaceTrackerConnected); @@ -1040,6 +1036,43 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (somethingChanged) { _additionalFlagsChanged = usecTimestampNow(); + if (newHasAudioEnabledFaceMovement) { + qCWarning(avatars) << "name " << getName() << "audio enabled flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "audio enabled flag is false"; + } + if (newHasProceduralEyeFaceMovement) { + qCWarning(avatars) << "name " << getName() << "eye face enabled flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "eye face flag is false"; + } + if (newHasProceduralBlinkFaceMovement) { + qCWarning(avatars) << "name " << getName() << "blink flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "blink flag is false"; + } + if (newFaceTrackerConnected) { + qCWarning(avatars) << "name " << getName() << "face tracker flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "face tracker flag is false"; + } + if (newEyeTrackerConnected) { + qCWarning(avatars) << "name " << getName() << "eye tracker flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "eye tracker flag is false"; + } + if (newHandState) { + qCWarning(avatars) << "name " << getName() << "hand state flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "hand state flag is false"; + } + if (newKeyState) { + qCWarning(avatars) << "name " << getName() << "key state flag is true"; + } else { + qCWarning(avatars) << "name " << getName() << "key state flag is false"; + } + + } int numBytesRead = sourceBuffer - startSection; _additionalFlagsRate.increment(numBytesRead); @@ -1096,12 +1129,12 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(FaceTrackerInfo, sizeof(AvatarDataPacket::FaceTrackerInfo)); auto faceTrackerInfo = reinterpret_cast(sourceBuffer); sourceBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); - qCWarning(avatars) << "parse data left eye blink " << faceTrackerInfo->leftEyeBlink; + //qCWarning(avatars) << "parse data left eye blink " << faceTrackerInfo->leftEyeBlink; - _headData->_leftEyeBlink = faceTrackerInfo->leftEyeBlink; - _headData->_rightEyeBlink = faceTrackerInfo->rightEyeBlink; - _headData->_averageLoudness = faceTrackerInfo->averageLoudness; - _headData->_browAudioLift = faceTrackerInfo->browAudioLift; + //_headData->_leftEyeBlink = faceTrackerInfo->leftEyeBlink; + //_headData->_rightEyeBlink = faceTrackerInfo->rightEyeBlink; + //_headData->_averageLoudness = faceTrackerInfo->averageLoudness; + //_headData->_browAudioLift = faceTrackerInfo->browAudioLift; int numCoefficients = faceTrackerInfo->numBlendshapeCoefficients; const int coefficientsSize = sizeof(float) * numCoefficients; From 97831e61f054d496d1c9b3740bd8e36ccc4a18f6 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 7 Jun 2018 12:54:46 -0700 Subject: [PATCH 40/78] removed stray comments --- interface/src/avatar/MyHead.cpp | 1 + .../src/avatars-renderer/Head.cpp | 3 --- libraries/avatars/src/AvatarData.cpp | 23 ++++++++----------- libraries/avatars/src/AvatarData.h | 13 ++++++++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index eb365c20fb..960dfd3402 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -57,6 +57,7 @@ void MyHead::simulate(float deltaTime) { auto eyeTracker = DependencyManager::get(); _isEyeTrackerConnected = eyeTracker->isTracking(); + // if eye tracker is connected we should get the data here. } Parent::simulate(deltaTime); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 538c928902..07d82f8af7 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -93,7 +93,6 @@ void Head::simulate(float deltaTime) { const float FULLY_OPEN = 0.0f; const float FULLY_CLOSED = 1.0f; if (getHasProceduralBlinkFaceMovement()) { - //qCDebug(avatars_renderer) << "in the blink code " << _owningAvatar->getName(); // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; const float TALKING_LOUDNESS = 100.0f; @@ -143,7 +142,6 @@ void Head::simulate(float deltaTime) { // use data to update fake Faceshift blendshape coefficients if (getHasAudioEnabledFaceMovement()) { - //qCDebug(avatars_renderer) << "in the audio face code"; // Update audio attack data for facial animation (eyebrows and mouth) float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz _audioAttack = audioAttackAveragingRate * _audioAttack + @@ -175,7 +173,6 @@ void Head::simulate(float deltaTime) { _transientBlendshapeCoefficients); if (getHasProceduralEyeFaceMovement()) { - //qCDebug(avatars_renderer) << "in the eye face code"; applyEyelidOffset(getOrientation()); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index feb73fd0fb..539e109501 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -522,17 +522,14 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent auto faceTrackerInfo = reinterpret_cast(destinationBuffer); const auto& blendshapeCoefficients = _headData->getBlendshapeCoefficients(); - //for (int i = 0; i < blendshapeCoefficients.size(); i++) { - // qCWarning(avatars) << "blend coeff " << i << " " << blendshapeCoefficients[i]; - //} - + //note: we don't use the blink and average loudness, we just use the numBlendShapes and + // compute the procedural info on the client side. faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; faceTrackerInfo->rightEyeBlink = _headData->_rightEyeBlink; faceTrackerInfo->averageLoudness = _headData->_averageLoudness; faceTrackerInfo->browAudioLift = _headData->_browAudioLift; faceTrackerInfo->numBlendshapeCoefficients = blendshapeCoefficients.size(); destinationBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); - //qCWarning(avatars) << "face tracker info left eye blink " << faceTrackerInfo->leftEyeBlink; memcpy(destinationBuffer, blendshapeCoefficients.data(), blendshapeCoefficients.size() * sizeof(float)); destinationBuffer += blendshapeCoefficients.size() * sizeof(float); @@ -999,7 +996,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // hand state, stored as a semi-nibble plus a bit in the bitItems // we store the hand state as well as other items in a shared bitset. The hand state is an octal, but is split // into two sections to maintain backward compatibility. The bits are ordered as such (0-7 left to right). - // AA 6/1/18 added three more flags here for procedural audio, blink, and eye saccade enabled + // AA 6/1/18 added three more flags bits 8,9, and 10 for procedural audio, blink, and eye saccade enabled // +---+-----+-----+--+--+--+--+-----+ // |x,x|H0,H1|x,x,x|H2|Au|Bl|Ey|xxxxx| // +---+-----+-----+--+--+--+--+-----+ @@ -1124,25 +1121,23 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } if (hasFaceTrackerInfo) { + //qCWarning(avatars) << "parsing face tracker info "; auto startSection = sourceBuffer; PACKET_READ_CHECK(FaceTrackerInfo, sizeof(AvatarDataPacket::FaceTrackerInfo)); auto faceTrackerInfo = reinterpret_cast(sourceBuffer); - sourceBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); - //qCWarning(avatars) << "parse data left eye blink " << faceTrackerInfo->leftEyeBlink; - - //_headData->_leftEyeBlink = faceTrackerInfo->leftEyeBlink; - //_headData->_rightEyeBlink = faceTrackerInfo->rightEyeBlink; - //_headData->_averageLoudness = faceTrackerInfo->averageLoudness; - //_headData->_browAudioLift = faceTrackerInfo->browAudioLift; - int numCoefficients = faceTrackerInfo->numBlendshapeCoefficients; const int coefficientsSize = sizeof(float) * numCoefficients; + sourceBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); + PACKET_READ_CHECK(FaceTrackerCoefficients, coefficientsSize); _headData->_blendshapeCoefficients.resize(numCoefficients); // make sure there's room for the copy! _headData->_transientBlendshapeCoefficients.resize(numCoefficients); + + //only copy the blendshapes to headData not the procedural face info memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, coefficientsSize); sourceBuffer += coefficientsSize; + int numBytesRead = sourceBuffer - startSection; _faceTrackerRate.increment(numBytesRead); _faceTrackerUpdateRate.increment(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index ad304d5796..51b3257ba2 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -79,14 +79,21 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = // Bitset of state flags - we store the key state, hand state, Faceshift, eye tracking, and existence of // referential data in this bit set. The hand state is an octal, but is split into two sections to maintain // backward compatibility. The bits are ordered as such (0-7 left to right). -// +-----+-----+-+-+-+--+ -// |K0,K1|H0,H1|F|E|R|H2| -// +-----+-----+-+-+-+--+ +// AA 6/1/18 added three more flags bits 8,9, and 10 for procedural audio, blink, and eye saccade enabled +// +// +-----+-----+-+-+-+--+--+--+--+-----+ +// |K0,K1|H0,H1|F|E|R|H2|Au|Bl|Ey|xxxxx| +// +-----+-----+-+-+-+--+--+--+--+-----+ +// // Key state - K0,K1 is found in the 1st and 2nd bits // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits // Face tracker - F is found in the 5th bit // Eye tracker - E is found in the 6th bit // Referential Data - R is found in the 7th bit +// Procedural audio to mouth movement is enabled 8th bit +// Procedural Blink is enabled 9th bit +// Procedural Eyelid is enabled 10th bit + const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits const int IS_FACE_TRACKER_CONNECTED = 4; // 5th bit From da92ff993f76f961923895b39d62f26390396316 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 6 Jun 2018 03:52:22 +0300 Subject: [PATCH 41/78] terminate thread if it didn't complete during MAX_SCRIPT_QUITTING_TIME --- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 72860acbc5..8e329c9c07 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -419,7 +419,7 @@ void ScriptEngine::waitTillDoneRunning() { // Wait for the scripting thread to stop running, as // flooding it with aborts/exceptions will persist it longer static const auto MAX_SCRIPT_QUITTING_TIME = 0.5 * MSECS_PER_SECOND; - if (workerThread->wait(MAX_SCRIPT_QUITTING_TIME)) { + if (!workerThread->wait(MAX_SCRIPT_QUITTING_TIME)) { workerThread->terminate(); } } From 836c3da8581c6db43cbda0754cefd2266dff137f Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 7 Jun 2018 15:11:57 -0700 Subject: [PATCH 42/78] cleaned up white space and removed extra declaration of hadAudioEnabledFaceMovement --- interface/src/avatar/MyAvatar.h | 1 - interface/src/avatar/MyHead.cpp | 2 +- .../src/avatars-renderer/Head.cpp | 17 +-- libraries/avatars/src/AvatarData.cpp | 11 +- libraries/avatars/src/HeadData.h | 2 - .../DefaultStylizedFemale_Clothed.fst | 139 ------------------ scripts/developer/facialExpressions.js | 4 +- 7 files changed, 10 insertions(+), 166 deletions(-) delete mode 100644 scripts/developer/DefaultStylizedFemale_Clothed.fst diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 813dddcc98..b401b2469a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1466,7 +1466,6 @@ private: float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; std::atomic _hasScriptedBlendShapes { false }; - bool _hasAudioEnabledFaceMovement { true }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp index 960dfd3402..9b05a26c76 100644 --- a/interface/src/avatar/MyHead.cpp +++ b/interface/src/avatar/MyHead.cpp @@ -54,7 +54,7 @@ void MyHead::simulate(float deltaTime) { _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); } } - + auto eyeTracker = DependencyManager::get(); _isEyeTrackerConnected = eyeTracker->isTracking(); // if eye tracker is connected we should get the data here. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 07d82f8af7..50f20918fa 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -45,13 +45,6 @@ void Head::reset() { void Head::simulate(float deltaTime) { const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for - //qCDebug(avatars_renderer) << "name " << _owningAvatar->getName(); - //if (_owningAvatar->isMyAvatar()) { - // qCDebug(avatars_renderer) << "my avatar"; - //} else { - // qCDebug(avatars_renderer) << "not my avatar " << _owningAvatar->getAudioLoudness(); - //} - // grab the audio loudness from the owning avatar, if we have one float audioLoudness = _owningAvatar ? _owningAvatar->getAudioLoudness() : 0.0f; @@ -84,9 +77,7 @@ void Head::simulate(float deltaTime) { } else { _saccade = glm::vec3(); } - - - + const float BLINK_SPEED = 10.0f; const float BLINK_SPEED_VARIABILITY = 1.0f; const float BLINK_START_VARIABILITY = 0.25f; @@ -162,7 +153,7 @@ void Head::simulate(float deltaTime) { _mouth4 = 0.0f; _mouthTime = 0.0f; } - + FaceTracker::updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, _browAudioLift, @@ -171,11 +162,11 @@ void Head::simulate(float deltaTime) { _mouth3, _mouth4, _transientBlendshapeCoefficients); - + if (getHasProceduralEyeFaceMovement()) { applyEyelidOffset(getOrientation()); } - + _leftEyePosition = _rightEyePosition = getPosition(); if (_owningAvatar) { auto skeletonModel = static_cast(_owningAvatar)->getSkeletonModel(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 539e109501..b8ccbce962 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -521,8 +521,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent auto startSection = destinationBuffer; auto faceTrackerInfo = reinterpret_cast(destinationBuffer); const auto& blendshapeCoefficients = _headData->getBlendshapeCoefficients(); - - //note: we don't use the blink and average loudness, we just use the numBlendShapes and + // note: we don't use the blink and average loudness, we just use the numBlendShapes and // compute the procedural info on the client side. faceTrackerInfo->leftEyeBlink = _headData->_leftEyeBlink; faceTrackerInfo->rightEyeBlink = _headData->_rightEyeBlink; @@ -1068,7 +1067,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } else { qCWarning(avatars) << "name " << getName() << "key state flag is false"; } - } int numBytesRead = sourceBuffer - startSection; @@ -1121,7 +1119,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } if (hasFaceTrackerInfo) { - //qCWarning(avatars) << "parsing face tracker info "; auto startSection = sourceBuffer; PACKET_READ_CHECK(FaceTrackerInfo, sizeof(AvatarDataPacket::FaceTrackerInfo)); @@ -1129,12 +1126,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { int numCoefficients = faceTrackerInfo->numBlendshapeCoefficients; const int coefficientsSize = sizeof(float) * numCoefficients; sourceBuffer += sizeof(AvatarDataPacket::FaceTrackerInfo); - + PACKET_READ_CHECK(FaceTrackerCoefficients, coefficientsSize); _headData->_blendshapeCoefficients.resize(numCoefficients); // make sure there's room for the copy! - _headData->_transientBlendshapeCoefficients.resize(numCoefficients); - - //only copy the blendshapes to headData not the procedural face info + //only copy the blendshapes to headData, not the procedural face info memcpy(_headData->_blendshapeCoefficients.data(), sourceBuffer, coefficientsSize); sourceBuffer += coefficientsSize; diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index f8eca0915e..e8289269d8 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -101,8 +101,6 @@ protected: glm::vec3 _lookAtPosition; quint64 _lookAtPositionChanged { 0 }; - //std::atomic _hasProceduralBlinkFaceMovement{ true }; - //std::atomic _hasProceduralEyeFaceMovement{ true }; bool _hasAudioEnabledFaceMovement { true }; bool _hasProceduralBlinkFaceMovement{ true }; bool _hasProceduralEyeFaceMovement{ true }; diff --git a/scripts/developer/DefaultStylizedFemale_Clothed.fst b/scripts/developer/DefaultStylizedFemale_Clothed.fst deleted file mode 100644 index 3e46d6a15c..0000000000 --- a/scripts/developer/DefaultStylizedFemale_Clothed.fst +++ /dev/null @@ -1,139 +0,0 @@ -name = DefaultStylizedFemale_Clothed -type = body+head -scale = 1 -filename = DefaultStylizedFemale_Clothed/DefaultStylizedFemale_Clothed.fbx -texdir = DefaultStylizedFemale_Clothed/textures -joint = jointLean = Spine -joint = jointRightHand = RightHand -joint = jointEyeLeft = LeftEye -joint = jointHead = HeadTop_End -joint = jointNeck = Neck -joint = jointRoot = Hips -joint = jointEyeRight = RightEye -joint = jointLeftHand = LeftHand -freeJoint = LeftArm -freeJoint = LeftForeArm -freeJoint = RightArm -freeJoint = RightForeArm -bs = EyeBlink_L = Blink_Left = 1 -bs = Sneer = Squint_Right = 0.5 -bs = Sneer = Squint_Left = 0.5 -bs = Sneer = NoseScrunch_Right = 0.75 -bs = Sneer = NoseScrunch_Left = 0.75 -bs = ChinLowerRaise = Jaw_Up = 1 -bs = EyeSquint_R = Squint_Right = 1 -bs = MouthSmile_R = Smile_Right = 1 -bs = ChinUpperRaise = UpperLipUp_Right = 0.5 -bs = ChinUpperRaise = UpperLipUp_Left = 0.5 -bs = LipsLowerOpen = LowerLipOut = 1 -bs = LipsLowerDown = LowerLipDown_Right = 0.7 -bs = LipsLowerDown = LowerLipDown_Left = 0.7 -bs = BrowsU_L = BrowsUp_Left = 1 -bs = MouthRight = Midmouth_Right = 1 -bs = MouthDimple_R = Smile_Right = 0.25 -bs = LipsPucker = MouthNarrow_Right = 1 -bs = LipsPucker = MouthNarrow_Left = 1 -bs = Puff = CheekPuff_Right = 1 -bs = Puff = CheekPuff_Left = 1 -bs = JawFwd = JawForeward = 1 -bs = BrowsD_L = BrowsDown_Left = 1 -bs = LipsFunnel = TongueUp = 1 -bs = LipsFunnel = MouthWhistle_NarrowAdjust_Right = 0.5 -bs = LipsFunnel = MouthWhistle_NarrowAdjust_Left = 0.5 -bs = LipsFunnel = MouthNarrow_Right = 1 -bs = LipsFunnel = MouthNarrow_Left = 1 -bs = LipsFunnel = Jaw_Down = 0.36 -bs = LipsFunnel = JawForeward = 0.39 -bs = LipsUpperOpen = UpperLipOut = 1 -bs = EyeSquint_L = Squint_Left = 1 -bs = MouthDimple_L = Smile_Left = 0.25 -bs = LipsLowerClose = LowerLipIn = 1 -bs = MouthFrown_R = Frown_Right = 1 -bs = MouthFrown_L = Frown_Left = 1 -bs = BrowsU_R = BrowsUp_Right = 1 -bs = JawOpen = MouthOpen = 0.7 -bs = JawRight = Jaw_Right = 1 -bs = MouthLeft = Midmouth_Left = 1 -bs = BrowsU_C = BrowsUp_Right = 1 -bs = BrowsU_C = BrowsUp_Left = 1 -bs = LipsUpperUp = UpperLipUp_Right = 0.7 -bs = LipsUpperUp = UpperLipUp_Left = 0.7 -bs = EyeBlink_R = Blink_Right = 1 -bs = EyeOpen_R = EyesWide_Right = 1 -bs = LipsUpperClose = UpperLipIn = 1 -bs = MouthSmile_L = Smile_Left = 1 -bs = EyeOpen_L = EyesWide_Left = 1 -bs = JawLeft = JawRotateY_Left = 0.5 -bs = BrowsD_R = BrowsDown_Right = 1 -jointIndex = RightHandThumb4 = 21 -jointIndex = Neck = 62 -jointIndex = LeftHandIndex4 = 57 -jointIndex = Body = 71 -jointIndex = LeftHandIndex1 = 54 -jointIndex = RightHand = 17 -jointIndex = RightHandMiddle1 = 26 -jointIndex = Spine = 11 -jointIndex = RightHandRing2 = 31 -jointIndex = RightArm = 15 -jointIndex = RightHandPinky2 = 35 -jointIndex = LeftToeBase = 9 -jointIndex = RightHandIndex3 = 24 -jointIndex = RightHandRing1 = 30 -jointIndex = RightHandPinky1 = 34 -jointIndex = RightEye = 66 -jointIndex = LeftHandRing4 = 49 -jointIndex = LeftHandRing2 = 47 -jointIndex = RightHandMiddle2 = 27 -jointIndex = Head = 63 -jointIndex = LeftHandMiddle4 = 53 -jointIndex = LeftLeg = 7 -jointIndex = LeftHandPinky2 = 43 -jointIndex = LeftHandThumb1 = 58 -jointIndex = LeftHandPinky4 = 45 -jointIndex = RightHandIndex1 = 22 -jointIndex = Tops = 67 -jointIndex = Hips = 0 -jointIndex = LeftUpLeg = 6 -jointIndex = RightShoulder = 14 -jointIndex = Spine2 = 13 -jointIndex = RightHandRing4 = 33 -jointIndex = RightHandThumb3 = 20 -jointIndex = RightHandIndex4 = 25 -jointIndex = LeftFoot = 8 -jointIndex = LeftHandRing3 = 48 -jointIndex = LeftHand = 41 -jointIndex = LeftForeArm = 40 -jointIndex = LeftToe_End = 10 -jointIndex = Bottoms = 68 -jointIndex = RightFoot = 3 -jointIndex = LeftHandMiddle2 = 51 -jointIndex = LeftHandThumb3 = 60 -jointIndex = RightHandPinky3 = 36 -jointIndex = LeftEye = 65 -jointIndex = LeftHandIndex2 = 55 -jointIndex = RightHandIndex2 = 23 -jointIndex = LeftHandPinky1 = 42 -jointIndex = LeftHandMiddle3 = 52 -jointIndex = RightHandMiddle4 = 29 -jointIndex = LeftHandThumb2 = 59 -jointIndex = Shoes = 69 -jointIndex = RightHandThumb1 = 18 -jointIndex = RightToe_End = 5 -jointIndex = RightHandThumb2 = 19 -jointIndex = RightUpLeg = 1 -jointIndex = RightLeg = 2 -jointIndex = LeftHandMiddle1 = 50 -jointIndex = LeftHandIndex3 = 56 -jointIndex = LeftHandThumb4 = 61 -jointIndex = RightHandRing3 = 32 -jointIndex = Hair = 70 -jointIndex = Spine1 = 12 -jointIndex = LeftHandRing1 = 46 -jointIndex = LeftArm = 39 -jointIndex = LeftShoulder = 38 -jointIndex = RightForeArm = 16 -jointIndex = HeadTop_End = 64 -jointIndex = RightHandPinky4 = 37 -jointIndex = LeftHandPinky3 = 44 -jointIndex = RightToeBase = 4 -jointIndex = RightHandMiddle3 = 28 diff --git a/scripts/developer/facialExpressions.js b/scripts/developer/facialExpressions.js index 84e3966c4a..37a4f4f796 100644 --- a/scripts/developer/facialExpressions.js +++ b/scripts/developer/facialExpressions.js @@ -1,7 +1,7 @@ // // facialExpressions.js // A script to set different emotions using blend shapes -// +// // Author: Elisa Lupin-Jimenez // Copyright High Fidelity 2018 // @@ -286,7 +286,7 @@ } } for (var blendshape in emotion) { - MyAvatar.setBlendshape(blendshape, + MyAvatar.setBlendshape(blendshape, mixValue(lastEmotionUsed[blendshape], emotion[blendshape], changingEmotionPercentage)); } }); From 67fc7b90b5da4e51c1d9d4e4218a46e206ebde80 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 7 Jun 2018 15:43:21 -0700 Subject: [PATCH 43/78] fixed get and set semi nibble to deal with 16 bits --- libraries/avatars-renderer/src/avatars-renderer/Head.cpp | 3 +-- libraries/shared/src/SharedUtil.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 50f20918fa..598bc2b21a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -59,7 +59,6 @@ void Head::simulate(float deltaTime) { _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); } - //if (!_isFaceTrackerConnected) { if (!_isEyeTrackerConnected) { // Update eye saccades const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; @@ -77,7 +76,7 @@ void Head::simulate(float deltaTime) { } else { _saccade = glm::vec3(); } - + const float BLINK_SPEED = 10.0f; const float BLINK_SPEED_VARIABILITY = 1.0f; const float BLINK_START_VARIABILITY = 0.25f; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index c0203106c0..bb22a1e753 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -298,11 +298,11 @@ void setAtBit(unsigned char& byte, int bitIndex) { } bool oneAtBit16(unsigned short word, int bitIndex) { - return (word >> (16 - bitIndex) & 1); + return (word >> (15 - bitIndex) & 1); } void setAtBit16(unsigned short& word, int bitIndex) { - word |= (1 << (16 - bitIndex)); + word |= (1 << (15 - bitIndex)); } @@ -313,7 +313,7 @@ void clearAtBit(unsigned char& byte, int bitIndex) { } int getSemiNibbleAt(unsigned short word, int bitIndex) { - return (word >> (6 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11 + return (word >> (14 - bitIndex) & 3); // semi-nibbles store 00, 01, 10, or 11 } int getNthBit(unsigned char byte, int ordinal) { @@ -337,7 +337,7 @@ int getNthBit(unsigned char byte, int ordinal) { void setSemiNibbleAt(unsigned short& word, int bitIndex, int value) { //assert(value <= 3 && value >= 0); - word |= ((value & 3) << (6 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 + word |= ((value & 3) << (14 - bitIndex)); // semi-nibbles store 00, 01, 10, or 11 } bool isInEnvironment(const char* environment) { From 22f25835569b5d39dc45a1154c28a62cd02d8c11 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 7 Jun 2018 16:17:33 -0700 Subject: [PATCH 44/78] removed debug print statement in AvatarData.cpp --- libraries/avatars/src/AvatarData.cpp | 36 ---------------------------- 1 file changed, 36 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index b8ccbce962..b5186ba8f4 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1032,42 +1032,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (somethingChanged) { _additionalFlagsChanged = usecTimestampNow(); - if (newHasAudioEnabledFaceMovement) { - qCWarning(avatars) << "name " << getName() << "audio enabled flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "audio enabled flag is false"; - } - if (newHasProceduralEyeFaceMovement) { - qCWarning(avatars) << "name " << getName() << "eye face enabled flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "eye face flag is false"; - } - if (newHasProceduralBlinkFaceMovement) { - qCWarning(avatars) << "name " << getName() << "blink flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "blink flag is false"; - } - if (newFaceTrackerConnected) { - qCWarning(avatars) << "name " << getName() << "face tracker flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "face tracker flag is false"; - } - if (newEyeTrackerConnected) { - qCWarning(avatars) << "name " << getName() << "eye tracker flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "eye tracker flag is false"; - } - if (newHandState) { - qCWarning(avatars) << "name " << getName() << "hand state flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "hand state flag is false"; - } - if (newKeyState) { - qCWarning(avatars) << "name " << getName() << "key state flag is true"; - } else { - qCWarning(avatars) << "name " << getName() << "key state flag is false"; - } - } int numBytesRead = sourceBuffer - startSection; _additionalFlagsRate.increment(numBytesRead); From 3f5820266762f9962d9bb5bd91912272005e4f02 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 7 Jun 2018 17:41:12 -0700 Subject: [PATCH 45/78] updated the AvatarMixerPacketVersion to ProceduralFaceMovementFlagsAndBlendshapes in PacketHeaders.cpp --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index b69733c18d..c342ecffc5 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::FBXReaderNodeReparenting); + return static_cast(AvatarMixerPacketVersion::ProceduralFaceMovementFlagsAndBlendshapes); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 5203a9d178..43495dab07 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -282,7 +282,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { AvatarIdentityLookAtSnapping, UpdatedMannequinDefaultAvatar, AvatarJointDefaultPoseFlags, - FBXReaderNodeReparenting + FBXReaderNodeReparenting, + ProceduralFaceMovementFlagsAndBlendshapes }; enum class DomainConnectRequestVersion : PacketVersion { From 83e1db14cffb882c440b47f6f792249b2e1786ea Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 7 Jun 2018 18:56:23 -0700 Subject: [PATCH 46/78] Set UAL default value earlier to be caught by crashpad --- interface/src/Application.cpp | 10 +--------- interface/src/main.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7ae34e2f51..60d5cf20f3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1443,17 +1443,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // add firstRun flag from settings to launch event Setting::Handle firstRun { Settings::firstRun, true }; - // once the settings have been loaded, check if we need to flip the default for UserActivityLogger - auto& userActivityLogger = UserActivityLogger::getInstance(); - if (!userActivityLogger.isDisabledSettingSet()) { - // the user activity logger is opt-out for Interface - // but it's defaulted to disabled for other targets - // so we need to enable it here if it has never been disabled by the user - userActivityLogger.disable(false); - } - QString machineFingerPrint = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint()); + auto& userActivityLogger = UserActivityLogger::getInstance(); if (userActivityLogger.isEnabled()) { // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 22db128f7e..cd8c052d63 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -81,6 +81,13 @@ int main(int argc, const char* argv[]) { // Instance UserActivityLogger now that the settings are loaded auto& ual = UserActivityLogger::getInstance(); + // once the settings have been loaded, check if we need to flip the default for UserActivityLogger + if (!ual.isDisabledSettingSet()) { + // the user activity logger is opt-out for Interface + // but it's defaulted to disabled for other targets + // so we need to enable it here if it has never been disabled by the user + ual.disable(false); + } qDebug() << "UserActivityLogger is enabled:" << ual.isEnabled(); if (ual.isEnabled()) { From b1c578ecaa159536e814a0d528b3328269aa6e1f Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 7 Jun 2018 22:13:53 -0700 Subject: [PATCH 47/78] fixed white space in head.cpp --- .../src/avatars-renderer/Head.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 598bc2b21a..5800c1404b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -95,19 +95,19 @@ void Head::simulate(float deltaTime) { } if (_leftEyeBlinkVelocity == 0.0f && _rightEyeBlinkVelocity == 0.0f) { - // no blinking when brows are raised; blink less with increasing loudness - const float BASE_BLINK_RATE = 15.0f / 60.0f; - const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; - if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * - ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { - _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; - if (randFloat() < 0.5f) { - _leftEyeBlink = BLINK_START_VARIABILITY; - } else { - _rightEyeBlink = BLINK_START_VARIABILITY; - } + // no blinking when brows are raised; blink less with increasing loudness + const float BASE_BLINK_RATE = 15.0f / 60.0f; + const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; + if (forceBlink || (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * + ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { + _leftEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + _rightEyeBlinkVelocity = BLINK_SPEED + randFloat() * BLINK_SPEED_VARIABILITY; + if (randFloat() < 0.5f) { + _leftEyeBlink = BLINK_START_VARIABILITY; + } else { + _rightEyeBlink = BLINK_START_VARIABILITY; } + } } else { _leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); _rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED); From dabcd4c234a58b118e0e5c01be336face48cca25 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 8 Jun 2018 10:19:33 -0700 Subject: [PATCH 48/78] fixed bracket spacing in HeadData.h --- libraries/avatars/src/HeadData.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index e8289269d8..f9c4b52139 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -102,8 +102,8 @@ protected: quint64 _lookAtPositionChanged { 0 }; bool _hasAudioEnabledFaceMovement { true }; - bool _hasProceduralBlinkFaceMovement{ true }; - bool _hasProceduralEyeFaceMovement{ true }; + bool _hasProceduralBlinkFaceMovement { true }; + bool _hasProceduralEyeFaceMovement { true }; bool _isFaceTrackerConnected { false }; bool _isEyeTrackerConnected { false }; float _leftEyeBlink { 0.0f }; From faafd26d2a77228c4872f885d15df6769f11f2a1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 May 2018 16:50:32 -0700 Subject: [PATCH 49/78] Add uncompressed KTX files to baked texture output --- assignment-client/src/assets/AssetServer.cpp | 18 ---- assignment-client/src/assets/AssetServer.h | 5 - libraries/baking/src/TextureBaker.cpp | 98 +++++++++++++------ libraries/image/src/image/Image.cpp | 88 ++++++++--------- libraries/image/src/image/Image.h | 56 +++++------ libraries/ktx/src/TextureMeta.cpp | 4 + libraries/ktx/src/TextureMeta.h | 1 + .../src/model-networking/TextureCache.cpp | 23 ++++- libraries/shared/src/OwningBuffer.h | 29 ++++++ tools/oven/src/Oven.cpp | 6 -- 10 files changed, 190 insertions(+), 138 deletions(-) create mode 100644 libraries/shared/src/OwningBuffer.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 22ed01fd00..e0c35b7148 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -291,18 +291,6 @@ AssetServer::AssetServer(ReceivedMessage& message) : _bakingTaskPool(this), _filesizeLimit(AssetUtils::MAX_UPLOAD_SIZE) { - // store the current state of image compression so we can reset it when this assignment is complete - _wasColorTextureCompressionEnabled = image::isColorTexturesCompressionEnabled(); - _wasGrayscaleTextureCompressionEnabled = image::isGrayscaleTexturesCompressionEnabled(); - _wasNormalTextureCompressionEnabled = image::isNormalTexturesCompressionEnabled(); - _wasCubeTextureCompressionEnabled = image::isCubeTexturesCompressionEnabled(); - - // enable compression in image library - image::setColorTexturesCompressionEnabled(true); - image::setGrayscaleTexturesCompressionEnabled(true); - image::setNormalTexturesCompressionEnabled(true); - image::setCubeTexturesCompressionEnabled(true); - BAKEABLE_TEXTURE_EXTENSIONS = image::getSupportedFormats(); qDebug() << "Supported baking texture formats:" << BAKEABLE_MODEL_EXTENSIONS; @@ -354,12 +342,6 @@ void AssetServer::aboutToFinish() { while (_pendingBakes.size() > 0) { QCoreApplication::processEvents(); } - - // re-set defaults in image library - image::setColorTexturesCompressionEnabled(_wasCubeTextureCompressionEnabled); - image::setGrayscaleTexturesCompressionEnabled(_wasGrayscaleTextureCompressionEnabled); - image::setNormalTexturesCompressionEnabled(_wasNormalTextureCompressionEnabled); - image::setCubeTexturesCompressionEnabled(_wasCubeTextureCompressionEnabled); } void AssetServer::run() { diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 96a220d64d..b3d0f18a8f 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -167,11 +167,6 @@ private: using RequestQueue = QVector, SharedNodePointer>>; RequestQueue _queuedRequests; - bool _wasColorTextureCompressionEnabled { false }; - bool _wasGrayscaleTextureCompressionEnabled { false }; - bool _wasNormalTextureCompressionEnabled { false }; - bool _wasCubeTextureCompressionEnabled { false }; - uint64_t _filesizeLimit; }; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index b6957a2712..cadc1a2d7e 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include "ModelBakingLoggingCategory.h" const QString BAKED_TEXTURE_KTX_EXT = ".ktx"; @@ -124,47 +126,51 @@ void TextureBaker::processTexture() { TextureMeta meta; + auto originalCopyFilePath = _outputDirectory.absoluteFilePath(_textureURL.fileName()); { - auto filePath = _outputDirectory.absoluteFilePath(_textureURL.fileName()); - QFile file { filePath }; + QFile file { originalCopyFilePath }; if (!file.open(QIODevice::WriteOnly) || file.write(_originalTexture) == -1) { handleError("Could not write original texture for " + _textureURL.toString()); return; } - _outputFiles.push_back(filePath); + _originalTexture.clear(); + _outputFiles.push_back(originalCopyFilePath); meta.original = _metaTexturePathPrefix +_textureURL.fileName(); } - // IMPORTANT: _originalTexture is empty past this point - auto processedTexture = image::processImage(std::move(_originalTexture), _textureURL.toString().toStdString(), - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, _abortProcessing); - processedTexture->setSourceHash(hash); - - if (shouldStop()) { + auto buffer = std::shared_ptr((QIODevice*)new QFile(originalCopyFilePath)); + if (!buffer->open(QIODevice::ReadOnly)) { + handleError("Could not open original file at " + originalCopyFilePath); return; } - if (!processedTexture) { - handleError("Could not process texture " + _textureURL.toString()); - return; - } - - - auto memKTX = gpu::Texture::serialize(*processedTexture); - - if (!memKTX) { - handleError("Could not serialize " + _textureURL.toString() + " to KTX"); - return; - } - - const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat()); - if (name == nullptr) { - handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString()); - return; - } - - // attempt to write the baked texture to the destination file path + // Compressed KTX { + // IMPORTANT: _originalTexture is empty past this point + auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, _abortProcessing); + if (!processedTexture) { + handleError("Could not process texture " + _textureURL.toString()); + return; + } + processedTexture->setSourceHash(hash); + + if (shouldStop()) { + return; + } + + auto memKTX = gpu::Texture::serialize(*processedTexture); + if (!memKTX) { + handleError("Could not serialize " + _textureURL.toString() + " to KTX"); + return; + } + + const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat()); + if (name == nullptr) { + handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString()); + return; + } + const char* data = reinterpret_cast(memKTX->_storage->data()); const size_t length = memKTX->_storage->size(); @@ -179,6 +185,40 @@ void TextureBaker::processTexture() { meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName; } + // Uncompressed KTX + { + buffer->reset(); + auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, _abortProcessing); + if (!processedTexture) { + handleError("Could not process texture " + _textureURL.toString()); + return; + } + processedTexture->setSourceHash(hash); + + if (shouldStop()) { + return; + } + + auto memKTX = gpu::Texture::serialize(*processedTexture); + if (!memKTX) { + handleError("Could not serialize " + _textureURL.toString() + " to KTX"); + return; + } + + const char* data = reinterpret_cast(memKTX->_storage->data()); + const size_t length = memKTX->_storage->size(); + + auto fileName = _baseFilename + ".ktx"; + auto filePath = _outputDirectory.absoluteFilePath(fileName); + QFile bakedTextureFile { filePath }; + if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { + handleError("Could not write baked texture for " + _textureURL.toString()); + return; + } + _outputFiles.push_back(filePath); + meta.uncompressed = _metaTexturePathPrefix + fileName; + } { auto data = meta.serialize(); diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 63a4725f64..7faf811dec 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -126,63 +126,63 @@ TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, con } gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, true, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); } gpu::TexturePointer TextureUsage::create2DTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureColorFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, true, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureNormalMapFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); } gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, true, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); } gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } gpu::TexturePointer TextureUsage::createCubeTextureFromImage(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, true, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, true, abortProcessing); } gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(QImage&& srcImage, const std::string& srcImageName, - const std::atomic& abortProcessing) { - return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, false, abortProcessing); + bool compress, const std::atomic& abortProcessing) { + return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } @@ -253,17 +253,11 @@ uint32 packR11G11B10F(const glm::vec3& color) { return glm::packF2x11_1x10(ucolor); } -QImage processRawImageData(QByteArray&& content, const std::string& filename) { - // Take a local copy to force move construction - // https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f18-for-consume-parameters-pass-by-x-and-stdmove-the-parameter - QByteArray localCopy = std::move(content); - +QImage processRawImageData(QIODevice& content, const std::string& filename) { // Help the QImage loader by extracting the image file format from the url filename ext. // Some tga are not created properly without it. auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); - QBuffer buffer; - buffer.setData(localCopy); - QImageReader imageReader(&buffer, filenameExtension.c_str()); + QImageReader imageReader(&content, filenameExtension.c_str()); if (imageReader.canRead()) { return imageReader.read(); @@ -271,8 +265,8 @@ QImage processRawImageData(QByteArray&& content, const std::string& filename) { // Extension could be incorrect, try to detect the format from the content QImageReader newImageReader; newImageReader.setDecideFormatFromContent(true); - buffer.setData(localCopy); - newImageReader.setDevice(&buffer); + content.reset(); + newImageReader.setDevice(&content); if (newImageReader.canRead()) { qCWarning(imagelogging) << "Image file" << filename.c_str() << "has extension" << filenameExtension.c_str() @@ -284,11 +278,14 @@ QImage processRawImageData(QByteArray&& content, const std::string& filename) { return QImage(); } -gpu::TexturePointer processImage(QByteArray&& content, const std::string& filename, +gpu::TexturePointer processImage(std::shared_ptr content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType, - const std::atomic& abortProcessing) { + bool compress, const std::atomic& abortProcessing) { - QImage image = processRawImageData(std::move(content), filename); + QImage image = processRawImageData(*content.get(), filename); + // Texture content can take up a lot of memory. Here we release our ownership of that content + // in case it can be released. + content.reset(); int imageWidth = image.width(); int imageHeight = image.height(); @@ -314,7 +311,7 @@ gpu::TexturePointer processImage(QByteArray&& content, const std::string& filena } auto loader = TextureUsage::getTextureLoaderForType(textureType); - auto texture = loader(std::move(image), filename, abortProcessing); + auto texture = loader(std::move(image), filename, compress, abortProcessing); return texture; } @@ -804,7 +801,7 @@ void processTextureAlpha(const QImage& srcImage, bool& validAlpha, bool& alphaAs validAlpha = (numOpaques != NUM_PIXELS); } -gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, +gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, bool isStrict, const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage"); QImage image = processSourceImage(std::move(srcImage), false); @@ -825,7 +822,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma if ((image.width() > 0) && (image.height() > 0)) { gpu::Element formatMip; gpu::Element formatGPU; - if (isColorTexturesCompressionEnabled()) { + if (compress) { if (validAlpha) { // NOTE: This disables BC1a compression because it was producing odd artifacts on text textures // for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts). @@ -941,7 +938,8 @@ QImage processBumpMap(QImage&& image) { return result; } gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, - bool isBumpMap, const std::atomic& abortProcessing) { + bool compress, bool isBumpMap, + const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage"); QImage image = processSourceImage(std::move(srcImage), false); @@ -959,6 +957,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr gpu::Element formatMip; gpu::Element formatGPU; if (isNormalTexturesCompressionEnabled()) { + if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; } else { #ifdef USE_GLES @@ -980,7 +979,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr } gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, - bool isInvertedPixels, + bool compress, bool isInvertedPixels, const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "process2DTextureGrayscaleFromImage"); QImage image = processSourceImage(std::move(srcImage), false); @@ -999,6 +998,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr gpu::Element formatMip; gpu::Element formatGPU; if (isGrayscaleTexturesCompressionEnabled()) { + if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; } else { #ifdef USE_GLES @@ -1345,7 +1345,7 @@ QImage convertToHDRFormat(QImage&& srcImage, gpu::Element format) { } gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, - bool generateIrradiance, + bool compress, bool generateIrradiance, const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage"); @@ -1373,7 +1373,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(QImage&& srcI gpu::Element formatMip; gpu::Element formatGPU; - if (isCubeTexturesCompressionEnabled()) { + if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_HDR_RGB; } else { #ifdef USE_GLES diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 39f5ea3bab..3dca8f5586 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -41,60 +41,50 @@ enum Type { UNUSED_TEXTURE }; -using TextureLoader = std::function&)>; +using TextureLoader = std::function&)>; TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap()); gpu::TexturePointer create2DTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createStrict2DTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createAlbedoTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createEmissiveTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createNormalTextureFromNormalImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createNormalTextureFromBumpImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createRoughnessTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createRoughnessTextureFromGlossImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createMetallicTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createCubeTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); gpu::TexturePointer createLightmapTextureFromImage(QImage&& image, const std::string& srcImageName, - const std::atomic& abortProcessing); + bool compress, const std::atomic& abortProcessing); -gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool isStrict, - const std::atomic& abortProcessing); -gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool isBumpMap, - const std::atomic& abortProcessing); -gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool isInvertedPixels, - const std::atomic& abortProcessing); -gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool generateIrradiance, - const std::atomic& abortProcessing); +gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, + bool isStrict, const std::atomic& abortProcessing); +gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, + bool isBumpMap, const std::atomic& abortProcessing); +gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, + bool isInvertedPixels, const std::atomic& abortProcessing); +gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress, + bool generateIrradiance, const std::atomic& abortProcessing); } // namespace TextureUsage const QStringList getSupportedFormats(); -bool isColorTexturesCompressionEnabled(); -bool isNormalTexturesCompressionEnabled(); -bool isGrayscaleTexturesCompressionEnabled(); -bool isCubeTexturesCompressionEnabled(); - -void setColorTexturesCompressionEnabled(bool enabled); -void setNormalTexturesCompressionEnabled(bool enabled); -void setGrayscaleTexturesCompressionEnabled(bool enabled); -void setCubeTexturesCompressionEnabled(bool enabled); - -gpu::TexturePointer processImage(QByteArray&& content, const std::string& url, +gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType, - const std::atomic& abortProcessing = false); + bool compress = true, const std::atomic& abortProcessing = false); } // namespace image diff --git a/libraries/ktx/src/TextureMeta.cpp b/libraries/ktx/src/TextureMeta.cpp index 3a2abf24c4..c8427c1f60 100644 --- a/libraries/ktx/src/TextureMeta.cpp +++ b/libraries/ktx/src/TextureMeta.cpp @@ -33,6 +33,9 @@ bool TextureMeta::deserialize(const QByteArray& data, TextureMeta* meta) { if (root.contains("original")) { meta->original = root["original"].toString(); } + if (root.contains("uncompressed")) { + meta->uncompressed = root["uncompressed"].toString(); + } if (root.contains("compressed")) { auto compressed = root["compressed"].toObject(); for (auto it = compressed.constBegin(); it != compressed.constEnd(); it++) { @@ -57,6 +60,7 @@ QByteArray TextureMeta::serialize() { compressed[name] = kv.second.toString(); } root["original"] = original.toString(); + root["uncompressed"] = uncompressed.toString(); root["compressed"] = compressed; doc.setObject(root); diff --git a/libraries/ktx/src/TextureMeta.h b/libraries/ktx/src/TextureMeta.h index 6582c29e70..5450fee110 100644 --- a/libraries/ktx/src/TextureMeta.h +++ b/libraries/ktx/src/TextureMeta.h @@ -35,6 +35,7 @@ struct TextureMeta { QByteArray serialize(); QUrl original; + QUrl uncompressed; std::unordered_map availableTextureTypes; }; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index ed21fd35bc..40b31cac53 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -50,6 +50,8 @@ #include +#include + Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw") Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx") @@ -277,7 +279,7 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::Te return nullptr; } auto loader = image::TextureUsage::getTextureLoaderForType(type, options); - return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false)); + return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false, false)); } QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, @@ -964,7 +966,6 @@ void NetworkTexture::loadMetaContent(const QByteArray& content) { return; } - auto& backend = DependencyManager::get()->getGPUContext()->getBackend(); for (auto pair : meta.availableTextureTypes) { gpu::Element elFormat; @@ -990,6 +991,21 @@ void NetworkTexture::loadMetaContent(const QByteArray& content) { } } +#ifndef Q_OS_ANDROID + if (!meta.uncompressed.isEmpty()) { + _currentlyLoadingResourceType = ResourceType::KTX; + _activeUrl = _activeUrl.resolved(meta.uncompressed); + + auto textureCache = DependencyManager::get(); + auto self = _self.lock(); + if (!self) { + return; + } + QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection); + return; + } +#endif + if (!meta.original.isEmpty()) { _currentlyLoadingResourceType = ResourceType::ORIGINAL; _activeUrl = _activeUrl.resolved(meta.original); @@ -1143,7 +1159,8 @@ void ImageReader::read() { PROFILE_RANGE_EX(resource_parse_image_raw, __FUNCTION__, 0xffff0000, 0); // IMPORTANT: _content is empty past this point - texture = image::processImage(std::move(_content), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType()); + auto buffer = std::shared_ptr((QIODevice*)new OwningBuffer(std::move(_content))); + texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType()); if (!texture) { qCWarning(modelnetworking) << "Could not process:" << _url; diff --git a/libraries/shared/src/OwningBuffer.h b/libraries/shared/src/OwningBuffer.h new file mode 100644 index 0000000000..80184286bc --- /dev/null +++ b/libraries/shared/src/OwningBuffer.h @@ -0,0 +1,29 @@ +// +// OwningBuffer.h +// shared/src +// +// Created by Ryan Huffman on 5/31/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_OwningBuffer_h +#define hifi_OwningBuffer_h + +#include +class OwningBuffer : public QBuffer { +public: + OwningBuffer(const QByteArray& content) : _content(content) { + setData(_content); + } + OwningBuffer(QByteArray&& content) : _content(std::move(content)) { + setData(_content); + } + +private: + QByteArray _content; +}; + +#endif // hifi_OwningBuffer_h diff --git a/tools/oven/src/Oven.cpp b/tools/oven/src/Oven.cpp index c3fec2d15e..52b6db1aa5 100644 --- a/tools/oven/src/Oven.cpp +++ b/tools/oven/src/Oven.cpp @@ -25,12 +25,6 @@ Oven* Oven::_staticInstance { nullptr }; Oven::Oven() { _staticInstance = this; - // enable compression in image library - image::setColorTexturesCompressionEnabled(true); - image::setGrayscaleTexturesCompressionEnabled(true); - image::setNormalTexturesCompressionEnabled(true); - image::setCubeTexturesCompressionEnabled(true); - // setup our worker threads setupWorkerThreads(QThread::idealThreadCount()); From e82edb8b66f521322a4bc2e056899ccd61dc02ea Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 6 Jun 2018 15:42:10 -0700 Subject: [PATCH 50/78] add domain packet dissector, improve entity dissector --- tools/dissectors/hf-domain.lua | 23 ++++++++++ tools/dissectors/hf-entity.lua | 81 +++++++++++++++++++++++++++++----- tools/dissectors/hfudt.lua | 65 ++++++++++++++++----------- 3 files changed, 133 insertions(+), 36 deletions(-) create mode 100644 tools/dissectors/hf-domain.lua diff --git a/tools/dissectors/hf-domain.lua b/tools/dissectors/hf-domain.lua new file mode 100644 index 0000000000..093026bc92 --- /dev/null +++ b/tools/dissectors/hf-domain.lua @@ -0,0 +1,23 @@ +-- create the domain protocol +p_hf_domain = Proto("hf-domain", "HF Domain Protocol") + +-- domain packet fields +local f_domain_id = ProtoField.guid("hf_domain.domain_id", "Domain ID") +local f_domain_local_id = ProtoField.uint16("hf_domain.domain_local_id", "Domain Local ID") + +p_hf_domain.fields = { + f_domain_id, f_domain_local_id +} + +function p_hf_domain.dissector(buf, pinfo, tree) + pinfo.cols.protocol = p_hf_domain.name + + domain_subtree = tree:add(p_hf_domain, buf()) + + local i = 0 + + domain_subtree:add(f_domain_id, buf(i, 16)) + i = i + 16 + + domain_subtree:add_le(f_domain_local_id, buf(i, 2)) +end diff --git a/tools/dissectors/hf-entity.lua b/tools/dissectors/hf-entity.lua index f4de5a995d..51daa3497d 100644 --- a/tools/dissectors/hf-entity.lua +++ b/tools/dissectors/hf-entity.lua @@ -4,11 +4,21 @@ p_hf_entity = Proto("hf-entity", "HF Entity Protocol") -- entity packet fields local f_entity_sequence_number = ProtoField.uint16("hf_entity.sequence_number", "Sequence Number") local f_entity_timestamp = ProtoField.uint64("hf_entity.timestamp", "Timestamp") -local f_octal_code_bytes = ProtoField.uint8("hf_entity.octal_code_bytes", "Octal Code Bytes") +local f_octal_code_three_bit_sections = ProtoField.uint8("hf_entity.octal_code_three_bit_sections", "Octal Code Three Bit Sections") +local f_octal_code = ProtoField.bytes("hf_entity.octal_code", "Octal Code") local f_entity_id = ProtoField.guid("hf_entity.entity_id", "Entity ID") +local f_last_edited = ProtoField.uint64("hf_entity.last_edited", "Last Edited") +local f_coded_property_type = ProtoField.bytes("hf_entity.coded_property_type", "Coded Property Type") +local f_property_type = ProtoField.uint32("hf_entity.property_type", "Property Type") +local f_coded_update_delta = ProtoField.bytes("hf_entity.f_coded_update_delta", "Coded Update Delta") +local f_update_delta = ProtoField.uint32("hf_entity.update_delta", "Update Delta") p_hf_entity.fields = { - f_entity_sequence_number, f_entity_timestamp, f_octal_code_bytes, f_entity_id + f_entity_sequence_number, f_entity_timestamp, + f_octal_code_three_bit_sections, f_octal_code, + f_last_edited, f_entity_id, + f_coded_property_type, f_property_type, + f_coded_update_delta, f_update_delta } function p_hf_entity.dissector(buf, pinfo, tree) @@ -16,21 +26,72 @@ function p_hf_entity.dissector(buf, pinfo, tree) entity_subtree = tree:add(p_hf_entity, buf()) - i = 0 + local i = 0 entity_subtree:add_le(f_entity_sequence_number, buf(i, 2)) i = i + 2 - entity_subtree:add_le(f_entity_timestamp, buf(i, 4)) - i = i + 4 + entity_subtree:add_le(f_entity_timestamp, buf(i, 8)) + i = i + 8 - -- figure out the number of bytes the octal code takes - local octal_code_bytes = buf(i, 1):le_uint() - entity_subtree:add_le(f_octal_code_bytes, buf(i, 1)) + -- figure out the number of three bit sections in the octal code + local octal_code_three_bit_sections = buf(i, 1):le_uint() + entity_subtree:add_le(f_octal_code_three_bit_sections, buf(i, 1)) + i = i + 1 - -- skip over the octal code - i = i + 1 + octal_code_bytes + -- read the bytes for the octal code + local octal_code_bytes = math.ceil((octal_code_three_bit_sections * 3) / 8) + entity_subtree:add_le(f_octal_code, buf(i, octal_code_bytes)) + i = i + octal_code_bytes + + -- read the last edited timestamp + entity_subtree:add_le(f_last_edited, buf(i, 8)) + i = i + 8 -- read the entity ID entity_subtree:add(f_entity_id, buf(i, 16)) + i = i + 16 + + -- figure out the property type and the size of the coded value + local property_type, coded_property_bytes = number_of_coded_bytes(buf(i)) + entity_subtree:add(f_coded_property_type, buf(i, coded_property_bytes)) + entity_subtree:add(f_property_type, property_type) + i = i + coded_property_bytes + + -- figure out the update delta and the size of the coded value + local update_delta, coded_update_delta_bytes = number_of_coded_bytes(buf(i)) + entity_subtree:add(f_coded_update_delta, buf(i, coded_update_delta_bytes)) + entity_subtree:add(f_update_delta, update_delta) + i = i + coded_update_delta_bytes +end + +function number_of_coded_bytes(buf) + local coded_buffer = buf(0, 4):le_uint() -- max 64 bit value means max 10 header bits + + -- first figure out the total number of bytes for the coded value based + -- on the bits in the header + local total_coded_bytes = 1 + + for bit = 0, 10, 1 do + local header_bit = bit32.extract(coded_buffer, bit) + + if header_bit == 1 then + total_coded_bytes = total_coded_bytes + 1 + else + break + end + end + + -- pull out the bits and write them to our decoded value + local decoded_value = 0 + local decoded_position = 0 + local total_bits = total_coded_bytes * 8 + + for bit = total_coded_bytes, total_bits - 1, 1 do + local value_bit = bit32.extract(coded_buffer, total_bits - bit - 1) + decoded_value = bit32.replace(decoded_value, value_bit, decoded_position) + decoded_position = decoded_position + 1 + end + + return decoded_value, total_coded_bytes end diff --git a/tools/dissectors/hfudt.lua b/tools/dissectors/hfudt.lua index 9d2df801b2..c8b1d9feee 100644 --- a/tools/dissectors/hfudt.lua +++ b/tools/dissectors/hfudt.lua @@ -118,6 +118,10 @@ local packet_types = { [54] = "AssetGetInfoReply" } +local unsourced_packet_types = { + ["DomainList"] = true +} + function p_hfudt.dissector(buf, pinfo, tree) -- make sure this isn't a STUN packet - those don't follow HFUDT format @@ -230,54 +234,63 @@ function p_hfudt.dissector(buf, pinfo, tree) -- if the message bit is set, handle the second word if message_bit == 1 then - payload_offset = 12 + payload_offset = 12 - local second_word = buf(4, 4):le_uint() + local second_word = buf(4, 4):le_uint() - -- read message position from upper 2 bits - local message_position = bit32.rshift(second_word, 30) - local position = subtree:add(f_message_position, message_position) + -- read message position from upper 2 bits + local message_position = bit32.rshift(second_word, 30) + local position = subtree:add(f_message_position, message_position) - if message_positions[message_position] ~= nil then - -- if we know this position then add the name - position:append_text(" (".. message_positions[message_position] .. ")") - end + if message_positions[message_position] ~= nil then + -- if we know this position then add the name + position:append_text(" (".. message_positions[message_position] .. ")") + end - -- read message number from lower 30 bits - subtree:add(f_message_number, bit32.band(second_word, 0x3FFFFFFF)) + -- read message number from lower 30 bits + subtree:add(f_message_number, bit32.band(second_word, 0x3FFFFFFF)) - -- read the message part number - subtree:add(f_message_part_number, buf(8, 4):le_uint()) + -- read the message part number + subtree:add(f_message_part_number, buf(8, 4):le_uint()) end -- read the type local packet_type = buf(payload_offset, 1):le_uint() local ptype = subtree:add_le(f_type, buf(payload_offset, 1)) - if packet_types[packet_type] ~= nil then - subtree:add(f_type_text, packet_types[packet_type]) + local packet_type_text = packet_types[packet_type] + if packet_type_text ~= nil then + subtree:add(f_type_text, packet_type_text) -- if we know this packet type then add the name - ptype:append_text(" (".. packet_types[packet_type] .. ")") + ptype:append_text(" (".. packet_type_text .. ")") end - + -- read the version subtree:add_le(f_version, buf(payload_offset + 1, 1)) - -- read node local ID - local sender_id = buf(payload_offset + 2, 2) - subtree:add_le(f_sender_id, sender_id) + local i = payload_offset + 2 - local i = payload_offset + 4 + if unsourced_packet_types[packet_type_text] == nil then + -- read node local ID + local sender_id = buf(payload_offset + 2, 2) + subtree:add_le(f_sender_id, sender_id) + i = i + 2 - -- read HMAC MD5 hash - subtree:add(f_hmac_hash, buf(i, 16)) - i = i + 16 + -- read HMAC MD5 hash + subtree:add(f_hmac_hash, buf(i, 16)) + i = i + 16 + end + + -- Domain packets + if packet_type_text == "DomainList" then + Dissector.get("hf-domain"):call(buf(i):tvb(), pinfo, tree) + end -- AvatarData or BulkAvatarDataPacket - if packet_types[packet_type] == "AvatarData" or packet_types[packet_type] == "BulkAvatarDataPacket" then + if packet_type_text == "AvatarData" or packet_type_text == "BulkAvatarData" then Dissector.get("hf-avatar"):call(buf(i):tvb(), pinfo, tree) end - if packet_types[packet_type] == "EntityEdit" then + if packet_type_text == "EntityEdit" then Dissector.get("hf-entity"):call(buf(i):tvb(), pinfo, tree) end end From 24182d3451b828e659b55dd7199c807dc5a33a99 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Jun 2018 15:27:50 -0700 Subject: [PATCH 51/78] Remove compression variables & functions in image.cpp --- libraries/image/src/image/Image.cpp | 56 ----------------------------- 1 file changed, 56 deletions(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 7faf811dec..f812e515de 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -48,11 +48,6 @@ std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; static const auto HDR_FORMAT = gpu::Element::COLOR_R11G11B10; -static std::atomic compressColorTextures { false }; -static std::atomic compressNormalTextures { false }; -static std::atomic compressGrayscaleTextures { false }; -static std::atomic compressCubeTextures { false }; - uint rectifyDimension(const uint& dimension) { if (dimension == 0) { return 0; @@ -185,55 +180,6 @@ gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(QI return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, false, abortProcessing); } - -bool isColorTexturesCompressionEnabled() { -#if CPU_MIPMAPS - return compressColorTextures.load(); -#else - return false; -#endif -} - -bool isNormalTexturesCompressionEnabled() { -#if CPU_MIPMAPS - return compressNormalTextures.load(); -#else - return false; -#endif -} - -bool isGrayscaleTexturesCompressionEnabled() { -#if CPU_MIPMAPS - return compressGrayscaleTextures.load(); -#else - return false; -#endif -} - -bool isCubeTexturesCompressionEnabled() { -#if CPU_MIPMAPS - return compressCubeTextures.load(); -#else - return false; -#endif -} - -void setColorTexturesCompressionEnabled(bool enabled) { - compressColorTextures.store(enabled); -} - -void setNormalTexturesCompressionEnabled(bool enabled) { - compressNormalTextures.store(enabled); -} - -void setGrayscaleTexturesCompressionEnabled(bool enabled) { - compressGrayscaleTextures.store(enabled); -} - -void setCubeTexturesCompressionEnabled(bool enabled) { - compressCubeTextures.store(enabled); -} - static float denormalize(float value, const float minValue) { return value < minValue ? 0.0f : value; } @@ -956,7 +902,6 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr if ((image.width() > 0) && (image.height() > 0)) { gpu::Element formatMip; gpu::Element formatGPU; - if (isNormalTexturesCompressionEnabled()) { if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_XY; } else { @@ -997,7 +942,6 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr if ((image.width() > 0) && (image.height() > 0)) { gpu::Element formatMip; gpu::Element formatGPU; - if (isGrayscaleTexturesCompressionEnabled()) { if (compress) { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_RED; } else { From 3e65e0b0cd914b43d1aa640a6b7a91d0f52d663b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 8 Jun 2018 13:21:47 -0700 Subject: [PATCH 52/78] Update processImage to not compress by default --- libraries/image/src/image/Image.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 3dca8f5586..ccf4845fca 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -84,7 +84,7 @@ const QStringList getSupportedFormats(); gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType, - bool compress = true, const std::atomic& abortProcessing = false); + bool compress = false, const std::atomic& abortProcessing = false); } // namespace image From a2399ea1f637a798a80aaa788a753d7936d73e1e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 8 Jun 2018 16:07:01 -0700 Subject: [PATCH 53/78] keep small masses from introducing NaN into bullet's calculations --- libraries/physics/src/PhysicsEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 83ffa21a55..1a258c0fc3 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -105,6 +105,10 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { } case MOTION_TYPE_DYNAMIC: { mass = motionState->getMass(); + if (mass != mass || mass < 1.0f) { + qCDebug(physics) << "mass is too low, setting to 1.0 Kg --" << mass; + mass = 1.0f; + } btCollisionShape* shape = const_cast(motionState->getShape()); assert(shape); shape->calculateLocalInertia(mass, inertia); From 2143bae1007fe1552224e37697d03b4dda114874 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 5 Apr 2018 10:23:46 -0700 Subject: [PATCH 54/78] Remove unused entity server backups The domain server now handles entity backups so this code is no longer used. This also fixes a bug where, if the entity server went down while writing the persist file, it would leave a lock file behind. When starting up the next time it would try to recover from the last backup. Because the entity server no longer gets any backup rules, and the way it searches for backups, it would end up finding the lock file and treating it as the most recent backup. This meant that the models.json.gz file was replaced with an empty file. --- assignment-client/src/octree/OctreeServer.cpp | 43 +-- assignment-client/src/octree/OctreeServer.h | 4 - libraries/octree/src/OctreePersistThread.cpp | 301 +----------------- libraries/octree/src/OctreePersistThread.h | 24 +- 4 files changed, 16 insertions(+), 356 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index acf0abeed1..83fbf6f3d8 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1055,17 +1055,6 @@ void OctreeServer::readConfiguration() { readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval); qDebug() << "persistInterval=" << _persistInterval; - bool noBackup; - readOptionBool(QString("NoBackup"), settingsSectionObject, noBackup); - _wantBackup = !noBackup; - qDebug() << "wantBackup=" << _wantBackup; - - if (!readOptionString("backupDirectoryPath", settingsSectionObject, _backupDirectoryPath)) { - _backupDirectoryPath = ""; - } - - qDebug() << "backupDirectoryPath=" << _backupDirectoryPath; - readOptionBool(QString("persistFileDownload"), settingsSectionObject, _persistFileDownload); qDebug() << "persistFileDownload=" << _persistFileDownload; @@ -1288,38 +1277,10 @@ void OctreeServer::beginRunning(QByteArray replaceData) { } auto persistFileDirectory = QFileInfo(_persistAbsoluteFilePath).absolutePath(); - if (_backupDirectoryPath.isEmpty()) { - // Use the persist file's directory to store backups - _backupDirectoryPath = persistFileDirectory; - } else { - // The backup directory has been set. - // If relative, make it relative to the entities directory in the application data directory - // If absolute, no resolution is necessary - QDir backupDirectory { _backupDirectoryPath }; - QString absoluteBackupDirectory; - if (backupDirectory.isRelative()) { - absoluteBackupDirectory = QDir(PathUtils::getAppDataFilePath("entities/")).absoluteFilePath(_backupDirectoryPath); - absoluteBackupDirectory = QDir(absoluteBackupDirectory).absolutePath(); - } else { - absoluteBackupDirectory = backupDirectory.absolutePath(); - } - backupDirectory = QDir(absoluteBackupDirectory); - if (!backupDirectory.exists()) { - if (backupDirectory.mkpath(".")) { - qDebug() << "Created backup directory"; - } else { - qDebug() << "ERROR creating backup directory, using persist file directory"; - _backupDirectoryPath = persistFileDirectory; - } - } else { - _backupDirectoryPath = absoluteBackupDirectory; - } - } - qDebug() << "Backups will be stored in: " << _backupDirectoryPath; // now set up PersistThread - _persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _backupDirectoryPath, _persistInterval, - _wantBackup, _settings, _debugTimestampNow, _persistAsFileType, replaceData); + _persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _persistInterval, _debugTimestampNow, + _persistAsFileType, replaceData); _persistThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 87145dd46e..3595fe1129 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -190,7 +190,6 @@ protected: QString _persistFilePath; QString _persistAbsoluteFilePath; QString _persistAsFileType; - QString _backupDirectoryPath; int _packetsPerClientPerInterval; int _packetsTotalPerInterval; OctreePointer _tree; // this IS a reaveraging tree @@ -203,10 +202,7 @@ protected: OctreePersistThread* _persistThread; int _persistInterval; - bool _wantBackup; bool _persistFileDownload; - QString _backupExtensionFormat; - int _backupInterval; int _maxBackupVersions; time_t _started; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 3dc051675d..5f05c3cc85 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -37,25 +37,19 @@ const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds -OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, int persistInterval, - bool wantBackup, const QJsonObject& settings, bool debugTimestampNow, - QString persistAsFileType, const QByteArray& replacementData) : +OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval, + bool debugTimestampNow, QString persistAsFileType, const QByteArray& replacementData) : _tree(tree), _filename(filename), - _backupDirectory(backupDirectory), _persistInterval(persistInterval), _initialLoadComplete(false), _replacementData(replacementData), _loadTimeUSecs(0), _lastCheck(0), - _wantBackup(wantBackup), _debugTimestampNow(debugTimestampNow), _lastTimeDebug(0), _persistAsFileType(persistAsFileType) { - parseSettings(settings); - - // in case the persist filename has an extension that doesn't match the file type QString sansExt = fileNameWithoutExtension(_filename, PERSIST_EXTENSIONS); _filename = sansExt + "." + _persistAsFileType; @@ -70,72 +64,6 @@ QString OctreePersistThread::getPersistFileMimeType() const { return ""; } -void OctreePersistThread::parseSettings(const QJsonObject& settings) { - if (settings["backups"].isArray()) { - const QJsonArray& backupRules = settings["backups"].toArray(); - qCDebug(octree) << "BACKUP RULES:"; - - foreach (const QJsonValue& value, backupRules) { - - QJsonObject obj = value.toObject(); - - int interval = 0; - int count = 0; - - QJsonValue intervalVal = obj["backupInterval"]; - if (intervalVal.isString()) { - interval = intervalVal.toString().toInt(); - } else { - interval = intervalVal.toInt(); - } - - QJsonValue countVal = obj["maxBackupVersions"]; - if (countVal.isString()) { - count = countVal.toString().toInt(); - } else { - count = countVal.toInt(); - } - - qCDebug(octree) << " Name:" << obj["Name"].toString(); - qCDebug(octree) << " format:" << obj["format"].toString(); - qCDebug(octree) << " interval:" << interval; - qCDebug(octree) << " count:" << count; - - BackupRule newRule = { obj["Name"].toString(), interval, obj["format"].toString(), count, 0}; - - newRule.lastBackup = getMostRecentBackupTimeInUsecs(obj["format"].toString()); - - if (newRule.lastBackup > 0) { - quint64 now = usecTimestampNow(); - quint64 sinceLastBackup = now - newRule.lastBackup; - qCDebug(octree) << " lastBackup:" << qPrintable(formatUsecTime(sinceLastBackup)) << "ago"; - } else { - qCDebug(octree) << " lastBackup: NEVER"; - } - - _backupRules << newRule; - } - } else { - qCDebug(octree) << "BACKUP RULES: NONE"; - } -} - -quint64 OctreePersistThread::getMostRecentBackupTimeInUsecs(const QString& format) { - - quint64 mostRecentBackupInUsecs = 0; - - QString mostRecentBackupFileName; - QDateTime mostRecentBackupTime; - - bool recentBackup = getMostRecentBackup(format, mostRecentBackupFileName, mostRecentBackupTime); - - if (recentBackup) { - mostRecentBackupInUsecs = mostRecentBackupTime.toMSecsSinceEpoch() * USECS_PER_MSEC; - } - - return mostRecentBackupInUsecs; -} - void OctreePersistThread::replaceData(QByteArray data) { backupCurrentFile(); @@ -188,20 +116,7 @@ bool OctreePersistThread::process() { _tree->withWriteLock([&] { PerformanceWarning warn(true, "Loading Octree File", true); - // First check to make sure "lock" file doesn't exist. If it does exist, then - // our last save crashed during the save, and we want to load our most recent backup. - QString lockFileName = _filename + ".lock"; - std::ifstream lockFile(qPrintable(lockFileName), std::ios::in | std::ios::binary | std::ios::ate); - if (lockFile.is_open()) { - qCDebug(octree) << "WARNING: Octree lock file detected at startup:" << lockFileName; - - lockFile.close(); - qCDebug(octree) << "Removing lock file:" << lockFileName; - remove(qPrintable(lockFileName)); - qCDebug(octree) << "Lock file removed:" << lockFileName; - } - - persistentFileRead = _tree->readFromFile(qPrintable(_filename.toLocal8Bit())); + persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData()); _tree->pruneTree(); }); @@ -233,11 +148,6 @@ bool OctreePersistThread::process() { // Since we just loaded the persistent file, we can consider ourselves as having "just checked" for persistance. _lastCheck = usecTimestampNow(); // we just loaded, no need to save again - - // This last persist time is not really used until the file is actually persisted. It is only - // used in formatting the backup filename in cases of non-rolling backup names. However, we don't - // want an uninitialized value for this, so we set it to the current time (startup of the server) - time(&_lastPersistTime); if (_replacementData.isNull()) { sendLatestEntityDataToDS(); @@ -304,26 +214,21 @@ void OctreePersistThread::persist() { qCDebug(octree) << "DONE pruning Octree before saving..."; }); - qCDebug(octree) << "persist operation calling backup..."; - backup(); // handle backup if requested - qCDebug(octree) << "persist operation DONE with backup..."; - _tree->incrementPersistDataVersion(); // create our "lock" file to indicate we're saving. QString lockFileName = _filename + ".lock"; - std::ofstream lockFile(qPrintable(lockFileName), std::ios::out|std::ios::binary); + std::ofstream lockFile(lockFileName.toLocal8Bit().constData(), std::ios::out|std::ios::binary); if(lockFile.is_open()) { qCDebug(octree) << "saving Octree lock file created at:" << lockFileName; - _tree->writeToFile(qPrintable(_filename), NULL, _persistAsFileType); - time(&_lastPersistTime); + _tree->writeToFile(_filename.toLocal8Bit().constData(), NULL, _persistAsFileType); _tree->clearDirtyBit(); // tree is clean after saving qCDebug(octree) << "DONE saving Octree to file..."; lockFile.close(); qCDebug(octree) << "saving Octree lock file closed:" << lockFileName; - remove(qPrintable(lockFileName)); + remove(lockFileName.toLocal8Bit().constData()); qCDebug(octree) << "saving Octree lock file removed:" << lockFileName; } @@ -345,197 +250,3 @@ void OctreePersistThread::sendLatestEntityDataToDS() { qCWarning(octree) << "Failed to persist octree to DS"; } } - -void OctreePersistThread::restoreFromMostRecentBackup() { - qCDebug(octree) << "Restoring from most recent backup..."; - - QString mostRecentBackupFileName; - QDateTime mostRecentBackupTime; - - bool recentBackup = getMostRecentBackup(QString(""), mostRecentBackupFileName, mostRecentBackupTime); - - // If we found a backup file, restore from that file. - if (recentBackup) { - qCDebug(octree) << "BEST backup file:" << mostRecentBackupFileName << " last modified:" << mostRecentBackupTime.toString(); - - qCDebug(octree) << "Removing old file:" << _filename; - remove(qPrintable(_filename)); - - qCDebug(octree) << "Restoring backup file " << mostRecentBackupFileName << "..."; - bool result = QFile::copy(mostRecentBackupFileName, _filename); - if (result) { - qCDebug(octree) << "DONE restoring backup file " << mostRecentBackupFileName << "to" << _filename << "..."; - } else { - qCDebug(octree) << "ERROR while restoring backup file " << mostRecentBackupFileName << "to" << _filename << "..."; - perror("ERROR while restoring backup file"); - } - } else { - qCDebug(octree) << "NO BEST backup file found."; - } -} - -bool OctreePersistThread::getMostRecentBackup(const QString& format, - QString& mostRecentBackupFileName, QDateTime& mostRecentBackupTime) { - - // Based on our backup file name, determine the path and file name pattern for backup files - QFileInfo persistFileInfo(_filename); - QString path = _backupDirectory; - QString fileNamePart = persistFileInfo.fileName(); - - QStringList filters; - - if (format.isEmpty()) { - // Create a file filter that will find all backup files of this extension format - foreach(const BackupRule& rule, _backupRules) { - QString backupExtension = rule.extensionFormat; - backupExtension.replace(QRegExp("%."), "*"); - QString backupFileNamePart = fileNamePart + backupExtension; - filters << backupFileNamePart; - } - } else { - QString backupExtension = format; - backupExtension.replace(QRegExp("%."), "*"); - QString backupFileNamePart = fileNamePart + backupExtension; - filters << backupFileNamePart; - } - - bool bestBackupFound = false; - QString bestBackupFile; - QDateTime bestBackupFileTime; - - // Iterate over all of the backup files in the persist location - QDirIterator dirIterator(path, filters, QDir::Files|QDir::NoSymLinks, QDirIterator::NoIteratorFlags); - while(dirIterator.hasNext()) { - - dirIterator.next(); - QDateTime lastModified = dirIterator.fileInfo().lastModified(); - - // Based on last modified date, track the most recently modified file as the best backup - if (lastModified > bestBackupFileTime) { - bestBackupFound = true; - bestBackupFile = dirIterator.filePath(); - bestBackupFileTime = lastModified; - } - } - - // If we found a backup then return the results - if (bestBackupFound) { - mostRecentBackupFileName = bestBackupFile; - mostRecentBackupTime = bestBackupFileTime; - } - return bestBackupFound; -} - -void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { - - if (rule.extensionFormat.contains("%N")) { - if (rule.maxBackupVersions > 0) { - qCDebug(octree) << "Rolling old backup versions for rule" << rule.name << "..."; - - QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName(); - - // Delete maximum rolling file because rename() fails on Windows if target exists - QString backupMaxExtensionN = rule.extensionFormat; - backupMaxExtensionN.replace(QString("%N"), QString::number(rule.maxBackupVersions)); - QString backupMaxFilenameN = backupFileName + backupMaxExtensionN; - QFile backupMaxFileN(backupMaxFilenameN); - if (backupMaxFileN.exists()) { - int result = remove(qPrintable(backupMaxFilenameN)); - if (result != 0) { - qCDebug(octree) << "ERROR deleting old backup file " << backupMaxFilenameN; - } - } - - for(int n = rule.maxBackupVersions - 1; n > 0; n--) { - QString backupExtensionN = rule.extensionFormat; - QString backupExtensionNplusOne = rule.extensionFormat; - backupExtensionN.replace(QString("%N"), QString::number(n)); - backupExtensionNplusOne.replace(QString("%N"), QString::number(n+1)); - - QString backupFilenameN = findMostRecentFileExtension(backupFileName, PERSIST_EXTENSIONS) + backupExtensionN; - QString backupFilenameNplusOne = backupFileName + backupExtensionNplusOne; - - QFile backupFileN(backupFilenameN); - - if (backupFileN.exists()) { - qCDebug(octree) << "rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; - int result = rename(qPrintable(backupFilenameN), qPrintable(backupFilenameNplusOne)); - if (result == 0) { - qCDebug(octree) << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; - } else { - qCDebug(octree) << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; - perror("ERROR in rolling backup file"); - } - } - } - qCDebug(octree) << "Done rolling old backup versions..."; - } else { - qCDebug(octree) << "Rolling backups for rule" << rule.name << "." - << " Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]." - << " No need to roll backups..."; - } - } -} - -void OctreePersistThread::backup() { - qCDebug(octree) << "backup operation wantBackup:" << _wantBackup; - if (_wantBackup) { - quint64 now = usecTimestampNow(); - - for(int i = 0; i < _backupRules.count(); i++) { - BackupRule& rule = _backupRules[i]; - - quint64 sinceLastBackup = now - rule.lastBackup; - quint64 SECS_TO_USECS = 1000 * 1000; - quint64 intervalToBackup = rule.interval * SECS_TO_USECS; - - qCDebug(octree) << "Checking [" << rule.name << "] - Time since last backup [" << sinceLastBackup << "] " << - "compared to backup interval [" << intervalToBackup << "]..."; - - if (sinceLastBackup > intervalToBackup) { - qCDebug(octree) << "Time since last backup [" << sinceLastBackup << "] for rule [" << rule.name - << "] exceeds backup interval [" << intervalToBackup << "] doing backup now..."; - - struct tm* localTime = localtime(&_lastPersistTime); - - QString backupFileName = _backupDirectory + "/" + QUrl(_filename).fileName(); - - // check to see if they asked for version rolling format - if (rule.extensionFormat.contains("%N")) { - rollOldBackupVersions(rule); // rename all the old backup files accordingly - QString backupExtension = rule.extensionFormat; - backupExtension.replace(QString("%N"), QString("1")); - backupFileName += backupExtension; - } else { - char backupExtension[256]; - strftime(backupExtension, sizeof(backupExtension), qPrintable(rule.extensionFormat), localTime); - backupFileName += backupExtension; - } - - if (rule.maxBackupVersions > 0) { - QFile persistFile(_filename); - if (persistFile.exists()) { - qCDebug(octree) << "backing up persist file " << _filename << "to" << backupFileName << "..."; - bool result = QFile::copy(_filename, backupFileName); - if (result) { - qCDebug(octree) << "DONE backing up persist file..."; - rule.lastBackup = now; // only record successful backup in this case. - } else { - qCDebug(octree) << "ERROR in backing up persist file..."; - perror("ERROR in backing up persist file"); - } - } else { - qCDebug(octree) << "persist file " << _filename << " does not exist. " << - "nothing to backup for this rule ["<< rule.name << "]..."; - } - } else { - qCDebug(octree) << "This backup rule" << rule.name - << " has Max Rolled Backup Versions less than 1 [" << rule.maxBackupVersions << "]." - << " There are no backups to be done..."; - } - } else { - qCDebug(octree) << "Backup not needed for this rule ["<< rule.name << "]..."; - } - } - } -} diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index bde207001f..990a3b1fad 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -32,20 +32,22 @@ public: static const int DEFAULT_PERSIST_INTERVAL; - OctreePersistThread(OctreePointer tree, const QString& filename, const QString& backupDirectory, - int persistInterval = DEFAULT_PERSIST_INTERVAL, bool wantBackup = false, - const QJsonObject& settings = QJsonObject(), bool debugTimestampNow = false, - QString persistAsFileType = "json.gz", const QByteArray& replacementData = QByteArray()); + OctreePersistThread(OctreePointer tree, + const QString& filename, + int persistInterval = DEFAULT_PERSIST_INTERVAL, + bool debugTimestampNow = false, + QString persistAsFileType = "json.gz", + const QByteArray& replacementData = QByteArray()); bool isInitialLoadComplete() const { return _initialLoadComplete; } quint64 getLoadElapsedTime() const { return _loadTimeUSecs; } - void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist - QString getPersistFilename() const { return _filename; } QString getPersistFileMimeType() const; QByteArray getPersistFileContents() const; + void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist + signals: void loadCompleted(); @@ -54,12 +56,6 @@ protected: virtual bool process() override; void persist(); - void backup(); - void rollOldBackupVersions(const BackupRule& rule); - void restoreFromMostRecentBackup(); - bool getMostRecentBackup(const QString& format, QString& mostRecentBackupFileName, QDateTime& mostRecentBackupTime); - quint64 getMostRecentBackupTimeInUsecs(const QString& format); - void parseSettings(const QJsonObject& settings); bool backupCurrentFile(); void replaceData(QByteArray data); @@ -68,17 +64,13 @@ protected: private: OctreePointer _tree; QString _filename; - QString _backupDirectory; int _persistInterval; bool _initialLoadComplete; QByteArray _replacementData; quint64 _loadTimeUSecs; - time_t _lastPersistTime; quint64 _lastCheck; - bool _wantBackup; - QVector _backupRules; bool _debugTimestampNow; quint64 _lastTimeDebug; From d476146a56fab23afd4b01e33b86d35af690ca76 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 10 May 2018 10:47:49 -0700 Subject: [PATCH 55/78] Move OctreePersistThread off GenericThread and add octree negotiation --- assignment-client/src/octree/OctreeServer.cpp | 162 ++++---------- assignment-client/src/octree/OctreeServer.h | 26 +-- libraries/octree/src/OctreePersistThread.cpp | 205 ++++++++++++------ libraries/octree/src/OctreePersistThread.h | 19 +- 4 files changed, 208 insertions(+), 204 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 83fbf6f3d8..b8329b4d3e 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -231,18 +231,19 @@ void OctreeServer::trackProcessWaitTime(float time) { OctreeServer::OctreeServer(ReceivedMessage& message) : ThreadedAssignment(message), _argc(0), - _argv(NULL), - _parsedArgV(NULL), + _argv(nullptr), + _parsedArgV(nullptr), + _httpManager(nullptr), _statusPort(0), _packetsPerClientPerInterval(10), _packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL), - _tree(NULL), + _tree(nullptr), _wantPersist(true), _debugSending(false), _debugReceiving(false), _verboseDebug(false), - _octreeInboundPacketProcessor(NULL), - _persistThread(NULL), + _octreeInboundPacketProcessor(nullptr), + _persistManager(nullptr), _started(time(0)), _startedUSecs(usecTimestampNow()) { @@ -265,11 +266,8 @@ OctreeServer::~OctreeServer() { _octreeInboundPacketProcessor->deleteLater(); } - if (_persistThread) { - _persistThread->terminating(); - _persistThread->terminate(); - _persistThread->deleteLater(); - } + qDebug() << "Waiting for persist thread to come down"; + _persistThread.wait(); // cleanup our tree here... qDebug() << qPrintable(_safeServerName) << "server START cleaning up octree... [" << this << "]"; @@ -1117,111 +1115,14 @@ void OctreeServer::run() { } void OctreeServer::domainSettingsRequestComplete() { - if (_state != OctreeServerState::WaitingForDomainSettings) { - qCWarning(octree_server) << "Received domain settings after they have already been received"; - return; - } - auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket"); packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket"); - - packetReceiver.registerListener(PacketType::OctreeDataFileReply, this, "handleOctreeDataFileReply"); + packetReceiver.registerListener(getMyQueryMessageType(), this, "handleOctreeQueryPacket"); qDebug(octree_server) << "Received domain settings"; readConfiguration(); - _state = OctreeServerState::WaitingForOctreeDataNegotation; - - auto nodeList = DependencyManager::get(); - const DomainHandler& domainHandler = nodeList->getDomainHandler(); - - auto packet = NLPacket::create(PacketType::OctreeDataFileRequest, -1, true, false); - - OctreeUtils::RawOctreeData data; - qCDebug(octree_server) << "Reading octree data from" << _persistAbsoluteFilePath; - if (data.readOctreeDataInfoFromFile(_persistAbsoluteFilePath)) { - qCDebug(octree_server) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")"; - packet->writePrimitive(true); - auto id = data.id.toRfc4122(); - packet->write(id); - packet->writePrimitive(data.version); - } else { - qCWarning(octree_server) << "No octree data found"; - packet->writePrimitive(false); - } - - qCDebug(octree_server) << "Sending request for octree data to DS"; - nodeList->sendPacket(std::move(packet), domainHandler.getSockAddr()); -} - -void OctreeServer::handleOctreeDataFileReply(QSharedPointer message) { - if (_state != OctreeServerState::WaitingForOctreeDataNegotation) { - qCWarning(octree_server) << "Server received ocree data file reply but is not currently negotiating."; - return; - } - - bool includesNewData; - message->readPrimitive(&includesNewData); - QByteArray replaceData; - if (includesNewData) { - replaceData = message->readAll(); - qDebug() << "Got reply to octree data file request, new data sent"; - } else { - qDebug() << "Got reply to octree data file request, current entity data is sufficient"; - - OctreeUtils::RawEntityData data; - qCDebug(octree_server) << "Reading octree data from" << _persistAbsoluteFilePath; - if (data.readOctreeDataInfoFromFile(_persistAbsoluteFilePath)) { - if (data.id.isNull()) { - qCDebug(octree_server) << "Current octree data has a null id, updating"; - data.resetIdAndVersion(); - - QFile file(_persistAbsoluteFilePath); - if (file.open(QIODevice::WriteOnly)) { - auto entityData = data.toGzippedByteArray(); - file.write(entityData); - file.close(); - } else { - qCDebug(octree_server) << "Failed to update octree data"; - } - } - } - } - - _state = OctreeServerState::Running; - beginRunning(replaceData); -} - -void OctreeServer::beginRunning(QByteArray replaceData) { - if (_state != OctreeServerState::Running) { - qCWarning(octree_server) << "Server is not running"; - return; - } - - auto nodeList = DependencyManager::get(); - - // we need to ask the DS about agents so we can ping/reply with them - nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); - - beforeRun(); // after payload has been processed - - connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); - connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - -#ifndef WIN32 - setvbuf(stdout, NULL, _IOLBF, 0); -#endif - - nodeList->linkedDataCreateCallback = [this](Node* node) { - auto queryNodeData = createOctreeQueryNode(); - queryNodeData->init(); - node->setLinkedData(std::move(queryNodeData)); - }; - - srand((unsigned)time(0)); - // if we want Persistence, set up the local file and persist thread if (_wantPersist) { static const QString ENTITY_PERSIST_EXTENSION = ".json.gz"; @@ -1279,10 +1180,42 @@ void OctreeServer::beginRunning(QByteArray replaceData) { auto persistFileDirectory = QFileInfo(_persistAbsoluteFilePath).absolutePath(); // now set up PersistThread - _persistThread = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _persistInterval, _debugTimestampNow, - _persistAsFileType, replaceData); - _persistThread->initialize(true); + _persistManager = new OctreePersistThread(_tree, _persistAbsoluteFilePath, _persistInterval, _debugTimestampNow, + _persistAsFileType); + _persistManager->moveToThread(&_persistThread); + connect(&_persistThread, &QThread::finished, _persistManager, &QObject::deleteLater); + connect(&_persistThread, &QThread::started, _persistManager, &OctreePersistThread::start); + connect(_persistManager, &OctreePersistThread::loadCompleted, this, [this]() { + beginRunning(); + }); + _persistThread.start(); + } else { + beginRunning(); } +} + +void OctreeServer::beginRunning() { + auto nodeList = DependencyManager::get(); + + // we need to ask the DS about agents so we can ping/reply with them + nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); + + beforeRun(); // after payload has been processed + + connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); + connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); + +#ifndef WIN32 + setvbuf(stdout, nullptr, _IOLBF, 0); +#endif + + nodeList->linkedDataCreateCallback = [this](Node* node) { + auto queryNodeData = createOctreeQueryNode(); + queryNodeData->init(); + node->setLinkedData(std::move(queryNodeData)); + }; + + srand((unsigned)time(0)); // set up our OctreeServerPacketProcessor _octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this); @@ -1345,7 +1278,7 @@ void OctreeServer::aboutToFinish() { qDebug() << qPrintable(_safeServerName) << "inform Octree Inbound Packet Processor that we are shutting down..."; - // we're going down - set the NodeList linkedDataCallback to NULL so we do not create any more OctreeQueryNode objects. + // we're going down - set the NodeList linkedDataCallback to nullptr so we do not create any more OctreeQueryNode objects. // This ensures that we don't get any more newly connecting nodes DependencyManager::get()->linkedDataCreateCallback = nullptr; @@ -1363,9 +1296,8 @@ void OctreeServer::aboutToFinish() { // which waits on the thread to be done before returning _sendThreads.clear(); // Cleans up all the send threads. - if (_persistThread) { - _persistThread->aboutToFinish(); - _persistThread->terminating(); + if (_persistManager) { + _persistThread.quit(); } qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish..."; diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 3595fe1129..5c157b46b2 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -33,12 +33,6 @@ Q_DECLARE_LOGGING_CATEGORY(octree_server) const int DEFAULT_PACKETS_PER_INTERVAL = 2000; // some 120,000 packets per second total -enum class OctreeServerState { - WaitingForDomainSettings, - WaitingForOctreeDataNegotation, - Running -}; - /// Handles assignments of type OctreeServer - sending octrees to various clients. class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler { Q_OBJECT @@ -46,8 +40,6 @@ public: OctreeServer(ReceivedMessage& message); ~OctreeServer(); - OctreeServerState _state { OctreeServerState::WaitingForDomainSettings }; - /// allows setting of run arguments void setArguments(int argc, char** argv); @@ -68,12 +60,12 @@ public: static void clientConnected() { _clientCount++; } static void clientDisconnected() { _clientCount--; } - bool isInitialLoadComplete() const { return (_persistThread) ? _persistThread->isInitialLoadComplete() : true; } - bool isPersistEnabled() const { return (_persistThread) ? true : false; } - quint64 getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; } - QString getPersistFilename() const { return (_persistThread) ? _persistThread->getPersistFilename() : ""; } - QString getPersistFileMimeType() const { return (_persistThread) ? _persistThread->getPersistFileMimeType() : "text/plain"; } - QByteArray getPersistFileContents() const { return (_persistThread) ? _persistThread->getPersistFileContents() : QByteArray(); } + bool isInitialLoadComplete() const { return (_persistManager) ? _persistManager->isInitialLoadComplete() : true; } + bool isPersistEnabled() const { return (_persistManager) ? true : false; } + quint64 getLoadElapsedTime() const { return (_persistManager) ? _persistManager->getLoadElapsedTime() : 0; } + QString getPersistFilename() const { return (_persistManager) ? _persistManager->getPersistFilename() : ""; } + QString getPersistFileMimeType() const { return (_persistManager) ? _persistManager->getPersistFileMimeType() : "text/plain"; } + QByteArray getPersistFileContents() const { return (_persistManager) ? _persistManager->getPersistFileContents() : QByteArray(); } // Subclasses must implement these methods virtual std::unique_ptr createOctreeQueryNode() = 0; @@ -149,7 +141,6 @@ private slots: void domainSettingsRequestComplete(); void handleOctreeQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleOctreeDataNackPacket(QSharedPointer message, SharedNodePointer senderNode); - void handleOctreeDataFileReply(QSharedPointer message); void removeSendThread(); protected: @@ -171,7 +162,7 @@ protected: QString getConfiguration(); QString getStatusLink(); - void beginRunning(QByteArray replaceData); + void beginRunning(); UniqueSendThread createSendThread(const SharedNodePointer& node); virtual UniqueSendThread newSendThread(const SharedNodePointer& node) = 0; @@ -199,7 +190,8 @@ protected: bool _debugTimestampNow; bool _verboseDebug; OctreeInboundPacketProcessor* _octreeInboundPacketProcessor; - OctreePersistThread* _persistThread; + OctreePersistThread* _persistManager; + QThread _persistThread; int _persistInterval; bool _persistFileDownload; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 5f05c3cc85..94d76b26ec 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -38,12 +38,11 @@ const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval, - bool debugTimestampNow, QString persistAsFileType, const QByteArray& replacementData) : + bool debugTimestampNow, QString persistAsFileType) : _tree(tree), _filename(filename), _persistInterval(persistInterval), _initialLoadComplete(false), - _replacementData(replacementData), _loadTimeUSecs(0), _lastCheck(0), _debugTimestampNow(debugTimestampNow), @@ -55,6 +54,140 @@ OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& file _filename = sansExt + "." + _persistAsFileType; } +void OctreePersistThread::start() { + cleanupOldReplacementBackups(); + + QFile tempFile { getTempFilename() }; + if (tempFile.exists()) { + qWarning(octree) << "Found temporary octree file at" << tempFile.fileName(); + qDebug(octree) << "Attempting to recover from temporary octree file"; + QFile::remove(_filename); + if (tempFile.rename(_filename)) { + qDebug(octree) << "Successfully recovered from temporary octree file"; + } else { + qWarning(octree) << "Failed to recover from temporary octree file"; + tempFile.remove(); + } + } + + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); + packetReceiver.registerListener(PacketType::OctreeDataFileReply, this, "handleOctreeDataFileReply"); + + auto nodeList = DependencyManager::get(); + const DomainHandler& domainHandler = nodeList->getDomainHandler(); + + auto packet = NLPacket::create(PacketType::OctreeDataFileRequest, -1, true, false); + + OctreeUtils::RawOctreeData data; + qCDebug(octree) << "Reading octree data from" << _filename; + if (data.readOctreeDataInfoFromFile(_filename)) { + qCDebug(octree) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")"; + packet->writePrimitive(true); + auto id = data.id.toRfc4122(); + packet->write(id); + packet->writePrimitive(data.version); + } else { + qCWarning(octree) << "No octree data found"; + packet->writePrimitive(false); + } + + qCDebug(octree) << "Sending request for octree data to DS"; + nodeList->sendPacket(std::move(packet), domainHandler.getSockAddr()); +} + +void OctreePersistThread::handleOctreeDataFileReply(QSharedPointer message) { + if (_initialLoadComplete) { + qCWarning(octree) << "Received OctreeDataFileReply after initial load had completed"; + return; + } + + bool includesNewData; + message->readPrimitive(&includesNewData); + QByteArray replacementData; + if (includesNewData) { + replacementData = message->readAll(); + replaceData(replacementData); + qDebug() << "Got reply to octree data file request, new data sent"; + } else { + qDebug() << "Got reply to octree data file request, current entity data is sufficient"; + + OctreeUtils::RawEntityData data; + qCDebug(octree) << "Reading octree data from" << _filename; + if (data.readOctreeDataInfoFromFile(_filename)) { + if (data.id.isNull()) { + qCDebug(octree) << "Current octree data has a null id, updating"; + data.resetIdAndVersion(); + + QFile file(_filename); + if (file.open(QIODevice::WriteOnly)) { + auto entityData = data.toGzippedByteArray(); + file.write(entityData); + file.close(); + } else { + qCDebug(octree) << "Failed to update octree data"; + } + } + } + } + + quint64 loadStarted = usecTimestampNow(); + qCDebug(octree) << "loading Octrees from file: " << _filename << "..."; + + OctreeUtils::RawOctreeData data; + if (data.readOctreeDataInfoFromFile(_filename)) { + qDebug() << "Setting entity version info to: " << data.id << data.version; + _tree->setOctreeVersionInfo(data.id, data.version); + } + + bool persistentFileRead; + + _tree->withWriteLock([&] { + PerformanceWarning warn(true, "Loading Octree File", true); + + persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData()); + _tree->pruneTree(); + }); + + quint64 loadDone = usecTimestampNow(); + _loadTimeUSecs = loadDone - loadStarted; + + _tree->clearDirtyBit(); // the tree is clean since we just loaded it + qCDebug(octree, "DONE loading Octrees from file... fileRead=%s", debug::valueOf(persistentFileRead)); + + unsigned long nodeCount = OctreeElement::getNodeCount(); + unsigned long internalNodeCount = OctreeElement::getInternalNodeCount(); + unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); + qCDebug(octree, "Nodes after loading scene %lu nodes %lu internal %lu leaves", nodeCount, internalNodeCount, leafNodeCount); + + bool wantDebug = false; + if (wantDebug) { + double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() + / (double)OctreeElement::getGetChildAtIndexCalls(); + qCDebug(octree) << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls() + << " getChildAtIndexTime=" << OctreeElement::getGetChildAtIndexTime() << " perGet=" << usecPerGet; + + double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() + / (double)OctreeElement::getSetChildAtIndexCalls(); + qCDebug(octree) << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls() + << " setChildAtIndexTime=" << OctreeElement::getSetChildAtIndexTime() << " perSet=" << usecPerSet; + } + + _initialLoadComplete = true; + + // Since we just loaded the persistent file, we can consider ourselves as having "just checked" for persistance. + _lastCheck = usecTimestampNow(); // we just loaded, no need to save again + + if (replacementData.isNull()) { + sendLatestEntityDataToDS(); + } + + qDebug() << "Starting timer"; + QTimer::singleShot(_persistInterval, this, &OctreePersistThread::process); + + emit loadCompleted(); +} + + QString OctreePersistThread::getPersistFileMimeType() const { if (_persistAsFileType == "json") { return "application/json"; @@ -96,68 +229,9 @@ bool OctreePersistThread::backupCurrentFile() { } bool OctreePersistThread::process() { + qDebug() << "Processing..."; - if (!_initialLoadComplete) { - quint64 loadStarted = usecTimestampNow(); - qCDebug(octree) << "loading Octrees from file: " << _filename << "..."; - - if (!_replacementData.isNull()) { - replaceData(_replacementData); - } - - OctreeUtils::RawOctreeData data; - if (data.readOctreeDataInfoFromFile(_filename)) { - qDebug() << "Setting entity version info to: " << data.id << data.dataVersion; - _tree->setOctreeVersionInfo(data.id, data.dataVersion); - } - - bool persistentFileRead; - - _tree->withWriteLock([&] { - PerformanceWarning warn(true, "Loading Octree File", true); - - persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData()); - _tree->pruneTree(); - }); - - quint64 loadDone = usecTimestampNow(); - _loadTimeUSecs = loadDone - loadStarted; - - _tree->clearDirtyBit(); // the tree is clean since we just loaded it - qCDebug(octree, "DONE loading Octrees from file... fileRead=%s", debug::valueOf(persistentFileRead)); - - unsigned long nodeCount = OctreeElement::getNodeCount(); - unsigned long internalNodeCount = OctreeElement::getInternalNodeCount(); - unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); - qCDebug(octree, "Nodes after loading scene %lu nodes %lu internal %lu leaves", nodeCount, internalNodeCount, leafNodeCount); - - bool wantDebug = false; - if (wantDebug) { - double usecPerGet = (double)OctreeElement::getGetChildAtIndexTime() - / (double)OctreeElement::getGetChildAtIndexCalls(); - qCDebug(octree) << "getChildAtIndexCalls=" << OctreeElement::getGetChildAtIndexCalls() - << " getChildAtIndexTime=" << OctreeElement::getGetChildAtIndexTime() << " perGet=" << usecPerGet; - - double usecPerSet = (double)OctreeElement::getSetChildAtIndexTime() - / (double)OctreeElement::getSetChildAtIndexCalls(); - qCDebug(octree) << "setChildAtIndexCalls=" << OctreeElement::getSetChildAtIndexCalls() - << " setChildAtIndexTime=" << OctreeElement::getSetChildAtIndexTime() << " perSet=" << usecPerSet; - } - - _initialLoadComplete = true; - - // Since we just loaded the persistent file, we can consider ourselves as having "just checked" for persistance. - _lastCheck = usecTimestampNow(); // we just loaded, no need to save again - - if (_replacementData.isNull()) { - sendLatestEntityDataToDS(); - } - _replacementData.clear(); - - emit loadCompleted(); - } - - if (isStillRunning()) { + if (true) { //isStillRunning()) { quint64 MSECS_TO_USECS = 1000; quint64 USECS_TO_SLEEP = 10 * MSECS_TO_USECS; // every 10ms std::this_thread::sleep_for(std::chrono::microseconds(USECS_TO_SLEEP)); @@ -186,14 +260,15 @@ bool OctreePersistThread::process() { } } - return isStillRunning(); // keep running till they terminate us + //return isStillRunning(); // keep running till they terminate us + QTimer::singleShot(1000, this, &OctreePersistThread::process); + return true; } void OctreePersistThread::aboutToFinish() { qCDebug(octree) << "Persist thread about to finish..."; persist(); qCDebug(octree) << "Persist thread done with about to finish..."; - _stopThread = true; } QByteArray OctreePersistThread::getPersistFileContents() const { diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 990a3b1fad..5f161c100f 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -18,7 +18,7 @@ #include #include "Octree.h" -class OctreePersistThread : public GenericThread { +class OctreePersistThread : public QObject { Q_OBJECT public: class BackupRule { @@ -36,8 +36,7 @@ public: const QString& filename, int persistInterval = DEFAULT_PERSIST_INTERVAL, bool debugTimestampNow = false, - QString persistAsFileType = "json.gz", - const QByteArray& replacementData = QByteArray()); + QString persistAsFileType = "json.gz"); bool isInitialLoadComplete() const { return _initialLoadComplete; } quint64 getLoadElapsedTime() const { return _loadTimeUSecs; } @@ -48,25 +47,31 @@ public: void aboutToFinish(); /// call this to inform the persist thread that the owner is about to finish to support final persist +public slots: + void start(); + signals: void loadCompleted(); -protected: - /// Implements generic processing behavior for this thread. - virtual bool process() override; +protected slots: + bool process(); + void handleOctreeDataFileReply(QSharedPointer message); +protected: void persist(); bool backupCurrentFile(); + void cleanupOldReplacementBackups(); void replaceData(QByteArray data); void sendLatestEntityDataToDS(); + QString getTempFilename() const { return _filename + ".temp"; } + private: OctreePointer _tree; QString _filename; int _persistInterval; bool _initialLoadComplete; - QByteArray _replacementData; quint64 _loadTimeUSecs; From 8f4f3db9ce673ed53e3f8d388836dde317f4295b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 10 May 2018 10:48:14 -0700 Subject: [PATCH 56/78] Add octree backup file cleanup --- libraries/octree/src/OctreePersistThread.cpp | 64 +++++++++++++++----- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 94d76b26ec..93d20ab12a 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,11 @@ #include "OctreeUtils.h" #include "OctreeDataUtils.h" -const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds +const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL { 1000 * 30 }; // every 30 seconds + +constexpr int MAX_OCTREE_REPLACEMENT_BACKUP_FILES_COUNT { 20 }; +//constexpr int MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 50 * 1000 * 1000 }; +constexpr size_t MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 70000 }; OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval, bool debugTimestampNow, QString persistAsFileType) : @@ -280,6 +285,37 @@ QByteArray OctreePersistThread::getPersistFileContents() const { return fileContents; } +void OctreePersistThread::cleanupOldReplacementBackups() { + QRegExp filenameRegex { ".*\\.backup\\.\\d{8}-\\d{6}$" }; + QFileInfo persistFile { _filename }; + QDir backupDir { persistFile.absolutePath() }; + backupDir.setSorting(QDir::SortFlag::Time); + backupDir.setFilter(QDir::Filter::Files); + qDebug() << "Scanning backups for cleanup:" << backupDir.absolutePath(); + + auto count = 0; + size_t totalSize = 0; + for (auto fileInfo : backupDir.entryInfoList()) { + auto absPath = fileInfo.absoluteFilePath(); + qDebug() << "Found:" << absPath; + if (filenameRegex.exactMatch(absPath)) { + totalSize += fileInfo.size(); + if (count >= MAX_OCTREE_REPLACEMENT_BACKUP_FILES_COUNT || totalSize > MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES) { + qDebug() << "Removing:" << absPath; + QFile backup(absPath); + if (backup.remove()) { + qDebug() << "Removed backup:" << absPath; + } else { + qWarning() << "Failed to remove backup:" << absPath; + + } + } + count++; + } + } + qDebug() << "Found" << count << "backups"; +} + void OctreePersistThread::persist() { if (_tree->isDirty() && _initialLoadComplete) { @@ -291,20 +327,20 @@ void OctreePersistThread::persist() { _tree->incrementPersistDataVersion(); - // create our "lock" file to indicate we're saving. - QString lockFileName = _filename + ".lock"; - std::ofstream lockFile(lockFileName.toLocal8Bit().constData(), std::ios::out|std::ios::binary); - if(lockFile.is_open()) { - qCDebug(octree) << "saving Octree lock file created at:" << lockFileName; - - _tree->writeToFile(_filename.toLocal8Bit().constData(), NULL, _persistAsFileType); + QString tempFilename = getTempFilename(); + qCDebug(octree) << "Saving temporary Octree file to:" << tempFilename; + if (_tree->writeToFile(tempFilename.toLocal8Bit().constData(), nullptr, _persistAsFileType)) { + QFile tempFile { tempFilename }; _tree->clearDirtyBit(); // tree is clean after saving - qCDebug(octree) << "DONE saving Octree to file..."; - - lockFile.close(); - qCDebug(octree) << "saving Octree lock file closed:" << lockFileName; - remove(lockFileName.toLocal8Bit().constData()); - qCDebug(octree) << "saving Octree lock file removed:" << lockFileName; + QFile::remove(_filename); + if (tempFile.rename(_filename)) { + qCDebug(octree) << "DONE moving temporary Octree file to" << _filename; + } else { + qCWarning(octree) << "Failed to move temporary Octree file to " << _filename; + tempFile.remove(); + } + } else { + qCWarning(octree) << "Failed to open temp Octree file at" << tempFilename; } sendLatestEntityDataToDS(); From d0af06939f4b51486578cc5e11314b313e93b73f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 10 May 2018 15:51:55 -0700 Subject: [PATCH 57/78] Update persistInterval to use std::chrono in OctreePersistThread --- assignment-client/src/octree/OctreeServer.cpp | 9 +++++++-- assignment-client/src/octree/OctreeServer.h | 2 +- libraries/octree/src/OctreePersistThread.cpp | 12 ++++++------ libraries/octree/src/OctreePersistThread.h | 6 +++--- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index b8329b4d3e..798f539211 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1050,8 +1050,13 @@ void OctreeServer::readConfiguration() { _persistAsFileType = "json.gz"; _persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL; - readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval); - qDebug() << "persistInterval=" << _persistInterval; + int result { -1 }; + readOptionInt(QString("persistInterval"), settingsSectionObject, result); + if (result != -1) { + _persistInterval = std::chrono::seconds(result); + } + + qDebug() << "persistInterval=" << _persistInterval.count(); readOptionBool(QString("persistFileDownload"), settingsSectionObject, _persistFileDownload); qDebug() << "persistFileDownload=" << _persistFileDownload; diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 5c157b46b2..07b1e334b1 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -193,7 +193,7 @@ protected: OctreePersistThread* _persistManager; QThread _persistThread; - int _persistInterval; + std::chrono::milliseconds _persistInterval; bool _persistFileDownload; int _maxBackupVersions; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 93d20ab12a..ae03ff2463 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -36,13 +36,12 @@ #include "OctreeUtils.h" #include "OctreeDataUtils.h" -const int OctreePersistThread::DEFAULT_PERSIST_INTERVAL { 1000 * 30 }; // every 30 seconds +const std::chrono::seconds OctreePersistThread::DEFAULT_PERSIST_INTERVAL { 30 }; constexpr int MAX_OCTREE_REPLACEMENT_BACKUP_FILES_COUNT { 20 }; -//constexpr int MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 50 * 1000 * 1000 }; -constexpr size_t MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 70000 }; +constexpr size_t MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 50 * 1000 * 1000 }; -OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, int persistInterval, +OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, std::chrono::milliseconds persistInterval, bool debugTimestampNow, QString persistAsFileType) : _tree(tree), _filename(filename), @@ -187,7 +186,8 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointer intervalToCheck) { _lastCheck = now; diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 5f161c100f..bd114cec06 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -30,11 +30,11 @@ public: quint64 lastBackup; }; - static const int DEFAULT_PERSIST_INTERVAL; + static const std::chrono::seconds DEFAULT_PERSIST_INTERVAL; OctreePersistThread(OctreePointer tree, const QString& filename, - int persistInterval = DEFAULT_PERSIST_INTERVAL, + std::chrono::milliseconds persistInterval = DEFAULT_PERSIST_INTERVAL, bool debugTimestampNow = false, QString persistAsFileType = "json.gz"); @@ -70,7 +70,7 @@ protected: private: OctreePointer _tree; QString _filename; - int _persistInterval; + std::chrono::milliseconds _persistInterval; bool _initialLoadComplete; quint64 _loadTimeUSecs; From 9afc9d19c5bab4e2845ca51f3117d423e32f93bf Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 8 Jun 2018 16:00:31 -0700 Subject: [PATCH 58/78] Cleanup comments and process structure in OctreePersistThread --- assignment-client/src/octree/OctreeServer.cpp | 2 +- libraries/octree/src/OctreePersistThread.cpp | 59 ++++++------------- libraries/octree/src/OctreePersistThread.h | 5 +- 3 files changed, 22 insertions(+), 44 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 798f539211..c2fbc8ab37 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1053,7 +1053,7 @@ void OctreeServer::readConfiguration() { int result { -1 }; readOptionInt(QString("persistInterval"), settingsSectionObject, result); if (result != -1) { - _persistInterval = std::chrono::seconds(result); + _persistInterval = std::chrono::milliseconds(result); } qDebug() << "persistInterval=" << _persistInterval.count(); diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index ae03ff2463..af0bf55b46 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -36,7 +36,8 @@ #include "OctreeUtils.h" #include "OctreeDataUtils.h" -const std::chrono::seconds OctreePersistThread::DEFAULT_PERSIST_INTERVAL { 30 }; +constexpr std::chrono::seconds OctreePersistThread::DEFAULT_PERSIST_INTERVAL { 30 }; +constexpr std::chrono::milliseconds TIME_BETWEEN_PROCESSING { 10 }; constexpr int MAX_OCTREE_REPLACEMENT_BACKUP_FILES_COUNT { 20 }; constexpr size_t MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 50 * 1000 * 1000 }; @@ -48,7 +49,7 @@ OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& file _persistInterval(persistInterval), _initialLoadComplete(false), _loadTimeUSecs(0), - _lastCheck(0), + _lastPersistCheck(std::chrono::steady_clock::now()), _debugTimestampNow(debugTimestampNow), _lastTimeDebug(0), _persistAsFileType(persistAsFileType) @@ -95,7 +96,7 @@ void OctreePersistThread::start() { packet->writePrimitive(false); } - qCDebug(octree) << "Sending request for octree data to DS"; + qCDebug(octree) << "Sending OctreeDataFileRequest to DS"; nodeList->sendPacket(std::move(packet), domainHandler.getSockAddr()); } @@ -111,9 +112,9 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerreadAll(); replaceData(replacementData); - qDebug() << "Got reply to octree data file request, new data sent"; + qDebug() << "Got OctreeDataFileReply, new data sent"; } else { - qDebug() << "Got reply to octree data file request, current entity data is sufficient"; + qDebug() << "Got OctreeDataFileReply, current entity data is sufficient"; OctreeUtils::RawEntityData data; qCDebug(octree) << "Reading octree data from" << _filename; @@ -178,16 +179,14 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerupdate(); - // do our updates then check to save... - _tree->update(); + auto now = std::chrono::steady_clock::now(); + auto timeSinceLastPersist = now - _lastPersistCheck; - quint64 now = usecTimestampNow(); - quint64 sinceLastSave = now - _lastCheck; - quint64 intervalToCheck = std::chrono::microseconds(_persistInterval).count(); - - if (sinceLastSave > intervalToCheck) { - _lastCheck = now; - persist(); - } + qDebug() << "Seconds since last persist: " << std::chrono::duration_cast(timeSinceLastPersist).count(); + if (timeSinceLastPersist > _persistInterval) { + _lastPersistCheck = now; + persist(); } - - // if we were asked to debugTimestampNow do that now... - if (_debugTimestampNow) { - quint64 now = usecTimestampNow(); - quint64 sinceLastDebug = now - _lastTimeDebug; - quint64 DEBUG_TIMESTAMP_INTERVAL = 600000000; // every 10 minutes - if (sinceLastDebug > DEBUG_TIMESTAMP_INTERVAL) { - _lastTimeDebug = usecTimestampNow(true); // ask for debug output - } - - } - //return isStillRunning(); // keep running till they terminate us - QTimer::singleShot(1000, this, &OctreePersistThread::process); - return true; + QTimer::singleShot(TIME_BETWEEN_PROCESSING.count(), this, &OctreePersistThread::process); } void OctreePersistThread::aboutToFinish() { diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index bd114cec06..5bb6e51e5f 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -54,7 +54,7 @@ signals: void loadCompleted(); protected slots: - bool process(); + void process(); void handleOctreeDataFileReply(QSharedPointer message); protected: @@ -71,12 +71,11 @@ private: OctreePointer _tree; QString _filename; std::chrono::milliseconds _persistInterval; + std::chrono::steady_clock::time_point _lastPersistCheck; bool _initialLoadComplete; quint64 _loadTimeUSecs; - quint64 _lastCheck; - bool _debugTimestampNow; quint64 _lastTimeDebug; From 9506e7edbe75d7a33805767ac554286ab8957ec0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 8 Jun 2018 14:47:20 -0700 Subject: [PATCH 59/78] check for update for dev-builds, handle semantic version --- interface/src/Application.cpp | 7 +- interface/src/ui/UpdateDialog.cpp | 36 +++++--- libraries/auto-updater/src/AutoUpdater.cpp | 68 ++++++++++----- libraries/auto-updater/src/AutoUpdater.h | 17 ++-- libraries/shared/src/ApplicationVersion.cpp | 94 +++++++++++++++++++++ libraries/shared/src/ApplicationVersion.h | 41 +++++++++ 6 files changed, 220 insertions(+), 43 deletions(-) create mode 100644 libraries/shared/src/ApplicationVersion.cpp create mode 100644 libraries/shared/src/ApplicationVersion.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c5857dac53..4a785db2c2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -905,7 +905,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1784,10 +1783,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // If launched from Steam, let it handle updates const QString HIFI_NO_UPDATER_COMMAND_LINE_KEY = "--no-updater"; bool noUpdater = arguments().indexOf(HIFI_NO_UPDATER_COMMAND_LINE_KEY) != -1; - if (!noUpdater) { + bool buildCanUpdate = BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable + || BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master; + if (!noUpdater && buildCanUpdate) { constexpr auto INSTALLER_TYPE_CLIENT_ONLY = "client_only"; - auto applicationUpdater = DependencyManager::get(); + auto applicationUpdater = DependencyManager::set(); AutoUpdater::InstallerType type = installerType == INSTALLER_TYPE_CLIENT_ONLY ? AutoUpdater::InstallerType::CLIENT_ONLY : AutoUpdater::InstallerType::FULL; diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 2dcc0c07eb..7ff2132ab9 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -21,19 +21,31 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { auto applicationUpdater = DependencyManager::get(); - int currentVersion = QCoreApplication::applicationVersion().toInt(); - int latestVersion = applicationUpdater.data()->getBuildData().lastKey(); - _updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " - + QString(applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"]).replace(" ", " "); + if (applicationUpdater) { - _releaseNotes = ""; - for (int i = latestVersion; i > currentVersion; i--) { - if (applicationUpdater.data()->getBuildData().contains(i)) { - QString releaseNotes = applicationUpdater.data()->getBuildData()[i]["releaseNotes"]; - releaseNotes.remove("
"); - releaseNotes.remove(QRegExp("^\n+")); - _releaseNotes += "\n" + QString().sprintf("%d", i) + "\n" + releaseNotes + "\n"; + auto buildData = applicationUpdater.data()->getBuildData(); + ApplicationVersion latestVersion = buildData.lastKey(); + _updateAvailableDetails = "v" + latestVersion.versionString + " released on " + + QString(buildData[latestVersion]["releaseTime"]).replace(" ", " "); + + _releaseNotes = ""; + + auto it = buildData.end(); + while (it != buildData.begin()) { + --it; + + if (applicationUpdater->getCurrentVersion() < it.key()) { + // grab the release notes for this later version + QString releaseNotes = it.value()["releaseNotes"]; + releaseNotes.remove("
"); + releaseNotes.remove(QRegExp("^\n+")); + _releaseNotes += "\n" + it.key().versionString + "\n" + releaseNotes + "\n"; + } else { + break; + } } + + } } @@ -47,5 +59,5 @@ const QString& UpdateDialog::releaseNotes() const { void UpdateDialog::triggerUpgrade() { auto applicationUpdater = DependencyManager::get(); - applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey()); + applicationUpdater.data()->openLatestUpdateURL(); } diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp index 6749cd9e10..300a22983a 100644 --- a/libraries/auto-updater/src/AutoUpdater.cpp +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -11,13 +11,16 @@ #include "AutoUpdater.h" -#include - -#include -#include #include -AutoUpdater::AutoUpdater() { +#include +#include +#include +#include + +AutoUpdater::AutoUpdater() : + _currentVersion(BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? BuildInfo::VERSION : BuildInfo::BUILD_NUMBER) +{ #if defined Q_OS_WIN32 _operatingSystem = "windows"; #elif defined Q_OS_MAC @@ -33,9 +36,22 @@ void AutoUpdater::checkForUpdate() { this->getLatestVersionData(); } +const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); +const QUrl MASTER_BUILDS_XML_URL("https://highfidelity.com/dev-builds.xml"); + void AutoUpdater::getLatestVersionData() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest latestVersionRequest(BUILDS_XML_URL); + + QUrl buildsURL; + + if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable) { + buildsURL = BUILDS_XML_URL; + } else if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master) { + buildsURL = MASTER_BUILDS_XML_URL; + } + + QNetworkRequest latestVersionRequest(buildsURL); + latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); @@ -52,12 +68,22 @@ void AutoUpdater::parseLatestVersionData() { QString clientOnly; }; - int version { 0 }; + QString version; QString downloadUrl; QString releaseTime; QString releaseNotes; QString commitSha; QString pullRequestNumber; + + QString versionKey; + + // stable builds look at the stable_version node (semantic version) + // master builds look at the version node (build number) + if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable) { + versionKey = "stable_version"; + } else if (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Master) { + versionKey = "version"; + } while (xml.readNextStartElement()) { if (xml.name() == "projects") { @@ -77,8 +103,8 @@ void AutoUpdater::parseLatestVersionData() { QHash campaignInstallers; while (xml.readNextStartElement()) { - if (xml.name() == "version") { - version = xml.readElementText().toInt(); + if (xml.name() == versionKey) { + version = xml.readElementText(); } else if (xml.name() == "url") { downloadUrl = xml.readElementText(); } else if (xml.name() == "installers") { @@ -159,31 +185,31 @@ void AutoUpdater::parseLatestVersionData() { } void AutoUpdater::checkVersionAndNotify() { - if (BuildInfo::BUILD_TYPE != BuildInfo::BuildType::Stable || _builds.empty()) { - // No version checking is required in nightly/PR/dev builds or when no build - // data was found for the platform + if (_builds.empty()) { + // no build data was found for this platform return; } - int latestVersionAvailable = _builds.lastKey(); - if (QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) { + + qDebug() << "Checking if update version" << _builds.lastKey().versionString + << "is newer than current version" << _currentVersion.versionString; + + if (_builds.lastKey() > _currentVersion) { emit newVersionIsAvailable(); } } -void AutoUpdater::performAutoUpdate(int version) { - // NOTE: This is not yet auto updating - however this is a checkpoint towards that end - // Next PR will handle the automatic download, upgrading and application restart - const QMap& chosenVersion = _builds.value(version); +void AutoUpdater::openLatestUpdateURL() { + const QMap& chosenVersion = _builds.last(); const QUrl& downloadUrl = chosenVersion.value("downloadUrl"); QDesktopServices::openUrl(downloadUrl); QCoreApplication::quit(); } -void AutoUpdater::downloadUpdateVersion(int version) { +void AutoUpdater::downloadUpdateVersion(const QString& version) { emit newVersionIsDownloaded(); } -void AutoUpdater::appendBuildData(int versionNumber, +void AutoUpdater::appendBuildData(const QString& versionNumber, const QString& downloadURL, const QString& releaseTime, const QString& releaseNotes, @@ -194,6 +220,6 @@ void AutoUpdater::appendBuildData(int versionNumber, thisBuildDetails.insert("releaseTime", releaseTime); thisBuildDetails.insert("releaseNotes", releaseNotes); thisBuildDetails.insert("pullRequestNumber", pullRequestNumber); - _builds.insert(versionNumber, thisBuildDetails); + _builds.insert(ApplicationVersion(versionNumber), thisBuildDetails); } diff --git a/libraries/auto-updater/src/AutoUpdater.h b/libraries/auto-updater/src/AutoUpdater.h index f56d7993e9..c788ac31d1 100644 --- a/libraries/auto-updater/src/AutoUpdater.h +++ b/libraries/auto-updater/src/AutoUpdater.h @@ -26,10 +26,9 @@ #include #include +#include #include -const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); - class AutoUpdater : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -43,25 +42,29 @@ public: }; void checkForUpdate(); - const QMap>& getBuildData() { return _builds; } - void performAutoUpdate(int version); + const QMap>& getBuildData() { return _builds; } + void openLatestUpdateURL(); void setInstallerType(InstallerType type) { _installerType = type; } void setInstallerCampaign(QString campaign) { _installerCampaign = campaign; } + const ApplicationVersion& getCurrentVersion() const { return _currentVersion; } + signals: void latestVersionDataParsed(); void newVersionIsAvailable(); void newVersionIsDownloaded(); private: - QMap> _builds; + QMap> _builds; QString _operatingSystem; InstallerType _installerType { InstallerType::FULL }; QString _installerCampaign { "" }; + + ApplicationVersion _currentVersion; void getLatestVersionData(); - void downloadUpdateVersion(int version); - void appendBuildData(int versionNumber, + void downloadUpdateVersion(const QString& version); + void appendBuildData(const QString& versionNumber, const QString& downloadURL, const QString& releaseTime, const QString& releaseNotes, diff --git a/libraries/shared/src/ApplicationVersion.cpp b/libraries/shared/src/ApplicationVersion.cpp new file mode 100644 index 0000000000..5c2d5ad11c --- /dev/null +++ b/libraries/shared/src/ApplicationVersion.cpp @@ -0,0 +1,94 @@ +// +// ApplicationVersion.cpp +// libraries/shared/src +// +// Created by Stephen Birarda on 6/8/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ApplicationVersion.h" + +#include + +#include +#include +#include + +ApplicationVersion::ApplicationVersion(const QString& versionString) : + versionString(versionString) +{ + // attempt to regex out a semantic version from the string + // handling both x.y.z and x.y formats + QRegExp semanticRegex("([\\d]+)\\.([\\d]+)(?:\\.([\\d]+))?"); + + int pos = semanticRegex.indexIn(versionString); + if (pos != -1) { + isSemantic = true; + auto captures = semanticRegex.capturedTexts(); + + major = captures[1].toInt(); + minor = captures[2].toInt(); + + if (captures.length() > 3) { + patch = captures[3].toInt(); + } else { + // the patch is implictly 0 if it was not included + patch = 0; + } + } else { + // if we didn't have a sematic style, we assume that we just have a build number + build = versionString.toInt(); + } +} + +bool ApplicationVersion::operator==(const ApplicationVersion& other) const { + if (isSemantic && other.isSemantic) { + return major == other.major && minor == other.minor && patch == other.patch; + } else if (!isSemantic && !other.isSemantic) { + return build == other.build; + } else { + assert(isSemantic == other.isSemantic); + return false; + } +} + +bool ApplicationVersion::operator<(const ApplicationVersion& other) const { + if (isSemantic && other.isSemantic) { + if (major == other.major) { + if (minor == other.minor) { + return patch < other.patch; + } else { + return minor < other.minor; + } + } else { + return major < other.major; + } + } else if (!isSemantic && !other.isSemantic) { + return build < other.build; + } else { + assert(isSemantic == other.isSemantic); + return false; + } +} + +bool ApplicationVersion::operator>(const ApplicationVersion& other) const { + if (isSemantic && other.isSemantic) { + if (major == other.major) { + if (minor == other.minor) { + return patch > other.patch; + } else { + return minor > other.minor; + } + } else { + return major > other.major; + } + } else if (!isSemantic && !other.isSemantic) { + return build > other.build; + } else { + assert(isSemantic == other.isSemantic); + return false; + } +} diff --git a/libraries/shared/src/ApplicationVersion.h b/libraries/shared/src/ApplicationVersion.h new file mode 100644 index 0000000000..5cb0a09a8d --- /dev/null +++ b/libraries/shared/src/ApplicationVersion.h @@ -0,0 +1,41 @@ +// +// ApplicationVersion.h +// libraries/shared/src +// +// Created by Stephen Birarda on 6/8/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ApplicationVersion_h +#define hifi_ApplicationVersion_h + +#include + +class ApplicationVersion { +public: + ApplicationVersion(const QString& versionString); + + bool operator==(const ApplicationVersion& other) const; + bool operator!=(const ApplicationVersion& other) const { return !(*this == other); } + + bool operator <(const ApplicationVersion& other) const; + bool operator >(const ApplicationVersion& other) const; + + bool operator >=(const ApplicationVersion& other) const { return (*this == other) || (*this > other); } + bool operator <=(const ApplicationVersion& other) const { return (*this == other) || (*this < other); } + + int major = -1; + int minor = -1; + int patch = -1; + + int build = -1; + + bool isSemantic { false }; + + QString versionString; +}; + +#endif // hifi_ApplicationVersion_h From 8dd268addbf4777a53f1d5a525d0437d9c1cfff3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 8 Jun 2018 17:13:04 -0700 Subject: [PATCH 60/78] handle semantic version updates in sandbox --- cmake/templates/console-build-info.json.in | 4 +- server-console/src/main.js | 55 +++++++++-------- server-console/src/modules/hf-updater.js | 70 ++++++++++++++++++++-- 3 files changed, 94 insertions(+), 35 deletions(-) diff --git a/cmake/templates/console-build-info.json.in b/cmake/templates/console-build-info.json.in index 6b4ee99292..e8cd8eee22 100644 --- a/cmake/templates/console-build-info.json.in +++ b/cmake/templates/console-build-info.json.in @@ -1,5 +1,7 @@ { "releaseType": "@RELEASE_TYPE@", + "buildNumber": "@BUILD_NUMBER@", + "stableBuild": "@STABLE_BUILD@", "buildIdentifier": "@BUILD_VERSION@", - "organization": "@BUILD_ORGANIZATION@" + "organization": "@BUILD_ORGANIZATION@" } diff --git a/server-console/src/main.js b/server-console/src/main.js index 8a92fc8a5d..f9c72ddf8a 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -60,7 +60,14 @@ function getBuildInfo() { } } - const DEFAULT_BUILD_INFO = { releaseType: "", buildIdentifier: "dev" }; + const DEFAULT_BUILD_INFO = { + releaseType: "", + buildIdentifier: "dev", + buildNumber: "0", + stableBuild: "0", + organization: "High Fidelity - dev" + }; + var buildInfo = DEFAULT_BUILD_INFO; if (buildInfoPath) { @@ -858,33 +865,25 @@ function onContentLoaded() { // maybeShowSplash(); if (buildInfo.releaseType == 'PRODUCTION' && !argv.noUpdater) { - var currentVersion = null; - try { - currentVersion = parseInt(buildInfo.buildIdentifier); - } catch (e) { - } - - if (currentVersion !== null) { - const CHECK_FOR_UPDATES_INTERVAL_SECONDS = 60 * 30; - var hasShownUpdateNotification = false; - const updateChecker = new updater.UpdateChecker(currentVersion, CHECK_FOR_UPDATES_INTERVAL_SECONDS); - updateChecker.on('update-available', function(latestVersion, url) { - if (!hasShownUpdateNotification) { - notifier.notify({ - icon: notificationIcon, - title: 'An update is available!', - message: 'High Fidelity version ' + latestVersion + ' is available', - wait: true, - url: url - }); - hasShownUpdateNotification = true; - } - }); - notifier.on('click', function(notifierObject, options) { - log.debug("Got click", options.url); - shell.openExternal(options.url); - }); - } + const CHECK_FOR_UPDATES_INTERVAL_SECONDS = 60 * 30; + var hasShownUpdateNotification = false; + const updateChecker = new updater.UpdateChecker(buildInfo, CHECK_FOR_UPDATES_INTERVAL_SECONDS); + updateChecker.on('update-available', function(latestVersion, url) { + if (!hasShownUpdateNotification) { + notifier.notify({ + icon: notificationIcon, + title: 'An update is available!', + message: 'High Fidelity version ' + latestVersion + ' is available', + wait: true, + url: url + }); + hasShownUpdateNotification = true; + } + }); + notifier.on('click', function(notifierObject, options) { + log.debug("Got click", options.url); + shell.openExternal(options.url); + }); } deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); diff --git a/server-console/src/modules/hf-updater.js b/server-console/src/modules/hf-updater.js index 489364f655..d20da3c663 100644 --- a/server-console/src/modules/hf-updater.js +++ b/server-console/src/modules/hf-updater.js @@ -8,10 +8,48 @@ const os = require('os'); const platform = os.type() == 'Windows_NT' ? 'windows' : 'mac'; const BUILDS_URL = 'https://highfidelity.com/builds.xml'; +const DEV_BUILDS_URL = 'https://highfidelity.com/dev-builds.xml'; -function UpdateChecker(currentVersion, checkForUpdatesEveryXSeconds) { - this.currentVersion = currentVersion; - log.debug('cur', currentVersion); +// returns 1 if A is greater, 0 if equal, -1 if A is lesser +function semanticVersionCompare(versionA, versionB) { + var versionAParts = versionA.split('.'); + var versionBParts = versionB.split('.'); + + // make sure each version has 3 parts + var partsLength = versionAParts.length; + while (partsLength < 3) { + partsLength = versionAParts.push(0); + } + + partsLength = versionBParts.length; + while (partsLength < 3) { + partsLength = versionBParts.push(0); + } + + // map all of the parts to numbers + versionAParts = versionAParts.map(Number); + versionBParts = versionBParts.map(Number); + + for (var i = 0; i < 3; ++i) { + if (versionAParts[i] == versionBParts[i]) { + continue; + } else if (versionAParts[i] > versionBParts[i]) { + return 1; + } else { + return -1; + } + } + + return 0; +} + +function UpdateChecker(buildInfo, checkForUpdatesEveryXSeconds) { + this.stableBuild = (buildInfo.stableBuild == "1"); + + this.buildsURL = this.stableBuild ? BUILDS_URL : DEV_BUILDS_URL; + this.currentVersion = this.stableBuild ? buildInfo.buildIdentifier : parseInt(buildInfo.buildNumber); + + log.debug('Current version is', this.currentVersion); setInterval(this.checkForUpdates.bind(this), checkForUpdatesEveryXSeconds * 1000); this.checkForUpdates(); @@ -29,12 +67,32 @@ UpdateChecker.prototype = extend(UpdateChecker.prototype, { try { var $ = cheerio.load(body, { xmlMode: true }); const latestBuild = $('project[name="interface"] platform[name="' + platform + '"]').children().first(); - const latestVersion = parseInt(latestBuild.find('version').text()); - log.debug("Latest version is:", latestVersion, this.currentVersion); - if (latestVersion > this.currentVersion) { + + var latestVersion = 0; + + if (this.stableBuild) { + latestVersion = latestBuild.find('stable_version').text(); + } else { + latestVersion = parseInt(latestBuild.find('version').text()); + } + + log.debug("Latest available update version is:", latestVersion); + + updateAvailable = false; + + if (this.stableBuild) { + // compare the semantic versions to see if the update is newer + updateAvailable = (semanticVersionCompare(latestVersion, this.currentVersion) == 1); + } else { + // for master builds we just compare the versions as integers + updateAvailable = latestVersion > this.currentVersion; + } + + if (updateAvailable) { const url = latestBuild.find('url').text(); this.emit('update-available', latestVersion, url); } + } catch (e) { log.warn("Error when checking for updates", e); } From 7a9d77d0d9fa032c20c62d5b647418ff9278a7a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 8 Jun 2018 17:15:27 -0700 Subject: [PATCH 61/78] force git abbreviated SHA to have length of 7 --- cmake/macros/SetPackagingParameters.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/SetPackagingParameters.cmake b/cmake/macros/SetPackagingParameters.cmake index 029c829022..ef96e989d8 100644 --- a/cmake/macros/SetPackagingParameters.cmake +++ b/cmake/macros/SetPackagingParameters.cmake @@ -95,7 +95,7 @@ macro(SET_PACKAGING_PARAMETERS) endif () execute_process( - COMMAND git log -1 --format=${_GIT_LOG_FORMAT} + COMMAND git log -1 --abbrev=7 --format=${_GIT_LOG_FORMAT} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE _GIT_LOG_OUTPUT ERROR_VARIABLE _GIT_LOG_ERROR From 94069c964d77b608a3888e479f414f66bbf8ff52 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Jun 2018 08:36:13 -0700 Subject: [PATCH 62/78] Remove unused variable and log message in OctreePersistThread --- libraries/octree/src/OctreePersistThread.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index af0bf55b46..3ceed6cb0f 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -233,14 +233,11 @@ bool OctreePersistThread::backupCurrentFile() { } void OctreePersistThread::process() { - auto startedProcessingAt = std::chrono::steady_clock::now(); - _tree->update(); auto now = std::chrono::steady_clock::now(); auto timeSinceLastPersist = now - _lastPersistCheck; - qDebug() << "Seconds since last persist: " << std::chrono::duration_cast(timeSinceLastPersist).count(); if (timeSinceLastPersist > _persistInterval) { _lastPersistCheck = now; persist(); From 11a48915e26dd237dedee187011d7d0ac7090c2b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Jun 2018 09:47:50 -0700 Subject: [PATCH 63/78] Fix order of variable initialization in OctreePersistThread --- libraries/octree/src/OctreePersistThread.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 3ceed6cb0f..8d22e896d1 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -47,9 +47,9 @@ OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& file _tree(tree), _filename(filename), _persistInterval(persistInterval), + _lastPersistCheck(std::chrono::steady_clock::now()), _initialLoadComplete(false), _loadTimeUSecs(0), - _lastPersistCheck(std::chrono::steady_clock::now()), _debugTimestampNow(debugTimestampNow), _lastTimeDebug(0), _persistAsFileType(persistAsFileType) @@ -283,7 +283,6 @@ void OctreePersistThread::cleanupOldReplacementBackups() { qDebug() << "Removed backup:" << absPath; } else { qWarning() << "Failed to remove backup:" << absPath; - } } count++; From fa22ea76301a4ef6ebcbdf20d482884ddb82bc76 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Jun 2018 11:06:11 -0700 Subject: [PATCH 64/78] Update ES to keep the backup that pushes file size over the limit --- libraries/octree/src/OctreePersistThread.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 8d22e896d1..8f55953360 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -40,7 +40,7 @@ constexpr std::chrono::seconds OctreePersistThread::DEFAULT_PERSIST_INTERVAL { 3 constexpr std::chrono::milliseconds TIME_BETWEEN_PROCESSING { 10 }; constexpr int MAX_OCTREE_REPLACEMENT_BACKUP_FILES_COUNT { 20 }; -constexpr size_t MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 50 * 1000 * 1000 }; +constexpr int64_t MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES { 50 * 1000 * 1000 }; OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& filename, std::chrono::milliseconds persistInterval, bool debugTimestampNow, QString persistAsFileType) : @@ -269,22 +269,22 @@ void OctreePersistThread::cleanupOldReplacementBackups() { backupDir.setFilter(QDir::Filter::Files); qDebug() << "Scanning backups for cleanup:" << backupDir.absolutePath(); - auto count = 0; - size_t totalSize = 0; + int count = 0; + int64_t totalSize = 0; for (auto fileInfo : backupDir.entryInfoList()) { auto absPath = fileInfo.absoluteFilePath(); - qDebug() << "Found:" << absPath; + qDebug() << " Found:" << absPath; if (filenameRegex.exactMatch(absPath)) { - totalSize += fileInfo.size(); if (count >= MAX_OCTREE_REPLACEMENT_BACKUP_FILES_COUNT || totalSize > MAX_OCTREE_REPLACEMENT_BACKUP_FILES_SIZE_BYTES) { - qDebug() << "Removing:" << absPath; + qDebug() << " Removing:" << absPath; QFile backup(absPath); if (backup.remove()) { - qDebug() << "Removed backup:" << absPath; + qDebug() << " Removed backup:" << absPath; } else { - qWarning() << "Failed to remove backup:" << absPath; + qWarning() << " Failed to remove backup:" << absPath; } } + totalSize += fileInfo.size(); count++; } } From 015c092e1c08d7d15a342bae3fb6855e17a9cc7e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Jun 2018 16:37:40 -0700 Subject: [PATCH 65/78] Update oven to only produce uncompressed ktx for cubemaps --- libraries/baking/src/TextureBaker.cpp | 8 ++++++-- libraries/baking/src/TextureBaker.h | 4 ++++ tools/oven/src/OvenCLIApplication.cpp | 6 ++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 4f42ad627c..ecfe724441 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -30,6 +30,8 @@ const QString BAKED_TEXTURE_KTX_EXT = ".ktx"; const QString BAKED_TEXTURE_BCN_SUFFIX = "_bcn.ktx"; const QString BAKED_META_TEXTURE_SUFFIX = ".texmeta.json"; +bool TextureBaker::_compressionEnabled = true; + TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory, const QString& metaTexturePathPrefix, const QString& baseFilename, const QByteArray& textureContent) : @@ -146,7 +148,7 @@ void TextureBaker::processTexture() { } // Compressed KTX - { + if (_compressionEnabled) { auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, _abortProcessing); if (!processedTexture) { @@ -186,7 +188,7 @@ void TextureBaker::processTexture() { } // Uncompressed KTX - { + if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) { buffer->reset(); auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, _abortProcessing); @@ -218,6 +220,8 @@ void TextureBaker::processTexture() { } _outputFiles.push_back(filePath); meta.uncompressed = _metaTexturePathPrefix + fileName; + } else { + buffer.reset(); } { diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index 54839c001a..c8c4fb73b8 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -41,6 +41,8 @@ public: virtual void setWasAborted(bool wasAborted) override; + static void setCompressionEnabled(bool enabled) { _compressionEnabled = enabled; } + public slots: virtual void bake() override; virtual void abort() override; @@ -65,6 +67,8 @@ private: QString _metaTexturePathPrefix; std::atomic _abortProcessing { false }; + + static bool _compressionEnabled; }; #endif // hifi_TextureBaker_h diff --git a/tools/oven/src/OvenCLIApplication.cpp b/tools/oven/src/OvenCLIApplication.cpp index 6f87359134..c405c5f4a0 100644 --- a/tools/oven/src/OvenCLIApplication.cpp +++ b/tools/oven/src/OvenCLIApplication.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "BakerCLI.h" @@ -47,10 +48,7 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) : if (parser.isSet(CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER)) { qDebug() << "Disabling texture compression"; - image::setColorTexturesCompressionEnabled(false); - image::setGrayscaleTexturesCompressionEnabled(false); - image::setNormalTexturesCompressionEnabled(false); - image::setCubeTexturesCompressionEnabled(false); + TextureBaker::setCompressionEnabled(false); } QMetaObject::invokeMethod(cli, "bakeFile", Qt::QueuedConnection, Q_ARG(QUrl, inputUrl), From d31ee52d794b811f193af54583d32afb9b57bc89 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 12 Jun 2018 03:51:55 -0700 Subject: [PATCH 66/78] revert PR-13162 from RC68 --- interface/src/avatar/AvatarMotionState.cpp | 47 +++++-------------- interface/src/avatar/AvatarMotionState.h | 6 --- .../src/avatars-renderer/Avatar.cpp | 14 +++--- libraries/physics/src/ObjectMotionState.h | 2 +- 4 files changed, 19 insertions(+), 50 deletions(-) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index beb7e34439..900c1c0a11 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -21,17 +21,6 @@ AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisi _type = MOTIONSTATE_TYPE_AVATAR; } -void AvatarMotionState::handleEasyChanges(uint32_t& flags) { - ObjectMotionState::handleEasyChanges(flags); - if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && !_body->isActive()) { - _body->activate(); - } -} - -bool AvatarMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) { - return ObjectMotionState::handleHardAndEasyChanges(flags, engine); -} - AvatarMotionState::~AvatarMotionState() { assert(_avatar); _avatar = nullptr; @@ -57,9 +46,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { const btCollisionShape* AvatarMotionState::computeNewShape() { ShapeInfo shapeInfo; std::static_pointer_cast(_avatar)->computeShapeInfo(shapeInfo); - glm::vec3 halfExtents = shapeInfo.getHalfExtents(); - halfExtents.y = 0.0f; - _diameter = 2.0f * glm::length(halfExtents); return getShapeManager()->getShape(shapeInfo); } @@ -74,31 +60,25 @@ void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const { worldTrans.setRotation(glmToBullet(getObjectRotation())); if (_body) { _body->setLinearVelocity(glmToBullet(getObjectLinearVelocity())); - _body->setAngularVelocity(glmToBullet(getObjectAngularVelocity())); + _body->setAngularVelocity(glmToBullet(getObjectLinearVelocity())); } } // virtual void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) { + // HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform + // as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie + // the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later. const float SPRING_TIMESCALE = 0.5f; float tau = PHYSICS_ENGINE_FIXED_SUBSTEP / SPRING_TIMESCALE; btVector3 currentPosition = worldTrans.getOrigin(); - btVector3 offsetToTarget = glmToBullet(getObjectPosition()) - currentPosition; - float distance = offsetToTarget.length(); - if ((1.0f - tau) * distance > _diameter) { - // the avatar body is far from its target --> slam position - btTransform newTransform; - newTransform.setOrigin(currentPosition + offsetToTarget); - newTransform.setRotation(glmToBullet(getObjectRotation())); - _body->setWorldTransform(newTransform); - _body->setLinearVelocity(glmToBullet(getObjectLinearVelocity())); - _body->setAngularVelocity(glmToBullet(getObjectAngularVelocity())); - } else { - // the avatar body is near its target --> slam velocity - btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget; - _body->setLinearVelocity(velocity); - _body->setAngularVelocity(glmToBullet(getObjectAngularVelocity())); - } + btVector3 targetPosition = glmToBullet(getObjectPosition()); + btTransform newTransform; + newTransform.setOrigin((1.0f - tau) * currentPosition + tau * targetPosition); + newTransform.setRotation(glmToBullet(getObjectRotation())); + _body->setWorldTransform(newTransform); + _body->setLinearVelocity(glmToBullet(getObjectLinearVelocity())); + _body->setAngularVelocity(glmToBullet(getObjectLinearVelocity())); } // These pure virtual methods must be implemented for each MotionState type @@ -165,8 +145,3 @@ void AvatarMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma mask = Physics::getDefaultCollisionMask(group); } -// virtual -float AvatarMotionState::getMass() const { - return std::static_pointer_cast(_avatar)->computeMass(); -} - diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index 73fb853312..90bd2a60ac 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -23,9 +23,6 @@ class AvatarMotionState : public ObjectMotionState { public: AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape); - virtual void handleEasyChanges(uint32_t& flags) override; - virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; - virtual PhysicsMotionType getMotionType() const override { return _motionType; } virtual uint32_t getIncomingDirtyFlags() override; @@ -67,8 +64,6 @@ public: virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; - virtual float getMass() const override; - friend class AvatarManager; friend class Avatar; @@ -81,7 +76,6 @@ protected: virtual const btCollisionShape* computeNewShape() override; AvatarSharedPointer _avatar; - float _diameter { 0.0f }; uint32_t _dirtyFlags; }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index da829b23e4..048b8b1633 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -799,6 +799,7 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { return true; } +// virtual void Avatar::simulateAttachments(float deltaTime) { assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size()); PerformanceTimer perfTimer("attachments"); @@ -1481,14 +1482,12 @@ void Avatar::updateDisplayNameAlpha(bool showDisplayName) { } } +// virtual void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) { float uniformScale = getModelScale(); - float radius = uniformScale * _skeletonModel->getBoundingCapsuleRadius(); - float height = uniformScale * _skeletonModel->getBoundingCapsuleHeight(); - shapeInfo.setCapsuleY(radius, 0.5f * height); - - glm::vec3 offset = uniformScale * _skeletonModel->getBoundingCapsuleOffset(); - shapeInfo.setOffset(offset); + shapeInfo.setCapsuleY(uniformScale * _skeletonModel->getBoundingCapsuleRadius(), + 0.5f * uniformScale * _skeletonModel->getBoundingCapsuleHeight()); + shapeInfo.setOffset(uniformScale * _skeletonModel->getBoundingCapsuleOffset()); } void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) { @@ -1511,8 +1510,9 @@ float Avatar::computeMass() { return _density * TWO_PI * radius * radius * (glm::length(end - start) + 2.0f * radius / 3.0f); } +// virtual void Avatar::rebuildCollisionShape() { - addPhysicsFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); + addPhysicsFlags(Simulation::DIRTY_SHAPE); } void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index e1cf5a4285..fbda9366fc 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -111,7 +111,7 @@ public: virtual PhysicsMotionType getMotionType() const { return _motionType; } void setMass(float mass); - virtual float getMass() const; + float getMass() const; void setBodyLinearVelocity(const glm::vec3& velocity) const; void setBodyAngularVelocity(const glm::vec3& velocity) const; From 649ffbe259d0563b41bea09ec356b875e1b483cc Mon Sep 17 00:00:00 2001 From: Gabriel Date: Tue, 12 Jun 2018 16:15:54 -0300 Subject: [PATCH 67/78] Don't hide jump button while moving --- .../display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 09b9b7f8f9..d8b8cbd54a 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -151,11 +151,9 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() { batch.setModelTransform(stickTransform); batch.draw(gpu::TRIANGLE_STRIP, 4); - if (!virtualPadManager.getLeftVirtualPad()->isBeingTouched()) { - batch.setResourceTexture(0, _virtualPadJumpBtnTexture); - batch.setModelTransform(jumpTransform); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } + batch.setResourceTexture(0, _virtualPadJumpBtnTexture); + batch.setModelTransform(jumpTransform); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); } #endif From 05d3f9286704194ae121effbf26c909e7ab0cbbd Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 12 Jun 2018 13:18:44 -0700 Subject: [PATCH 68/78] drillDownToPlace and iStacked based on server data. --- interface/resources/qml/hifi/Feed.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index d9e93c2fa7..4cd52a582d 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -86,7 +86,9 @@ Column { tags: tags, description: description, online_users: data.details.connections || data.details.concurrency || 0, - drillDownToPlace: false + // Server currently doesn't give isStacked (undefined). Could give bool. + drillDownToPlace: (data.isStacked === undefined) ? (data.action !== 'concurrency') : data.isStacked, + isStacked: !!data.isStacked }; } @@ -124,6 +126,7 @@ Column { onlineUsers: model.online_users; storyId: model.metaverseId; drillDownToPlace: model.drillDownToPlace; + isStacked: model.isStacked; textPadding: root.textPadding; smallMargin: root.smallMargin; From 4713d8d44ca7deb534eea0c59fa6e15ab681946d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Jun 2018 16:06:17 -0700 Subject: [PATCH 69/78] fix reference to builds URL for sandbox check --- server-console/src/modules/hf-updater.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/modules/hf-updater.js b/server-console/src/modules/hf-updater.js index d20da3c663..8362174c5d 100644 --- a/server-console/src/modules/hf-updater.js +++ b/server-console/src/modules/hf-updater.js @@ -58,7 +58,7 @@ util.inherits(UpdateChecker, events.EventEmitter); UpdateChecker.prototype = extend(UpdateChecker.prototype, { checkForUpdates: function() { log.debug("Checking for updates"); - request(BUILDS_URL, (error, response, body) => { + request(this.buildsURL, (error, response, body) => { if (error) { log.debug("Error", error); return; From 395767ed80ae15ce4bc27080b356d4e3da42f254 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Tue, 12 Jun 2018 20:11:22 -0300 Subject: [PATCH 70/78] Set android versionCode and versionName through gradle parameter RELEASE_NUMBER --- android/app/build.gradle | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 699008092c..f780abdea0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -4,12 +4,15 @@ android { compileSdkVersion 26 //buildToolsVersion '27.0.3' + def appVersionCode = Integer.valueOf(RELEASE_NUMBER ?: 1) + def appVersionName = RELEASE_NUMBER ?: "1.0" + defaultConfig { applicationId "io.highfidelity.hifiinterface" minSdkVersion 24 targetSdkVersion 26 - versionCode 1 - versionName "1.0" + versionCode appVersionCode + versionName appVersionName ndk { abiFilters 'arm64-v8a' } externalNativeBuild { cmake { From 016a6dcd84be77e284f34f3d8f5a4fb06c936097 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 10 Apr 2018 15:42:42 -0700 Subject: [PATCH 71/78] use ref for HDR_FORMAT to avoid static order --- libraries/image/src/image/Image.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 696e311495..584b1f421d 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -37,7 +37,9 @@ bool DEV_DECIMATE_TEXTURES = false; std::atomic DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; -static const auto HDR_FORMAT = gpu::Element::COLOR_R11G11B10; +// we use a ref here to work around static order initialization +// possibly causing the element not to be constructed yet +static const auto& HDR_FORMAT = gpu::Element::COLOR_R11G11B10; static std::atomic compressColorTextures { false }; static std::atomic compressNormalTextures { false }; From a558f71fcd752ed4d41164c345f0b831878f605f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Jun 2018 15:11:57 -0700 Subject: [PATCH 72/78] Fix resoruces path URLs not working with Script.resolvePath Depending on the context, "/~/" means different things. The ScriptEngine resolves this path to `/scripts`, but everywhere else it resolves to `/resources`. This change forces us to expand those paths instead of storing the "/~/" path, which will be incorrectly resolved once the ScriptEnging is using it. --- .../src/EntityTreeRenderer.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 600d1c32a8..52312e64c3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -44,6 +44,19 @@ size_t std::hash::operator()(const EntityItemID& id) const { retur std::function EntityTreeRenderer::_entitiesShouldFadeFunction; std::function EntityTreeRenderer::_renderDebugHullsOperator = [] { return false; }; +QString resolveScriptURL(const QString& scriptUrl) { + auto normalizedScriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); + QUrl url { normalizedScriptUrl }; + if (url.isLocalFile()) { + // Outside of the ScriptEngine, /~/ resolves to the /resources directory. + // Inside of the ScriptEngine, /~/ resolves to the /scripts directory. + // Here we expand local paths in case they are /~/ paths, so they aren't + // incorrectly recognized as being located in /scripts when utilized in ScriptEngine. + return PathUtils::expandToLocalDataAbsolutePath(url).toString(); + } + return normalizedScriptUrl; +} + EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : _wantScripts(wantScripts), @@ -221,7 +234,7 @@ void EntityTreeRenderer::reloadEntityScripts() { const auto& renderer = entry.second; const auto& entity = renderer->getEntity(); if (!entity->getScript().isEmpty()) { - _entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true); + _entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), resolveScriptURL(entity->getScript()), true); } } } @@ -914,8 +927,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool entity->scriptHasUnloaded(); } if (shouldLoad) { - scriptUrl = DependencyManager::get()->normalizeURL(scriptUrl); - _entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload); + _entitiesScriptEngine->loadEntityScript(entityID, resolveScriptURL(scriptUrl), reload); entity->scriptHasPreloaded(); } } From 569f7dc308957ce0f76dd1176e8f069fcec0b510 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 13 Jun 2018 08:33:06 -0700 Subject: [PATCH 73/78] Fix crash in image baking due to undefined mip format A recent change caused formatMip to no longer be specified. This causes an issue deeper in the code when generating mips, causing a Q_UNREACHABLE to be hit. --- libraries/image/src/image/Image.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 5ddcfa9fb4..dc45257d2b 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -835,6 +835,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma } else { formatGPU = gpu::Element::COLOR_COMPRESSED_BCX_SRGB; } + formatMip = formatGPU; } else { #ifdef USE_GLES // GLES does not support GL_BGRA From 4e230c9e5dc04d3fa152a62a50c5fa5c6999dd56 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 13 Jun 2018 11:23:37 -0700 Subject: [PATCH 74/78] Remove redundant setvbuf and update signal/slot syntax in OctreeServer --- assignment-client/src/octree/OctreeServer.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index c2fbc8ab37..2b2aaeb3e9 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1207,12 +1207,8 @@ void OctreeServer::beginRunning() { beforeRun(); // after payload has been processed - connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); - connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - -#ifndef WIN32 - setvbuf(stdout, nullptr, _IOLBF, 0); -#endif + connect(nodeList.data(), &NodeList::nodeAdded, &OctreeServer::nodeAdded); + connect(nodeList.data(), &NodeList::nodeKilled, &OctreeServer::nodeKilled); nodeList->linkedDataCreateCallback = [this](Node* node) { auto queryNodeData = createOctreeQueryNode(); From e49df4a0ab4d69b977a6152c452e111a9ac3a706 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 13 Jun 2018 11:42:18 -0700 Subject: [PATCH 75/78] Fix MS15858: Prevent Interface deadlock during 360 Snap capture --- .../marketplace/spectator-camera/SpectatorCamera.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml index 1b3698acd8..e0a503fcc8 100644 --- a/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml +++ b/unpublishedScripts/marketplace/spectator-camera/SpectatorCamera.qml @@ -205,7 +205,7 @@ Rectangle { // Spectator Camera Preview Hifi.ResourceImageItem { id: spectatorCameraPreview; - visible: masterSwitch.checked; + visible: masterSwitch.checked && !root.processing360Snapshot; url: showCameraView.checked || !HMD.active ? "resource://spectatorCameraFrame" : "resource://hmdPreviewFrame"; ready: masterSwitch.checked; mirrorVertically: true; From 6b159d6124b4c127f4b19ee352264f568b842ca9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 13 Jun 2018 11:54:20 -0700 Subject: [PATCH 76/78] Remove redundant read of octree data on startup --- libraries/octree/src/OctreePersistThread.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 8f55953360..d783a1effd 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -109,9 +109,12 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerreadPrimitive(&includesNewData); QByteArray replacementData; + OctreeUtils::RawOctreeData data; + bool hasValidOctreeData { false }; if (includesNewData) { replacementData = message->readAll(); replaceData(replacementData); + hasValidOctreeData = data.readOctreeDataInfoFromFile(_filename); qDebug() << "Got OctreeDataFileReply, new data sent"; } else { qDebug() << "Got OctreeDataFileReply, current entity data is sufficient"; @@ -119,6 +122,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointersetOctreeVersionInfo(data.id, data.version); } From c2f08dfdba2a8b71fc84f154b08b173670fcccf0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 13 Jun 2018 12:07:20 -0700 Subject: [PATCH 77/78] Update Octree to atomically persist data --- assignment-client/src/octree/OctreeServer.cpp | 4 +-- libraries/octree/src/Octree.cpp | 7 +++-- libraries/octree/src/OctreePersistThread.cpp | 31 +++---------------- libraries/octree/src/OctreePersistThread.h | 2 -- 4 files changed, 12 insertions(+), 32 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 2b2aaeb3e9..e993bea358 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1207,8 +1207,8 @@ void OctreeServer::beginRunning() { beforeRun(); // after payload has been processed - connect(nodeList.data(), &NodeList::nodeAdded, &OctreeServer::nodeAdded); - connect(nodeList.data(), &NodeList::nodeKilled, &OctreeServer::nodeKilled); + connect(nodeList.data(), &NodeList::nodeAdded, this, &OctreeServer::nodeAdded); + connect(nodeList.data(), &NodeList::nodeKilled, this, &OctreeServer::nodeKilled); nodeList->linkedDataCreateCallback = [this](Node* node) { auto queryNodeData = createOctreeQueryNode(); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5f943fabf2..d9da06221d 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -972,10 +973,12 @@ bool Octree::writeToJSONFile(const char* fileName, const OctreeElementPointer& e return false; } - QFile persistFile(fileName); + QSaveFile persistFile(fileName); bool success = false; if (persistFile.open(QIODevice::WriteOnly)) { - success = persistFile.write(jsonDataForFile) != -1; + if (persistFile.write(jsonDataForFile) != -1) { + success = persistFile.commit(); + } } else { qCritical("Could not write to JSON description of entities."); } diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index d783a1effd..8b1d766418 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -62,19 +62,6 @@ OctreePersistThread::OctreePersistThread(OctreePointer tree, const QString& file void OctreePersistThread::start() { cleanupOldReplacementBackups(); - QFile tempFile { getTempFilename() }; - if (tempFile.exists()) { - qWarning(octree) << "Found temporary octree file at" << tempFile.fileName(); - qDebug(octree) << "Attempting to recover from temporary octree file"; - QFile::remove(_filename); - if (tempFile.rename(_filename)) { - qDebug(octree) << "Successfully recovered from temporary octree file"; - } else { - qWarning(octree) << "Failed to recover from temporary octree file"; - tempFile.remove(); - } - } - auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::OctreeDataFileReply, this, "handleOctreeDataFileReply"); @@ -182,7 +169,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerincrementPersistDataVersion(); - QString tempFilename = getTempFilename(); - qCDebug(octree) << "Saving temporary Octree file to:" << tempFilename; - if (_tree->writeToFile(tempFilename.toLocal8Bit().constData(), nullptr, _persistAsFileType)) { - QFile tempFile { tempFilename }; + qCDebug(octree) << "Saving Octree data to:" << _filename; + if (_tree->writeToFile(_filename.toLocal8Bit().constData(), nullptr, _persistAsFileType)) { _tree->clearDirtyBit(); // tree is clean after saving - QFile::remove(_filename); - if (tempFile.rename(_filename)) { - qCDebug(octree) << "DONE moving temporary Octree file to" << _filename; - } else { - qCWarning(octree) << "Failed to move temporary Octree file to " << _filename; - tempFile.remove(); - } + qCDebug(octree) << "DONE persisting Octree data to" << _filename; } else { - qCWarning(octree) << "Failed to open temp Octree file at" << tempFilename; + qCWarning(octree) << "Failed to persist Octree data to" << _filename; } sendLatestEntityDataToDS(); diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 5bb6e51e5f..0044a8fa5a 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -65,8 +65,6 @@ protected: void replaceData(QByteArray data); void sendLatestEntityDataToDS(); - QString getTempFilename() const { return _filename + ".temp"; } - private: OctreePointer _tree; QString _filename; From 2b49d84a229c3bb091cc7c9b63e0a0adf2d7e7b0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 13 Jun 2018 12:57:06 -0700 Subject: [PATCH 78/78] Add better error logging in Octree::writeToJSONFile --- libraries/octree/src/Octree.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d9da06221d..f0e41d59ea 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -978,9 +978,14 @@ bool Octree::writeToJSONFile(const char* fileName, const OctreeElementPointer& e if (persistFile.open(QIODevice::WriteOnly)) { if (persistFile.write(jsonDataForFile) != -1) { success = persistFile.commit(); + if (!success) { + qCritical() << "Failed to commit to JSON save file:" << persistFile.errorString(); + } + } else { + qCritical("Failed to write to JSON file."); } } else { - qCritical("Could not write to JSON description of entities."); + qCritical("Failed to open JSON file for writing."); } return success;