diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 18d93bde40..286d18e2b5 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -6,8 +6,8 @@ if (WIN32) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/qtaudio_wasapi12.zip - URL_MD5 9e2eef41165f85344808f754b48bf08d + URL https://public.highfidelity.com/dependencies/qtaudio_wasapi13.zip + URL_MD5 aa56a45f19c18caee13d29a40d1d7d28 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" diff --git a/cmake/ports/hifi-deps/CONTROL b/cmake/ports/hifi-deps/CONTROL index 2441de9002..4cf952ccf0 100644 --- a/cmake/ports/hifi-deps/CONTROL +++ b/cmake/ports/hifi-deps/CONTROL @@ -1,4 +1,4 @@ Source: hifi-deps -Version: 0.1 +Version: 0.3 Description: Collected dependencies for High Fidelity applications Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib, webrtc (!android) diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py index 7bb261faa0..ebecca6226 100644 --- a/hifi_vcpkg.py +++ b/hifi_vcpkg.py @@ -265,7 +265,7 @@ endif() if platform.system() == 'Windows': url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-windows3.tar.gz' elif platform.system() == 'Darwin': - url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-macos3.tar.gz' + url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-macos.tar.gz?versionId=bLAgnoJ8IMKpqv8NFDcAu8hsyQy3Rwwz' elif platform.system() == 'Linux': if platform.linux_distribution()[1][:3] == '16.': url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-16.04-with-symbols.tar.gz' diff --git a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml index 8634648d17..3b6502cc98 100644 --- a/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml +++ b/interface/resources/qml/hifi/dialogs/graphics/GraphicsSettings.qml @@ -133,15 +133,12 @@ Item { ListElement { text: "Low World Detail" - worldDetailQualityValue: 0.25 } ListElement { text: "Medium World Detail" - worldDetailQualityValue: 0.5 } ListElement { text: "Full World Detail" - worldDetailQualityValue: 0.75 } } @@ -158,14 +155,7 @@ Item { currentIndex: -1 function refreshWorldDetailDropdown() { - var currentWorldDetailQuality = LODManager.worldDetailQuality; - if (currentWorldDetailQuality <= 0.25) { - worldDetailDropdown.currentIndex = 0; - } else if (currentWorldDetailQuality <= 0.5) { - worldDetailDropdown.currentIndex = 1; - } else { - worldDetailDropdown.currentIndex = 2; - } + worldDetailDropdown.currentIndex = LODManager.worldDetailQuality; } Component.onCompleted: { @@ -173,7 +163,7 @@ Item { } onCurrentIndexChanged: { - LODManager.worldDetailQuality = model.get(currentIndex).worldDetailQualityValue; + LODManager.worldDetailQuality = currentIndex; worldDetailDropdown.displayText = model.get(currentIndex).text; } } diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/audio/Audio.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/audio/Audio.qml index 108016ef8c..345c124725 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/audio/Audio.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/audio/Audio.qml @@ -258,13 +258,12 @@ Flickable { Layout.preferredHeight: contentItem.height Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin interactive: false - spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons clip: true model: AudioScriptingInterface.devices.input delegate: Item { - width: parent.width - height: inputDeviceCheckbox.height - + width: parent.width + height: model.type != "hmd" ? inputDeviceCheckbox.height + simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0 + visible: model.type != "hmd" SimplifiedControls.RadioButton { id: inputDeviceCheckbox anchors.left: parent.left @@ -354,13 +353,12 @@ Flickable { Layout.preferredHeight: contentItem.height Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin interactive: false - spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons clip: true model: AudioScriptingInterface.devices.output delegate: Item { width: parent.width - height: outputDeviceCheckbox.height - + height: model.type != "hmd" ? outputDeviceCheckbox.height +simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0 + visible: model.type != "hmd" SimplifiedControls.RadioButton { id: outputDeviceCheckbox anchors.left: parent.left diff --git a/interface/resources/qml/hifi/simplifiedUI/settingsApp/vr/VR.qml b/interface/resources/qml/hifi/simplifiedUI/settingsApp/vr/VR.qml index 420ee11a05..cc64f8bd9f 100644 --- a/interface/resources/qml/hifi/simplifiedUI/settingsApp/vr/VR.qml +++ b/interface/resources/qml/hifi/simplifiedUI/settingsApp/vr/VR.qml @@ -259,13 +259,13 @@ Flickable { Layout.preferredHeight: contentItem.height Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin interactive: false - spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons clip: true model: AudioScriptingInterface.devices.input delegate: Item { - width: parent.width - height: inputDeviceCheckbox.height - + width: parent.width + height: model.type != "desktop" ? inputDeviceCheckbox.height + simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0 + visible: model.type != "desktop" + SimplifiedControls.RadioButton { id: inputDeviceCheckbox anchors.left: parent.left @@ -355,13 +355,12 @@ Flickable { Layout.preferredHeight: contentItem.height Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin interactive: false - spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons clip: true model: AudioScriptingInterface.devices.output delegate: Item { width: parent.width - height: outputDeviceCheckbox.height - + height: model.type != "desktop" ? outputDeviceCheckbox.height + simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0 + visible: model.type != "desktop" SimplifiedControls.RadioButton { id: outputDeviceCheckbox anchors.left: parent.left diff --git a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml index 65a5eb0c80..812971dc3a 100644 --- a/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml +++ b/interface/resources/qml/hifi/simplifiedUI/topBar/SimplifiedTopBar.qml @@ -429,7 +429,7 @@ Rectangle { SimplifiedControls.TextField { id: goToTextField readonly property string shortPlaceholderText: "Jump to..." - readonly property string longPlaceholderText: "Type the name of a location to quickly jump there..." + readonly property string longPlaceholderText: "Quickly jump to a location by typing '/LocationName'" anchors.centerIn: parent width: Math.min(parent.width, 445) height: 35 diff --git a/interface/resources/serverless/empty.json b/interface/resources/serverless/empty.json new file mode 100644 index 0000000000..6431284d9b --- /dev/null +++ b/interface/resources/serverless/empty.json @@ -0,0 +1,10 @@ +{ + "DataVersion": 3, + "Paths": { + "/": "/0, 0, 0/0,0,0,0" + }, + "Entities": [ + ], + "Id": "{5807d519-eb7d-496d-b22a-0820811291c9}", + "Version": 120 +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 994c9c7fd3..dc8e502758 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5845,14 +5845,14 @@ void Application::pushPostUpdateLambda(void* key, const std::function& f // to everyone. // The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition(). // Note that it is called BEFORE we update position or joints based on sensors, etc. -void Application::updateMyAvatarLookAtPosition() { +void Application::updateMyAvatarLookAtPosition(float deltaTime) { PerformanceTimer perfTimer("lookAt"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); auto myAvatar = getMyAvatar(); FaceTracker* faceTracker = getActiveFaceTracker(); - myAvatar->updateLookAtPosition(faceTracker, _myCamera); + myAvatar->updateEyesLookAtPosition(faceTracker, _myCamera, deltaTime); } void Application::updateThreads(float deltaTime) { @@ -6636,7 +6636,7 @@ void Application::update(float deltaTime) { { PROFILE_RANGE(simulation, "MyAvatar"); PerformanceTimer perfTimer("MyAvatar"); - qApp->updateMyAvatarLookAtPosition(); + qApp->updateMyAvatarLookAtPosition(deltaTime); avatarManager->updateMyAvatar(deltaTime); } } @@ -7516,6 +7516,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get().data()); scriptEngine->registerGlobalObject("LODManager", DependencyManager::get().data()); + qScriptRegisterMetaType(scriptEngine.data(), worldDetailQualityToScriptValue, worldDetailQualityFromScriptValue); scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Performance", new PerformanceScriptingInterface()); diff --git a/interface/src/Application.h b/interface/src/Application.h index e3334d12d6..3e7126564d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -292,7 +292,7 @@ public: virtual void pushPostUpdateLambda(void* key, const std::function& func) override; - void updateMyAvatarLookAtPosition(); + void updateMyAvatarLookAtPosition(float deltaTime); float getGameLoopRate() const { return _gameLoopCounter.rate(); } diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 3e47d88f3c..4cd5025fc1 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -19,11 +19,8 @@ #include "ui/DialogsManager.h" #include "InterfaceLogging.h" -const float LODManager::DEFAULT_DESKTOP_LOD_DOWN_FPS = LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS; -const float LODManager::DEFAULT_HMD_LOD_DOWN_FPS = LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS; - -Setting::Handle desktopLODDecreaseFPS("desktopLODDecreaseFPS", LODManager::DEFAULT_DESKTOP_LOD_DOWN_FPS); -Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", LODManager::DEFAULT_HMD_LOD_DOWN_FPS); +Setting::Handle desktopWorldDetailQuality("desktopWorldDetailQuality", (int)DEFAULT_WORLD_DETAIL_QUALITY); +Setting::Handle hmdWorldDetailQuality("hmdWorldDetailQuality", (int)DEFAULT_WORLD_DETAIL_QUALITY); LODManager::LODManager() { } @@ -326,19 +323,21 @@ QString LODManager::getLODFeedbackText() { } void LODManager::loadSettings() { - setDesktopLODTargetFPS(desktopLODDecreaseFPS.get()); - Setting::Handle firstRun { Settings::firstRun, true }; + auto desktopQuality = static_cast(desktopWorldDetailQuality.get()); + auto hmdQuality = static_cast(hmdWorldDetailQuality.get()); + + Setting::Handle firstRun{ Settings::firstRun, true }; if (qApp->property(hifi::properties::OCULUS_STORE).toBool() && firstRun.get()) { - const float LOD_HIGH_QUALITY_LEVEL = 0.75f; - setHMDLODTargetFPS(LOD_HIGH_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS); - } else { - setHMDLODTargetFPS(hmdLODDecreaseFPS.get()); + hmdQuality = WORLD_DETAIL_HIGH; } + + setWorldDetailQuality(desktopQuality, false); + setWorldDetailQuality(hmdQuality, true); } void LODManager::saveSettings() { - desktopLODDecreaseFPS.set(getDesktopLODTargetFPS()); - hmdLODDecreaseFPS.set(getHMDLODTargetFPS()); + desktopWorldDetailQuality.set((int)_desktopWorldDetailQuality); + hmdWorldDetailQuality.set((int)_hmdWorldDetailQuality); } const float MIN_DECREASE_FPS = 0.5f; @@ -393,54 +392,33 @@ float LODManager::getLODTargetFPS() const { } } -void LODManager::setWorldDetailQuality(float quality) { - static const float MIN_FPS = 10; - static const float LOW = 0.25f; - - bool isLowestValue = quality == LOW; - bool isHMDMode = qApp->isHMDMode(); - - float maxFPS = isHMDMode ? LOD_MAX_LIKELY_HMD_FPS : LOD_MAX_LIKELY_DESKTOP_FPS; - float desiredFPS = maxFPS; - - if (!isLowestValue) { - float calculatedFPS = (maxFPS - (maxFPS * quality)); - desiredFPS = calculatedFPS < MIN_FPS ? MIN_FPS : calculatedFPS; - } - +void LODManager::setWorldDetailQuality(WorldDetailQuality quality, bool isHMDMode) { + float desiredFPS = isHMDMode ? QUALITY_TO_FPS_HMD[quality] : QUALITY_TO_FPS_DESKTOP[quality]; if (isHMDMode) { + _hmdWorldDetailQuality = quality; setHMDLODTargetFPS(desiredFPS); } else { + _desktopWorldDetailQuality = quality; setDesktopLODTargetFPS(desiredFPS); } - +} + +void LODManager::setWorldDetailQuality(WorldDetailQuality quality) { + setWorldDetailQuality(quality, qApp->isHMDMode()); emit worldDetailQualityChanged(); } -float LODManager::getWorldDetailQuality() const { +WorldDetailQuality LODManager::getWorldDetailQuality() const { + return qApp->isHMDMode() ? _hmdWorldDetailQuality : _desktopWorldDetailQuality; +} - static const float LOW = 0.25f; - static const float MEDIUM = 0.5f; - static const float HIGH = 0.75f; +QScriptValue worldDetailQualityToScriptValue(QScriptEngine* engine, const WorldDetailQuality& worldDetailQuality) { + return worldDetailQuality; +} - bool inHMD = qApp->isHMDMode(); - - float targetFPS = 0.0f; - if (inHMD) { - targetFPS = getHMDLODTargetFPS(); - } else { - targetFPS = getDesktopLODTargetFPS(); - } - float maxFPS = inHMD ? LOD_MAX_LIKELY_HMD_FPS : LOD_MAX_LIKELY_DESKTOP_FPS; - float percentage = 1.0f - targetFPS / maxFPS; - - if (percentage <= LOW) { - return LOW; - } else if (percentage <= MEDIUM) { - return MEDIUM; - } - - return HIGH; +void worldDetailQualityFromScriptValue(const QScriptValue& object, WorldDetailQuality& worldDetailQuality) { + worldDetailQuality = + static_cast(std::min(std::max(object.toInt32(), (int)WORLD_DETAIL_LOW), (int)WORLD_DETAIL_HIGH)); } void LODManager::setLODQualityLevel(float quality) { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index 3dafa8c800..4708deb61b 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -23,26 +23,52 @@ #include +/**jsdoc + *

The world detail quality rendered.

+ * + * + * + * + * + * + * + * + * + *
ValueDescription
0Low world detail quality.
1Medium world detail quality.
2High world detail quality.
+ * @typedef {number} LODManager.WorldDetailQuality + */ +enum WorldDetailQuality { + WORLD_DETAIL_LOW = 0, + WORLD_DETAIL_MEDIUM, + WORLD_DETAIL_HIGH +}; +Q_DECLARE_METATYPE(WorldDetailQuality); + #ifdef Q_OS_ANDROID const float LOD_DEFAULT_QUALITY_LEVEL = 0.2f; // default quality level setting is High (lower framerate) #else const float LOD_DEFAULT_QUALITY_LEVEL = 0.5f; // default quality level setting is Mid #endif -const float LOD_MAX_LIKELY_DESKTOP_FPS = 60.0f; // this is essentially, V-synch fps + #ifdef Q_OS_ANDROID -const float LOD_MAX_LIKELY_HMD_FPS = 36.0f; // this is essentially, V-synch fps +const WorldDetailQuality DEFAULT_WORLD_DETAIL_QUALITY = WORLD_DETAIL_LOW; +const std::vector QUALITY_TO_FPS_DESKTOP = { 60.0f, 30.0f, 15.0f }; +const std::vector QUALITY_TO_FPS_HMD = { 25.0f, 16.0f, 10.0f }; #else -const float LOD_MAX_LIKELY_HMD_FPS = 90.0f; // this is essentially, V-synch fps +const WorldDetailQuality DEFAULT_WORLD_DETAIL_QUALITY = WORLD_DETAIL_MEDIUM; +const std::vector QUALITY_TO_FPS_DESKTOP = { 60.0f, 30.0f, 15.0f }; +const std::vector QUALITY_TO_FPS_HMD = { 90.0f, 45.0f, 22.5f }; #endif const float LOD_OFFSET_FPS = 5.0f; // offset of FPS to add for computing the target framerate class AABox; + /**jsdoc * The LOD class manages your Level of Detail functions within Interface. * @namespace LODManager - * + * * @hifi-interface * @hifi-client-entity * @hifi-avatar @@ -51,12 +77,12 @@ class AABox; * @property {number} engineRunTime Read-only. * @property {number} gpuTime Read-only. */ - class LODManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - Q_PROPERTY(float worldDetailQuality READ getWorldDetailQuality WRITE setWorldDetailQuality NOTIFY worldDetailQualityChanged) + Q_PROPERTY(WorldDetailQuality worldDetailQuality READ getWorldDetailQuality WRITE setWorldDetailQuality + NOTIFY worldDetailQualityChanged) Q_PROPERTY(float lodQualityLevel READ getLODQualityLevel WRITE setLODQualityLevel NOTIFY lodQualityLevelChanged) @@ -193,8 +219,8 @@ public: float getSmoothRenderTime() const { return _smoothRenderTime; }; float getSmoothRenderFPS() const { return (_smoothRenderTime > 0.0f ? (float)MSECS_PER_SECOND / _smoothRenderTime : 0.0f); }; - void setWorldDetailQuality(float quality); - float getWorldDetailQuality() const; + void setWorldDetailQuality(WorldDetailQuality quality); + WorldDetailQuality getWorldDetailQuality() const; void setLODQualityLevel(float quality); float getLODQualityLevel() const; @@ -220,9 +246,6 @@ public: float getPidOd() const; float getPidO() const; - static const float DEFAULT_DESKTOP_LOD_DOWN_FPS; - static const float DEFAULT_HMD_LOD_DOWN_FPS; - signals: /**jsdoc @@ -244,6 +267,8 @@ signals: private: LODManager(); + void setWorldDetailQuality(WorldDetailQuality quality, bool isHMDMode); + std::mutex _automaticLODLock; bool _automaticLODAdjust = true; @@ -258,8 +283,11 @@ private: float _lodQualityLevel{ LOD_DEFAULT_QUALITY_LEVEL }; - float _desktopTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS }; - float _hmdTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS }; + WorldDetailQuality _desktopWorldDetailQuality { DEFAULT_WORLD_DETAIL_QUALITY }; + WorldDetailQuality _hmdWorldDetailQuality { DEFAULT_WORLD_DETAIL_QUALITY }; + + float _desktopTargetFPS { QUALITY_TO_FPS_DESKTOP[_desktopWorldDetailQuality] }; + float _hmdTargetFPS { QUALITY_TO_FPS_HMD[_hmdWorldDetailQuality] }; float _lodHalfAngle = getHalfAngleFromVisibilityDistance(DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT); int _boundaryLevelAdjust = 0; @@ -269,4 +297,7 @@ private: glm::vec4 _pidOutputs{ 0.0f }; }; +QScriptValue worldDetailQualityToScriptValue(QScriptEngine* engine, const WorldDetailQuality& worldDetailQuality); +void worldDetailQualityFromScriptValue(const QScriptValue& object, WorldDetailQuality& worldDetailQuality); + #endif // hifi_LODManager_h diff --git a/interface/src/PerformanceManager.cpp b/interface/src/PerformanceManager.cpp index 80c09e3fec..a7b9eff7cc 100644 --- a/interface/src/PerformanceManager.cpp +++ b/interface/src/PerformanceManager.cpp @@ -92,7 +92,7 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP RenderScriptingInterface::getInstance()->setShadowsEnabled(true); qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME); - DependencyManager::get()->setWorldDetailQuality(0.75f); + DependencyManager::get()->setWorldDetailQuality(WORLD_DETAIL_HIGH); break; case PerformancePreset::MID: @@ -104,7 +104,7 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP RenderScriptingInterface::getInstance()->setShadowsEnabled(false); qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::INTERACTIVE); - DependencyManager::get()->setWorldDetailQuality(0.5f); + DependencyManager::get()->setWorldDetailQuality(WORLD_DETAIL_MEDIUM); break; case PerformancePreset::LOW: @@ -114,7 +114,7 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommandedPpiScale); - DependencyManager::get()->setWorldDetailQuality(0.25f); + DependencyManager::get()->setWorldDetailQuality(WORLD_DETAIL_LOW); break; case PerformancePreset::UNKNOWN: diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6e0bfab69b..979d33ec5d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -906,6 +906,25 @@ void MyAvatar::simulate(float deltaTime, bool inView) { updateViewBoom(); } + // Head's look at blending needs updating + // before we perform rig animations and IK. + { + PerformanceTimer perfTimer("lookat"); + + CameraMode mode = qApp->getCamera().getMode(); + if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON || + mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { + if (!_pointAtActive || !_isPointTargetValid) { + updateHeadLookAt(deltaTime); + } else { + resetHeadLookAt(); + } + } else if (_headLookAtActive) { + resetHeadLookAt(); + _headLookAtActive = false; + } + } + // update sensorToWorldMatrix for camera and hand controllers // before we perform rig animations and IK. updateSensorToWorldMatrix(); @@ -957,18 +976,6 @@ void MyAvatar::simulate(float deltaTime, bool inView) { head->setPosition(headPosition); head->setScale(getModelScale()); head->simulate(deltaTime); - CameraMode mode = qApp->getCamera().getMode(); - if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON || - mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) { - if (!_pointAtActive || !_isPointTargetValid) { - updateHeadLookAt(deltaTime); - } else { - resetHeadLookAt(); - } - } else if (_headLookAtActive){ - resetHeadLookAt(); - _headLookAtActive = false; - } } // Record avatars movements. @@ -2151,7 +2158,7 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP const float DISTANCE_FACTOR = 3.14f; const float MY_ANGLE_FACTOR = 1.0f; const float OTHER_ANGLE_FACTOR = 1.0f; - const float OTHER_IS_TALKING_TERM = otherIsTalking ? 1.0f : 0.0f; + const float OTHER_IS_TALKING_TERM = otherIsTalking ? -1.0f : 0.0f; const float LOOKING_AT_OTHER_ALREADY_TERM = lookingAtOtherAlready ? -0.2f : 0.0f; const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; // meters @@ -2177,6 +2184,9 @@ static float lookAtCostFunction(const glm::vec3& myForward, const glm::vec3& myP void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) { glm::vec3 myForward = _lookAtYaw * IDENTITY_FORWARD; + if (_skeletonModel->isLoaded()) { + myForward = getHeadJointFrontVector(); + } glm::vec3 myPosition = getHead()->getEyePosition(); CameraMode mode = qApp->getCamera().getMode(); if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON) { @@ -2189,7 +2199,7 @@ void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) { foreach (const AvatarSharedPointer& avatarData, hash) { std::shared_ptr avatar = std::static_pointer_cast(avatarData); if (!avatar->isMyAvatar() && avatar->isInitialized()) { - glm::vec3 otherForward = avatar->getHead()->getForwardDirection(); + glm::vec3 otherForward = avatar->getHeadJointFrontVector(); glm::vec3 otherPosition = avatar->getHead()->getEyePosition(); const float TIME_WITHOUT_TALKING_THRESHOLD = 1.0f; bool otherIsTalking = avatar->getHead()->getTimeWithoutTalking() <= TIME_WITHOUT_TALKING_THRESHOLD; @@ -2277,7 +2287,9 @@ void MyAvatar::updateLookAtTargetAvatar() { AvatarHash hash = DependencyManager::get()->getHashCopy(); // determine what the best look at target for my avatar should be. - computeMyLookAtTarget(hash); + if (!_scriptControlsEyesLookAt) { + computeMyLookAtTarget(hash); + } // snap look at position for avatars that are looking at me. snapOtherAvatarLookAtTargetsToMe(hash); @@ -6612,7 +6624,7 @@ bool MyAvatar::getIsJointOverridden(int jointIndex) const { return _skeletonModel->getIsJointOverridden(jointIndex); } -void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera) { +void MyAvatar::updateEyesLookAtPosition(FaceTracker* faceTracker, Camera& myCamera, float deltaTime) { updateLookAtTargetAvatar(); @@ -6642,6 +6654,13 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera) } else { lookAtSpot = myHead->getEyePosition() + glm::normalize(leftVec) * 1000.0f; } + } else if (_scriptControlsEyesLookAt) { + if (_scriptEyesControlTimer < MAX_LOOK_AT_TIME_SCRIPT_CONTROL) { + _scriptEyesControlTimer += deltaTime; + lookAtSpot = _eyesLookAtTarget.get(); + } else { + _scriptControlsEyesLookAt = false; + } } else { controller::Pose leftEyePose = getControllerPoseInAvatarFrame(controller::Action::LEFT_EYE); controller::Pose rightEyePose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_EYE); @@ -6704,8 +6723,9 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera) if (headPose.isValid()) { lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE)); } else { - lookAtSpot = myHead->getEyePosition() + - (getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + lookAtSpot = _shouldTurnToFaceCamera ? + myHead->getLookAtPosition() : + myHead->getEyePosition() + getHeadJointFrontVector() * (float)TREE_SCALE; } } @@ -6725,7 +6745,7 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera) } } } - + _eyesLookAtTarget.set(lookAtSpot); getHead()->setLookAtPosition(lookAtSpot); } @@ -6808,6 +6828,17 @@ void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) { _lookAtScriptTarget = lookAtTarget; } +void MyAvatar::setEyesLookAt(const glm::vec3& lookAtTarget) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "setEyesLookAt", + Q_ARG(const glm::vec3&, lookAtTarget)); + return; + } + _eyesLookAtTarget.set(lookAtTarget); + _scriptEyesControlTimer = 0.0f; + _scriptControlsEyesLookAt = true; +} + glm::vec3 MyAvatar::getLookAtPivotPoint() { glm::vec3 avatarUp = getWorldOrientation() * Vectors::UP; glm::vec3 yAxisEyePosition = getWorldPosition() + avatarUp * glm::dot(avatarUp, _skeletonModel->getDefaultEyeModelPosition()); @@ -6900,4 +6931,3 @@ void MyAvatar::resetPointAt() { POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING); } } - diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 081fd00d5b..32ef16f811 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1765,10 +1765,26 @@ public: /**jsdoc * Returns the current head look at target point in world coordinates. * @function MyAvatar.getHeadLookAt - * @returns {Vec3} Default position between your avatar's eyes in world coordinates. + * @returns {Vec3} The head's look at target in world coordinates. */ Q_INVOKABLE glm::vec3 getHeadLookAt() { return _lookAtCameraTarget; } + /**jsdoc + * Force the avatar's eyes to look to the specified location. + * Once this method is called, API calls will have full control of the eyes for a limited time. + * If this method is not called for two seconds, the engine will regain control of the eyes. + * @function MyAvatar.setEyesLookAt + * @param {Vec3} lookAtTarget - The target point in world coordinates. + */ + Q_INVOKABLE void setEyesLookAt(const glm::vec3& lookAtTarget); + + /**jsdoc + * Returns the current eyes look at target point in world coordinates. + * @function MyAvatar.getEyesLookAt + * @returns {Vec3} The eyes's look at target in world coordinates. + */ + Q_INVOKABLE glm::vec3 getEyesLookAt() { return _eyesLookAtTarget.get(); } + /**jsdoc * Aims the pointing directional blending towards the provided target point. * The "point" reaction should be triggered before using this method. @@ -1906,7 +1922,7 @@ public: bool getFlowActive() const; bool getNetworkGraphActive() const; - void updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera); + void updateEyesLookAtPosition(FaceTracker* faceTracker, Camera& myCamera, float deltaTime); // sets the reaction enabled and triggered parameters of the passed in params // also clears internal reaction triggers @@ -2666,6 +2682,9 @@ private: eyeContactTarget _eyeContactTarget; float _eyeContactTargetTimer { 0.0f }; + ThreadSafeValueCache _eyesLookAtTarget { glm::vec3() }; + bool _scriptControlsEyesLookAt{ false }; + float _scriptEyesControlTimer{ 0.0f }; glm::vec3 _trackedHeadPosition; diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index 8cc45a3bf5..76c871fa5c 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -44,7 +44,8 @@ enum AudioDeviceRole { SelectedDesktopRole, SelectedHMDRole, PeakRole, - InfoRole + InfoRole, + TypeRole }; QHash AudioDeviceList::_roles { @@ -52,7 +53,8 @@ QHash AudioDeviceList::_roles { { SelectedDesktopRole, "selectedDesktop" }, { SelectedHMDRole, "selectedHMD" }, { PeakRole, "peak" }, - { InfoRole, "info" } + { InfoRole, "info" }, + { TypeRole, "type"} }; static QString getTargetDevice(bool hmd, QAudio::Mode mode) { @@ -60,12 +62,6 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) { auto& setting = getSetting(hmd, mode); if (setting.isSet()) { deviceName = setting.get(); - } else if (hmd) { - if (mode == QAudio::AudioInput) { - deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice(); - } else { // if (_mode == QAudio::AudioOutput) - deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice(); - } } else { deviceName = HifiAudioDeviceInfo::DEFAULT_DEVICE_NAME; } @@ -144,6 +140,8 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { return _devices.at(index.row())->selectedHMD; } else if (role == InfoRole) { return QVariant::fromValue(_devices.at(index.row())->info); + } else if (role == TypeRole) { + return _devices.at(index.row())->type; } else { return QVariant(); } @@ -166,8 +164,8 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) { QString deviceName = getTargetDevice(contextIsHMD, _mode); // FIXME can't use blocking connections here, so we can't determine whether the switch succeeded or not // We need to have the AudioClient emit signals on switch success / failure - QMetaObject::invokeMethod(client, "switchAudioDevice", - Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); + QMetaObject::invokeMethod(client, "switchAudioDevice", + Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName), Q_ARG(bool, contextIsHMD)); #if 0 bool switchResult = false; @@ -265,13 +263,20 @@ void AudioDeviceList::onDevicesChanged(const QList& devices bool hmdIsSelected = false; bool desktopIsSelected = false; - foreach(const HifiAudioDeviceInfo& deviceInfo, devices) { - for (bool isHMD : {false, true}) { - auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName; - if (deviceInfo.deviceName() == backupSelectedDeviceName) { - HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; - selectedDevice = deviceInfo; - backupSelectedDeviceName.clear(); + if (!_backupSelectedDesktopDeviceName.isEmpty() && !_backupSelectedHMDDeviceName.isEmpty()) { + foreach(const HifiAudioDeviceInfo& deviceInfo, devices) { + for (bool isHMD : {false, true}) { + auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName; + if (deviceInfo.deviceName() == backupSelectedDeviceName) { + if (isHMD && deviceInfo.getDeviceType() != HifiAudioDeviceInfo::desktop) { + _selectedHMDDevice= deviceInfo; + backupSelectedDeviceName.clear(); + } else if (!isHMD && deviceInfo.getDeviceType() != HifiAudioDeviceInfo::hmd) { + _selectedDesktopDevice = deviceInfo; + backupSelectedDeviceName.clear(); + } + + } } } } @@ -281,10 +286,18 @@ void AudioDeviceList::onDevicesChanged(const QList& devices device.info = deviceInfo; if (deviceInfo.isDefault()) { - if (deviceInfo.getMode() == QAudio::AudioInput) { - device.display = "Computer's default microphone (recommended)"; - } else { - device.display = "Computer's default audio (recommended)"; + if (deviceInfo.getDeviceType() == HifiAudioDeviceInfo::desktop) { + if (deviceInfo.getMode() == QAudio::AudioInput) { + device.display = "Computer's default microphone (recommended)"; + } else { + device.display = "Computer's default audio (recommended)"; + } + } else if (deviceInfo.getDeviceType() == HifiAudioDeviceInfo::hmd) { + if (deviceInfo.getMode() == QAudio::AudioInput) { + device.display = "Headset's default mic (recommended)"; + } else { + device.display = "Headset's default audio (recommended)"; + } } } else { device.display = device.info.deviceName() @@ -292,6 +305,19 @@ void AudioDeviceList::onDevicesChanged(const QList& devices .remove("Device") .replace(" )", ")"); } + + switch (deviceInfo.getDeviceType()) { + case HifiAudioDeviceInfo::hmd: + device.type = "hmd"; + break; + case HifiAudioDeviceInfo::desktop: + device.type = "desktop"; + break; + case HifiAudioDeviceInfo::both: + device.type = "both"; + break; + } + for (bool isHMD : {false, true}) { HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; @@ -302,8 +328,14 @@ void AudioDeviceList::onDevicesChanged(const QList& devices } else { //no selected device for context. fallback to saved - const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; - isSelected = (device.info.deviceName() == savedDeviceName); + QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; + + if (device.info.deviceName() == savedDeviceName) { + if ((isHMD && device.info.getDeviceType() != HifiAudioDeviceInfo::desktop) || + (!isHMD && device.info.getDeviceType() != HifiAudioDeviceInfo::hmd)) { + isSelected = true; + } + } } if (isSelected) { diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 13d97d53dd..3dc1949f80 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -29,6 +29,7 @@ public: QString display; bool selectedDesktop { false }; bool selectedHMD { false }; + QString type; }; class AudioDeviceList : public QAbstractListModel { diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h index 41c5ab7644..dc680b05cf 100644 --- a/interface/src/scripting/KeyboardScriptingInterface.h +++ b/interface/src/scripting/KeyboardScriptingInterface.h @@ -18,15 +18,23 @@ #include "DependencyManager.h" /**jsdoc - * The Keyboard API provides facilities to use 3D Physical keyboard. + * The Keyboard API provides facilities to use an in-world, virtual keyboard. When enabled, this keyboard is + * displayed instead of the 2D keyboard that raises at the bottom of the tablet or Web entities when a text input field has + * focus and you're in HMD mode. + * * @namespace Keyboard * * @hifi-interface * @hifi-client-entity * @hifi-avatar * - * @property {bool} raised - true If the keyboard is visible false otherwise - * @property {bool} password - true Will show * instead of characters in the text display false otherwise + * @property {boolean} raised - true if the virtual keyboard is visible, false if it isn't. + * @property {boolean} password - true if "*"s are displayed on the virtual keyboard's display + * instead of the characters typed, false if the actual characters are displayed. + * @property {boolean} use3DKeyboard - true if user settings have "Use Virtual Keyboard" enabled, + * false if it's disabled. Read-only. + * @property {boolean} preferMalletsOverLasers - true if user settings for the virtual keyboard have "Mallets" + * selected, false if "Lasers" is selected. Read-only. */ class KeyboardScriptingInterface : public QObject, public Dependency { @@ -39,14 +47,61 @@ class KeyboardScriptingInterface : public QObject, public Dependency { public: KeyboardScriptingInterface() = default; ~KeyboardScriptingInterface() = default; + + /**jsdoc + * Loads a JSON file that defines the virtual keyboard's layout. The default JSON file used is + * {@link https://github.com/highfidelity/hifi/blob/master/interface/resources/config/keyboard.json|https://github.com/highfidelity/hifi/.../keyboard.json}. + * @function Keyboard.loadKeyboardFile + * @param {string} path - The keyboard JSON file. + */ Q_INVOKABLE void loadKeyboardFile(const QString& string); + + /**jsdoc + * Enables the left mallet so that it is displayed when in HMD mode. + * @function Keyboard.enableLeftMallet + */ Q_INVOKABLE void enableLeftMallet(); + + /**jsdoc + * Enables the right mallet so that it is displayed when in HMD mode. + * @function Keyboard.enableRightMallet + */ Q_INVOKABLE void enableRightMallet(); + + /**jsdoc + * Disables the left mallet so that it is not displayed when in HMD mode. + * @function Keyboard.disableLeftMallet + */ Q_INVOKABLE void disableLeftMallet(); + + /**jsdoc + * Disables the right mallet so that it is not displayed when in HMD mode. + * @function Keyboard.disableRightMallet + */ Q_INVOKABLE void disableRightMallet(); + + /**jsdoc + * Configures the virtual keyboard to recognize a ray pointer as the left hand's laser. + * @function Keyboard.setLeftHandLaser + * @param {number} leftHandLaser - The ID of a ray pointer created by {@link Pointers.createPointer}. + */ Q_INVOKABLE void setLeftHandLaser(unsigned int leftHandLaser); + + /**jsdoc + * Configures the virtual keyboard to recognize a ray pointer as the right hand's laser. + * @function Keyboard.setRightHandLaser + * @param {number} rightHandLaser - The ID of a ray pointer created by {@link Pointers.createPointer}. + */ Q_INVOKABLE void setRightHandLaser(unsigned int rightHandLaser); + + /**jsdoc + * Checks whether an entity is part of the virtual keyboard. + * @function Keyboard.containsID + * @param {Uuid} entityID - The entity ID. + * @returns {boolean} true if the entity is part of the virtual keyboard, false if it isn't. + */ Q_INVOKABLE bool containsID(const QUuid& overlayID) const; + private: bool getPreferMalletsOverLasers() const; bool isRaised() const; diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index ae6a7c7d67..1020c12733 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -41,7 +41,8 @@ void MenuScriptingInterface::removeMenu(const QString& menu) { bool MenuScriptingInterface::menuExists(const QString& menu) { if (QThread::currentThread() == qApp->thread()) { - return Menu::getInstance()->menuExists(menu); + Menu* menuInstance = Menu::getInstance(); + return menuInstance && menuInstance->menuExists(menu); } bool result { false }; BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuExists", @@ -84,7 +85,8 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) { if (QThread::currentThread() == qApp->thread()) { - return Menu::getInstance()->menuItemExists(menu, menuitem); + Menu* menuInstance = Menu::getInstance(); + return menuInstance && menuInstance->menuItemExists(menu, menuitem); } bool result { false }; BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuItemExists", @@ -96,7 +98,8 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { if (QThread::currentThread() == qApp->thread()) { - return Menu::getInstance()->isOptionChecked(menuOption); + Menu* menuInstance = Menu::getInstance(); + return menuInstance && menuInstance->isOptionChecked(menuOption); } bool result { false }; BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isOptionChecked", @@ -113,7 +116,8 @@ void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) { if (QThread::currentThread() == qApp->thread()) { - return Menu::getInstance()->isOptionChecked(menuOption); + Menu* menuInstance = Menu::getInstance(); + return menuInstance && menuInstance->isMenuEnabled(menuOption); } bool result { false }; BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isMenuEnabled", diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index ec15dd8111..b7b5342066 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -58,18 +58,19 @@ void setupPreferences() { static const QString GRAPHICS_QUALITY { "Graphics Quality" }; { auto getter = []()->float { - return DependencyManager::get()->getWorldDetailQuality(); + return (int)DependencyManager::get()->getWorldDetailQuality(); }; - auto setter = [](float value) { - DependencyManager::get()->setWorldDetailQuality(value); + auto setter = [](int value) { + DependencyManager::get()->setWorldDetailQuality(static_cast(value)); }; - auto wodSlider = new SliderPreference(GRAPHICS_QUALITY, "World Detail", getter, setter); - wodSlider->setMin(0.25f); - wodSlider->setMax(0.75f); - wodSlider->setStep(0.25f); - preferences->addPreference(wodSlider); + auto wodButtons = new RadioButtonsPreference(GRAPHICS_QUALITY, "World Detail", getter, setter); + QStringList items; + items << "Low World Detail" << "Medium World Detail" << "High World Detail"; + wodButtons->setHeading("World Detail"); + wodButtons->setItems(items); + preferences->addPreference(wodButtons); auto getterShadow = []()->bool { auto menu = Menu::getInstance(); diff --git a/libraries/audio-client/CMakeLists.txt b/libraries/audio-client/CMakeLists.txt index 6b88292dd4..e1d90334ff 100644 --- a/libraries/audio-client/CMakeLists.txt +++ b/libraries/audio-client/CMakeLists.txt @@ -6,6 +6,7 @@ setup_hifi_library(Network Multimedia ${PLATFORM_QT_COMPONENTS}) link_hifi_libraries(audio plugins) include_hifi_library_headers(shared) include_hifi_library_headers(networking) +include_hifi_library_headers(gpu) if (ANDROID) else () diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index d29045c99b..18e8d11fc9 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include "AudioClientLogging.h" #include "AudioLogging.h" @@ -81,20 +83,50 @@ Mutex _recordMutex; HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode); +static QString getHmdAudioDeviceName(QAudio::Mode mode) { + foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getAllDisplayPlugins()) { + if (displayPlugin && displayPlugin->isHmd()) { + if (mode == QAudio::AudioInput) { + return displayPlugin->getPreferredAudioInDevice(); + } else { + return displayPlugin->getPreferredAudioOutDevice(); + } + break; + } + } + return QString(); +} + // thread-safe QList getAvailableDevices(QAudio::Mode mode) { + //get hmd device name prior to locking device mutex. in case of shutdown, this thread will be locked and audio client + //cannot properly shut down. + QString hmdDeviceName = getHmdAudioDeviceName(mode); + // NOTE: availableDevices() clobbers the Qt internal device list Lock lock(_deviceMutex); auto devices = QAudioDeviceInfo::availableDevices(mode); QList newDevices; - for (auto& device : devices) { newDevices.push_back(HifiAudioDeviceInfo(device, false, mode)); } newDevices.push_front(defaultAudioDeviceForMode(mode)); - + + if (!hmdDeviceName.isNull() && !hmdDeviceName.isEmpty()) { + HifiAudioDeviceInfo hmdDevice; + foreach(auto device, newDevices) { + if (device.getDevice().deviceName() == hmdDeviceName) { + hmdDevice = HifiAudioDeviceInfo(device.getDevice(), true, mode, HifiAudioDeviceInfo::hmd); + break; + } + } + + if (!hmdDevice.getDevice().isNull()) { + newDevices.push_front(hmdDevice); + } + } return newDevices; } @@ -110,8 +142,8 @@ void AudioClient::checkDevices() { auto inputDevices = getAvailableDevices(QAudio::AudioInput); auto outputDevices = getAvailableDevices(QAudio::AudioOutput); - QMetaObject::invokeMethod(this, "changeDefault", Q_ARG(HifiAudioDeviceInfo, inputDevices.first()), Q_ARG(QAudio::Mode, QAudio::AudioInput)); - QMetaObject::invokeMethod(this, "changeDefault", Q_ARG(HifiAudioDeviceInfo, outputDevices.first()), Q_ARG(QAudio::Mode, QAudio::AudioOutput)); + checkDefaultChanges(inputDevices); + checkDefaultChanges(outputDevices); Lock lock(_deviceMutex); if (inputDevices != _inputDevices) { @@ -125,6 +157,14 @@ void AudioClient::checkDevices() { } } +void AudioClient::checkDefaultChanges(QList& devices) { + foreach(auto device, devices) { + if (device.isDefault()) { + QMetaObject::invokeMethod(this, "changeDefault", Q_ARG(HifiAudioDeviceInfo, device), Q_ARG(QAudio::Mode, device.getMode())); + } + } +} + HifiAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const { Lock lock(_deviceMutex); @@ -386,12 +426,14 @@ void AudioClient::setAudioPaused(bool pause) { } } -HifiAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { +HifiAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName, bool isHmd=false) { HifiAudioDeviceInfo result; foreach (HifiAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { - result = audioDevice; - break; + if ((!isHmd && audioDevice.getDeviceType() != HifiAudioDeviceInfo::hmd) || (isHmd && audioDevice.getDeviceType() != HifiAudioDeviceInfo::desktop)) { + result = audioDevice; + break; + } } } return result; @@ -477,7 +519,7 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { // find a device in the list that matches the name we have and return it foreach(QAudioDeviceInfo audioDevice, devices){ if (audioDevice.deviceName() == CFStringGetCStringPtr(deviceName, kCFStringEncodingMacRoman)) { - return HifiAudioDeviceInfo(audioDevice, true, mode); + return HifiAudioDeviceInfo(audioDevice, true, mode, HifiAudioDeviceInfo::desktop); } } } @@ -532,7 +574,7 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { HifiAudioDeviceInfo foundDevice; foreach(QAudioDeviceInfo audioDevice, devices) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { - foundDevice=HifiAudioDeviceInfo(audioDevice,true,mode); + foundDevice = HifiAudioDeviceInfo(audioDevice, true, mode, HifiAudioDeviceInfo::desktop); break; } } @@ -558,8 +600,8 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } #endif // fallback for failed lookup is the default device - return (mode == QAudio::AudioInput) ? HifiAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice(), true,mode) : - HifiAudioDeviceInfo(QAudioDeviceInfo::defaultOutputDevice(), true, mode); + return (mode == QAudio::AudioInput) ? HifiAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice(), true, mode, HifiAudioDeviceInfo::desktop) : + HifiAudioDeviceInfo(QAudioDeviceInfo::defaultOutputDevice(), true, mode, HifiAudioDeviceInfo::desktop); } bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) { @@ -763,6 +805,7 @@ void AudioClient::stop() { // Destruction of the pointers will occur when the parent object (this) is destroyed) { Lock lock(_checkDevicesMutex); + _checkDevicesTimer->stop(); _checkDevicesTimer = nullptr; } { @@ -948,13 +991,18 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { void AudioClient::changeDefault(HifiAudioDeviceInfo newDefault, QAudio::Mode mode) { HifiAudioDeviceInfo currentDevice = mode == QAudio::AudioInput ? _inputDeviceInfo : _outputDeviceInfo; - if (currentDevice.isDefault() && currentDevice.getDevice() != newDefault.getDevice()) { + if (currentDevice.isDefault() && currentDevice.getDeviceType() == newDefault.getDeviceType() && currentDevice.getDevice() != newDefault.getDevice()) { switchAudioDevice(mode, newDefault); } } bool AudioClient::switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo) { auto device = deviceInfo; + if (deviceInfo.getDevice().isNull()) { + qCDebug(audioclient) << __FUNCTION__ << " switching to null device :" + << deviceInfo.deviceName() << " : " << deviceInfo.getDevice().deviceName(); + } + if (mode == QAudio::AudioInput) { return switchInputToAudioDevice(device); } else { @@ -962,8 +1010,8 @@ bool AudioClient::switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo } } -bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName) { - return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName)); +bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName, bool isHmd) { + return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName, isHmd)); } void AudioClient::configureReverb() { @@ -1771,7 +1819,8 @@ void AudioClient::outputFormatChanged() { bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); - qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << _inputDeviceInfo.deviceName() <<"----"<& devices); // calling with a null QAudioDevice will use the system default bool switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo = HifiAudioDeviceInfo()); - bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName); + bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName, bool isHmd); // Qt opensles plugin is not able to detect when the headset is plugged in void setHeadsetPluggedIn(bool pluggedIn); diff --git a/libraries/audio-client/src/HifiAudioDeviceInfo.cpp b/libraries/audio-client/src/HifiAudioDeviceInfo.cpp index 6e2d5fbe92..73d0670fa6 100644 --- a/libraries/audio-client/src/HifiAudioDeviceInfo.cpp +++ b/libraries/audio-client/src/HifiAudioDeviceInfo.cpp @@ -22,15 +22,14 @@ HifiAudioDeviceInfo& HifiAudioDeviceInfo::operator=(const HifiAudioDeviceInfo& o _audioDeviceInfo = other.getDevice(); _mode = other.getMode(); _isDefault = other.isDefault(); + _deviceType = other.getDeviceType(); return *this; } - bool HifiAudioDeviceInfo::operator==(const HifiAudioDeviceInfo& rhs) const { //Does the QAudioDeviceinfo match as well as is this the default device or return getDevice() == rhs.getDevice() && isDefault() == rhs.isDefault(); } bool HifiAudioDeviceInfo::operator!=(const HifiAudioDeviceInfo& rhs) const { return getDevice() != rhs.getDevice() || isDefault() != rhs.isDefault(); -} - +} \ No newline at end of file diff --git a/libraries/audio-client/src/HifiAudioDeviceInfo.h b/libraries/audio-client/src/HifiAudioDeviceInfo.h index 99520f1643..5bc7125574 100644 --- a/libraries/audio-client/src/HifiAudioDeviceInfo.h +++ b/libraries/audio-client/src/HifiAudioDeviceInfo.h @@ -23,19 +23,27 @@ class HifiAudioDeviceInfo : public QObject { Q_OBJECT public: + enum DeviceType { + desktop, + hmd, + both + }; + HifiAudioDeviceInfo() : QObject() {} HifiAudioDeviceInfo(const HifiAudioDeviceInfo &deviceInfo) : QObject(){ _audioDeviceInfo = deviceInfo.getDevice(); _mode = deviceInfo.getMode(); _isDefault = deviceInfo.isDefault(); + _deviceType = deviceInfo.getDeviceType(); } - HifiAudioDeviceInfo(QAudioDeviceInfo deviceInfo, bool isDefault, QAudio::Mode mode) : + HifiAudioDeviceInfo(QAudioDeviceInfo deviceInfo, bool isDefault, QAudio::Mode mode, DeviceType devType=both) : _audioDeviceInfo(deviceInfo), _isDefault(isDefault), - _mode(mode){ + _mode(mode), + _deviceType(devType){ } - + void setMode(QAudio::Mode mode) { _mode = mode; } void setIsDefault() { _isDefault = true; } void setDevice(QAudioDeviceInfo devInfo); @@ -52,7 +60,7 @@ public: QAudioDeviceInfo getDevice() const { return _audioDeviceInfo; } bool isDefault() const { return _isDefault; } QAudio::Mode getMode() const { return _mode; } - + DeviceType getDeviceType() const { return _deviceType; } HifiAudioDeviceInfo& operator=(const HifiAudioDeviceInfo& other); bool operator==(const HifiAudioDeviceInfo& rhs) const; bool operator!=(const HifiAudioDeviceInfo& rhs) const; @@ -61,6 +69,7 @@ private: QAudioDeviceInfo _audioDeviceInfo; bool _isDefault { false }; QAudio::Mode _mode { QAudio::AudioInput }; + DeviceType _deviceType{ both }; public: static const QString DEFAULT_DEVICE_NAME; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index bea9f979b8..277b293728 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -165,9 +165,6 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av _status = Status::ENDED; } - if (previousStatus != _status) { - qDebug(avatars_renderer) << "AvatarTransit " << avatarTransitStatusToStringMap[(int)previousStatus] << "->" << avatarTransitStatusToStringMap[_status]; - } return _status; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 63d8e2981c..aa6dc779d5 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -96,10 +96,16 @@ void Head::simulate(float deltaTime) { // 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 (_forceBlinkToRetarget || forceBlink || + (_browAudioLift < EPSILON && shouldDo(glm::max(1.0f, sqrt(fabs(_averageLoudness - _longTermAverageLoudness)) * ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) { float randSpeedVariability = randFloat(); float eyeBlinkVelocity = BLINK_SPEED + randSpeedVariability * BLINK_SPEED_VARIABILITY; + if (_forceBlinkToRetarget) { + // Slow down by half the blink if reseting eye target + eyeBlinkVelocity = 0.5f * eyeBlinkVelocity; + _forceBlinkToRetarget = false; + } _leftEyeBlinkVelocity = eyeBlinkVelocity; _rightEyeBlinkVelocity = eyeBlinkVelocity; if (randFloat() < 0.5f) { @@ -114,13 +120,12 @@ void Head::simulate(float deltaTime) { if (_leftEyeBlink == FULLY_CLOSED) { _leftEyeBlinkVelocity = -BLINK_SPEED; - + updateEyeLookAt(); } else if (_leftEyeBlink == FULLY_OPEN) { _leftEyeBlinkVelocity = 0.0f; } if (_rightEyeBlink == FULLY_CLOSED) { _rightEyeBlinkVelocity = -BLINK_SPEED; - } else if (_rightEyeBlink == FULLY_OPEN) { _rightEyeBlinkVelocity = 0.0f; } @@ -350,3 +355,24 @@ float Head::getFinalPitch() const { float Head::getFinalRoll() const { return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } + +void Head::setLookAtPosition(const glm::vec3& lookAtPosition) { + if (_isEyeLookAtUpdated && _requestLookAtPosition != lookAtPosition) { + _lookAtPositionChanged = usecTimestampNow(); + glm::vec3 oldAvatarLookAtVector = _requestLookAtPosition - _owningAvatar->getWorldPosition(); + glm::vec3 newAvatarLookAtVector = lookAtPosition - _owningAvatar->getWorldPosition(); + const float MIN_BLINK_ANGLE = 0.35f; // 20 degrees + _forceBlinkToRetarget = angleBetween(oldAvatarLookAtVector, newAvatarLookAtVector) > MIN_BLINK_ANGLE; + if (_forceBlinkToRetarget) { + _isEyeLookAtUpdated = false; + } else { + _lookAtPosition = lookAtPosition; + } + } + _requestLookAtPosition = lookAtPosition; +} + +void Head::updateEyeLookAt() { + _lookAtPosition = _requestLookAtPosition; + _isEyeLookAtUpdated = true; +} diff --git a/libraries/avatars-renderer/src/avatars-renderer/Head.h b/libraries/avatars-renderer/src/avatars-renderer/Head.h index e3c8d7d2b5..98b2fbb3a0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Head.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.h @@ -79,6 +79,9 @@ public: float getTimeWithoutTalking() const { return _timeWithoutTalking; } + virtual void setLookAtPosition(const glm::vec3& lookAtPosition) override; + void updateEyeLookAt(); + protected: // disallow copies of the Head, copy of owning Avatar is disallowed too Head(const Head&); @@ -123,6 +126,10 @@ protected: int _leftEyeLookAtID; int _rightEyeLookAtID; + glm::vec3 _requestLookAtPosition; + bool _forceBlinkToRetarget { false }; + bool _isEyeLookAtUpdated { false }; + // private methods void calculateMouthShapes(float timeRatio); void applyEyelidOffset(glm::quat headOrientation); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 710bfb8d2a..bfc60d06c5 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -3214,3 +3214,12 @@ void AvatarData::clearAvatarGrabData(const QUuid& grabID) { } }); } + +glm::vec3 AvatarData::getHeadJointFrontVector() const { + int headJointIndex = getJointIndex("Head"); + glm::quat headJointRotation = Quaternions::Y_180 * getAbsoluteJointRotationInObjectFrame(headJointIndex);// getAbsoluteJointRotationInRigFrame(headJointIndex, headJointRotation); + headJointRotation = getWorldOrientation() * headJointRotation; + float headYaw = safeEulerAngles(headJointRotation).y; + glm::quat headYawRotation = glm::angleAxis(headYaw, Vectors::UP); + return headYawRotation * IDENTITY_FORWARD; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index df0783ef4b..96c45c6293 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1484,6 +1484,7 @@ public: std::vector getSkeletonData() const; void sendSkeletonData() const; QVector getJointData() const; + glm::vec3 getHeadJointFrontVector() const; signals: diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index dc5aaf2595..020827c4d9 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -64,7 +64,7 @@ public: void setBlendshapeCoefficients(const QVector& blendshapeCoefficients) { _blendshapeCoefficients = blendshapeCoefficients; } const glm::vec3& getLookAtPosition() const { return _lookAtPosition; } - void setLookAtPosition(const glm::vec3& lookAtPosition) { + virtual void setLookAtPosition(const glm::vec3& lookAtPosition) { if (_lookAtPosition != lookAtPosition) { _lookAtPositionChanged = usecTimestampNow(); } diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index 8f1184904e..660159d9bd 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -13,6 +13,7 @@ #include #include #include +#include //#define HIFI_PLUGINMANAGER_DEBUG #if defined(HIFI_PLUGINMANAGER_DEBUG) @@ -21,6 +22,7 @@ #include #include +#include #include "RuntimePlugin.h" #include "CodecPlugin.h" @@ -221,7 +223,17 @@ const OculusPlatformPluginPointer PluginManager::getOculusPlatformPlugin() { return oculusPlatformPlugin; } -const DisplayPluginList& PluginManager::getDisplayPlugins() { +DisplayPluginList PluginManager::getAllDisplayPlugins() { + if (thread() != QThread::currentThread()) { + DisplayPluginList list; + QMetaObject::invokeMethod(this, "getAllDisplayPlugins", Qt::BlockingQueuedConnection, Q_RETURN_ARG(DisplayPluginList, list)); + return list; + } else { + return _displayPlugins; + } +} + + const DisplayPluginList& PluginManager::getDisplayPlugins() { static std::once_flag once; static auto deviceAddedCallback = [](QString deviceName) { qCDebug(plugins) << "Added device: " << deviceName; diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index eb377a2c8e..f0aa662634 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -51,6 +51,7 @@ public: using PluginFilter = std::function; void setPluginFilter(PluginFilter pluginFilter) { _pluginFilter = pluginFilter; } + Q_INVOKABLE DisplayPluginList getAllDisplayPlugins(); signals: void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices); diff --git a/scripts/simplifiedUI/ui/simplifiedUI.js b/scripts/simplifiedUI/ui/simplifiedUI.js index 3025b938cb..2ce6a3e073 100644 --- a/scripts/simplifiedUI/ui/simplifiedUI.js +++ b/scripts/simplifiedUI/ui/simplifiedUI.js @@ -377,7 +377,7 @@ function displayInitialLaunchWindow() { initialLaunchWindow.fromQml.connect(onMessageFromInitialLaunchWindow); - Window.location = "file:///~/serverless/tutorial.json"; + Window.location = "file:///~/serverless/empty.json"; } var SECOND_LAUNCH_QML_PATH = Script.resolvePath("simplifiedFTUE/SecondLaunchWindow.qml"); @@ -405,7 +405,7 @@ function displaySecondLaunchWindow() { secondLaunchWindow.fromQml.connect(onMessageFromSecondLaunchWindow); - Window.location = "file:///~/serverless/tutorial.json"; + Window.location = "file:///~/serverless/empty.json"; } function closeInitialLaunchWindow() { diff --git a/tests-manual/qml/qml/MacQml.qml b/tests-manual/qml/qml/MacQml.qml index bb7e3a0dff..14749bb826 100644 --- a/tests-manual/qml/qml/MacQml.qml +++ b/tests-manual/qml/qml/MacQml.qml @@ -61,17 +61,21 @@ Item { Rectangle { width: 5 height: 5 - color: "red" - ColorAnimation on color { loops: Animation.Infinite; from: "red"; to: "yellow"; duration: 1000 } + color: "blue" + ColorAnimation on color { loops: Animation.Infinite; from: "blue"; to: "yellow"; duration: 1000 } } - WebEngineView { id: root - url: "https://google.com/" - x: 6; y: 6; - width: parent.width * 0.8 - height: parent.height * 0.8 + url: "https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/#19583796789766627" +// url: "https://vimeo.com/108650530" +// url: "https://www.youtube.com/watch?v=7EWQOeQf32U&autoplay=1&loop=1" +// x: 6; y: 6; + anchors.fill: parent +// width: parent.width * 0.8 +// height: parent.height * 0.8 } + + } diff --git a/tests-manual/qml/src/MacQml.cpp b/tests-manual/qml/src/MacQml.cpp index 9c5f91041e..aa732b7209 100644 --- a/tests-manual/qml/src/MacQml.cpp +++ b/tests-manual/qml/src/MacQml.cpp @@ -39,7 +39,7 @@ void MacQml::init() { _surface->load(url, callback); _surface->resize(_window->size()); _surface->resume(); - + _window->installEventFilter(_surface.get()); } void MacQml::draw() { diff --git a/tests-manual/qml/src/MacQml.h b/tests-manual/qml/src/MacQml.h index 50f71cb72e..b46349440e 100644 --- a/tests-manual/qml/src/MacQml.h +++ b/tests-manual/qml/src/MacQml.h @@ -9,7 +9,7 @@ public: QmlPtr _surface; GLuint _fbo{ 0 }; - MacQml(const QWindow* window) : Parent(window) {} + MacQml(QWindow* window) : Parent(window) {} void update() override; void init() override; void draw() override; diff --git a/tests-manual/qml/src/StressWeb.h b/tests-manual/qml/src/StressWeb.h index a68e34d0c1..eb4aa80ba0 100644 --- a/tests-manual/qml/src/StressWeb.h +++ b/tests-manual/qml/src/StressWeb.h @@ -24,7 +24,7 @@ public: std::array, DIVISIONS_X> _surfaces; GLuint _fbo{ 0 }; - StressWeb(const QWindow* window) : Parent(window) {} + StressWeb(QWindow* window) : Parent(window) {} static QString getSourceUrl(bool video); void buildSurface(QmlInfo& qmlInfo, bool video); void destroySurface(QmlInfo& qmlInfo); diff --git a/tests-manual/qml/src/TestCase.h b/tests-manual/qml/src/TestCase.h index 191eecb408..084e306bb5 100644 --- a/tests-manual/qml/src/TestCase.h +++ b/tests-manual/qml/src/TestCase.h @@ -8,8 +8,8 @@ class TestCase { public: using QmlPtr = QSharedPointer; - using Builder = std::function; - TestCase(const QWindow* window) : _window(window) {} + using Builder = std::function; + TestCase(QWindow* window) : _window(window) {} virtual void init(); virtual void destroy(); virtual void update(); @@ -18,6 +18,6 @@ public: protected: QOpenGLFunctions_4_1_Core _glf; - const QWindow* _window; + QWindow* _window; std::function _discardLamdba; }; diff --git a/tests-manual/qml/src/main.cpp b/tests-manual/qml/src/main.cpp index 1d98ebf8c8..f2229f138f 100644 --- a/tests-manual/qml/src/main.cpp +++ b/tests-manual/qml/src/main.cpp @@ -205,12 +205,22 @@ void TestWindow::resizeEvent(QResizeEvent* ev) { int main(int argc, char** argv) { +#ifdef Q_OS_MAC auto format = getDefaultOpenGLSurfaceFormat(); - format.setVersion(4, 1); + // Deal with some weirdness in the chromium context sharing on Mac. + // The primary share context needs to be 3.2, so that the Chromium will + // succeed in it's creation of it's command stub contexts. + format.setVersion(3, 2); + // This appears to resolve the issues with corrupted fonts on OSX. No + // idea why. + qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "true"); + // https://i.kym-cdn.com/entries/icons/original/000/008/342/ihave.jpg QSurfaceFormat::setDefaultFormat(format); +#endif + QGuiApplication app(argc, argv); - TestCase::Builder builder = [](const QWindow* window)->TestCase*{ return new MacQml(window); }; + TestCase::Builder builder = [](QWindow* window)->TestCase*{ return new MacQml(window); }; TestWindow window(builder); return app.exec(); } diff --git a/tools/qt-builder/README.md b/tools/qt-builder/README.md index 956dda4534..59db078aa9 100644 --- a/tools/qt-builder/README.md +++ b/tools/qt-builder/README.md @@ -1,8 +1,10 @@ # General This document describes the process to build Qt 5.12.3. -Note that there are three patches. The first (to qfloat16.h) is needed to compile QT 5.12.3 on Visual Studio 2017 due to a bug in Visual Studio (*bitset* will not compile. Note that there is a change in CMakeLists.txt to support this. -The second patch is to OpenSL ES audio. -The third is a patch to QScriptEngine to prevent crashes in QScriptEnginePrivate::reportAdditionalMemoryCost, during garbage collection. See https://bugreports.qt.io/browse/QTBUG-76176 +Note that there are several patches. +* The first (to qfloat16.h) is needed to compile QT 5.12.3 on Visual Studio 2017 due to a bug in Visual Studio (*bitset* will not compile. Note that there is a change in CMakeLists.txt to support this. +* The second patch is to OpenSL ES audio and allow audio echo cancelllation on Android. +* The third is a patch to QScriptEngine to prevent crashes in QScriptEnginePrivate::reportAdditionalMemoryCost, during garbage collection. See https://bugreports.qt.io/browse/QTBUG-76176 +* The fourth is a patch which fixes video playback on WebEngineViews on mac. See https://bugreports.qt.io/browse/QTBUG-70967 ## Requirements ### Windows 1. Visual Studio 2017 @@ -222,6 +224,7 @@ git clone --recursive git://code.qt.io/qt/qt5.git -b 5.12.3 --single-branch `cd qt5` `git apply --ignore-space-change --ignore-whitespace patches/aec.patch` `git apply --ignore-space-change --ignore-whitespace patches/qtscript-crash-fix.patch` +`git apply --ignore-space-change --ignore-whitespace patches/mac-web-video.patch` `cd ..` #### Configuring `mkdir qt5-install` diff --git a/tools/qt-builder/patches/mac-web-video.patch b/tools/qt-builder/patches/mac-web-video.patch new file mode 100644 index 0000000000..2ea81ce18b --- /dev/null +++ b/tools/qt-builder/patches/mac-web-video.patch @@ -0,0 +1,247 @@ +Submodule qtwebengine contains modified content +diff --git a/qtwebengine/src/core/stream_video_node.cpp b/qtwebengine/src/core/stream_video_node.cpp +index 29922f86..baa39d3b 100644 +--- a/qtwebengine/src/core/stream_video_node.cpp ++++ b/qtwebengine/src/core/stream_video_node.cpp +@@ -62,38 +62,45 @@ protected: + const char *vertexShader() const override { + // Keep in sync with cc::VertexShaderVideoTransform + static const char *shader = +- "attribute highp vec4 a_position;\n" +- "attribute mediump vec2 a_texCoord;\n" +- "uniform highp mat4 matrix;\n" +- "uniform highp mat4 texMatrix;\n" +- "varying mediump vec2 v_texCoord;\n" +- "void main() {\n" +- " gl_Position = matrix * a_position;\n" +- " v_texCoord = vec4(texMatrix * vec4(a_texCoord.x, 1.0 - a_texCoord.y, 0.0, 1.0)).xy;\n" +- "}"; ++ R"SHADER(#version 150 core ++in vec4 a_position; ++in vec2 a_texCoord; ++uniform mat4 matrix; ++uniform mat4 texMatrix; ++out vec2 v_texCoord; ++void main() { ++ gl_Position = matrix * a_position; ++ v_texCoord = vec4(texMatrix * vec4(a_texCoord.x, 1.0 - a_texCoord.y, 0.0, 1.0)).xy; ++} ++ )SHADER"; + return shader; + } + + const char *fragmentShader() const override { + // Keep in sync with cc::FragmentShaderRGBATexAlpha + static const char *shaderExternal = +- "#extension GL_OES_EGL_image_external : require\n" +- "varying mediump vec2 v_texCoord;\n" +- "uniform samplerExternalOES s_texture;\n" +- "uniform lowp float alpha;\n" +- "void main() {\n" +- " lowp vec4 texColor = texture2D(s_texture, v_texCoord);\n" +- " gl_FragColor = texColor * alpha;\n" +- "}"; ++ R"SHADER(#version 150 core ++#extension GL_OES_EGL_image_external : require ++in vec2 v_texCoord; ++uniform samplerExternalOES s_texture; ++uniform float alpha; ++out vec4 fragColor; ++void main() { ++ vec4 texColor = texture(s_texture, v_texCoord); ++ fragColor = texColor * alpha; ++} ++ )SHADER"; + static const char *shader2DRect = +- "#extension GL_ARB_texture_rectangle : require\n" +- "varying mediump vec2 v_texCoord;\n" +- "uniform sampler2DRect s_texture;\n" +- "uniform lowp float alpha;\n" +- "void main() {\n" +- " lowp vec4 texColor = texture2DRect(s_texture, v_texCoord);\n" +- " gl_FragColor = texColor * alpha;\n" +- "}"; ++ R"SHADER(#version 150 core ++in vec2 v_texCoord; ++uniform sampler2D s_texture; ++uniform float alpha; ++out vec4 fragColor; ++void main() { ++ vec4 texColor = texture(s_texture, v_texCoord); ++ fragColor = texColor * alpha; ++} ++ )SHADER"; + if (m_target == ExternalTarget) + return shaderExternal; + else +diff --git a/qtwebengine/src/core/yuv_video_node.cpp b/qtwebengine/src/core/yuv_video_node.cpp +index 4a436d95..dc4b6ff9 100644 +--- a/qtwebengine/src/core/yuv_video_node.cpp ++++ b/qtwebengine/src/core/yuv_video_node.cpp +@@ -59,39 +59,41 @@ public: + YUVVideoMaterialShader(const gfx::ColorSpace &colorSpace) + { + static const char *shaderHead = +- "varying mediump vec2 v_yaTexCoord;\n" +- "varying mediump vec2 v_uvTexCoord;\n" +- "uniform sampler2D y_texture;\n" +- "uniform sampler2D u_texture;\n" +- "uniform sampler2D v_texture;\n" +- "uniform mediump float alpha;\n" +- "uniform mediump vec4 ya_clamp_rect;\n" +- "uniform mediump vec4 uv_clamp_rect;\n"; +- static const char *shader = +- "void main() {\n" +- " mediump vec2 ya_clamped =\n" +- " max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord));\n" +- " mediump float y_raw = texture2D(y_texture, ya_clamped).x;\n" +- " mediump vec2 uv_clamped =\n" +- " max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord));\n" +- " mediump float u_unsigned = texture2D(u_texture, uv_clamped).x;\n" +- " mediump float v_unsigned = texture2D(v_texture, uv_clamped).x;\n" +- " mediump vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned);\n" +- " mediump vec3 rgb = DoColorConversion(yuv);\n" +- " gl_FragColor = vec4(rgb, 1.0) * alpha;\n" +- "}"; ++ R"SHADER(#version 150 core ++in vec2 v_yaTexCoord; ++in vec2 v_uvTexCoord; ++uniform sampler2D y_texture; ++uniform sampler2D u_texture; ++uniform sampler2D v_texture; ++uniform float alpha; ++uniform vec4 ya_clamp_rect; ++uniform vec4 uv_clamp_rect; ++out vec4 fragColor; ++ )SHADER"; ++ ++ static const char *shader = R"SHADER( ++void main() { ++ vec2 ya_clamped = ++ max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord)); ++ float y_raw = texture(y_texture, ya_clamped).x; ++ vec2 uv_clamped = ++ max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord)); ++ float u_unsigned = texture(u_texture, uv_clamped).x; ++ float v_unsigned = texture(v_texture, uv_clamped).x; ++ vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned); ++ vec3 rgb = DoColorConversion(yuv); ++ fragColor = vec4(rgb, 1.0) * alpha; ++} ++ )SHADER"; ++ + // Invalid or unspecified color spaces should be treated as REC709. + gfx::ColorSpace src = colorSpace.IsValid() ? colorSpace : gfx::ColorSpace::CreateREC709(); + gfx::ColorSpace dst = gfx::ColorSpace::CreateSRGB(); + std::unique_ptr transform = + gfx::ColorTransform::NewColorTransform(src, dst, gfx::ColorTransform::Intent::INTENT_PERCEPTUAL); + +- QByteArray header(shaderHead); +- if (QOpenGLContext::currentContext()->isOpenGLES()) +- header = QByteArray("precision mediump float;\n") + header; +- + m_csShader = QByteArray::fromStdString(transform->GetShaderSource()); +- m_fragmentShader = header + m_csShader + QByteArray(shader); ++ m_fragmentShader = QByteArray(shaderHead) + m_csShader + QByteArray(shader); + } + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; + +@@ -108,20 +110,22 @@ protected: + const char *vertexShader() const override { + // Keep in sync with logic in VertexShader in components/viz/service/display/shader.cc + const char *shader = +- "attribute highp vec4 a_position;\n" +- "attribute mediump vec2 a_texCoord;\n" +- "uniform highp mat4 matrix;\n" +- "varying mediump vec2 v_yaTexCoord;\n" +- "varying mediump vec2 v_uvTexCoord;\n" +- "uniform mediump vec2 yaTexScale;\n" +- "uniform mediump vec2 yaTexOffset;\n" +- "uniform mediump vec2 uvTexScale;\n" +- "uniform mediump vec2 uvTexOffset;\n" +- "void main() {\n" +- " gl_Position = matrix * a_position;\n" +- " v_yaTexCoord = a_texCoord * yaTexScale + yaTexOffset;\n" +- " v_uvTexCoord = a_texCoord * uvTexScale + uvTexOffset;\n" +- "}"; ++ R"SHADER(#version 150 core ++in vec4 a_position; ++in vec2 a_texCoord; ++uniform mat4 matrix; ++out vec2 v_yaTexCoord; ++out vec2 v_uvTexCoord; ++uniform vec2 yaTexScale; ++uniform vec2 yaTexOffset; ++uniform vec2 uvTexScale; ++uniform vec2 uvTexOffset; ++void main() { ++ gl_Position = matrix * a_position; ++ v_yaTexCoord = a_texCoord * yaTexScale + yaTexOffset; ++ v_uvTexCoord = a_texCoord * uvTexScale + uvTexOffset; ++} ++ )SHADER"; + return shader; + } + +@@ -168,33 +172,35 @@ public: + YUVAVideoMaterialShader(const gfx::ColorSpace &colorSpace) : YUVVideoMaterialShader(colorSpace) + { + static const char *shaderHead = +- "varying mediump vec2 v_yaTexCoord;\n" +- "varying mediump vec2 v_uvTexCoord;\n" +- "uniform sampler2D y_texture;\n" +- "uniform sampler2D u_texture;\n" +- "uniform sampler2D v_texture;\n" +- "uniform sampler2D a_texture;\n" +- "uniform mediump float alpha;\n" +- "uniform mediump vec4 ya_clamp_rect;\n" +- "uniform mediump vec4 uv_clamp_rect;\n"; ++ R"SHADER(#version 150 core ++in vec2 v_yaTexCoord; ++in vec2 v_uvTexCoord; ++uniform sampler2D y_texture; ++uniform sampler2D u_texture; ++uniform sampler2D v_texture; ++uniform sampler2D a_texture; ++uniform float alpha; ++uniform vec4 ya_clamp_rect; ++uniform vec4 uv_clamp_rect; ++out vec4 fragColor; ++ )SHADER"; + static const char *shader = +- "void main() {\n" +- " mediump vec2 ya_clamped =\n" +- " max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord));\n" +- " mediump float y_raw = texture2D(y_texture, ya_clamped).x;\n" +- " mediump vec2 uv_clamped =\n" +- " max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord));\n" +- " mediump float u_unsigned = texture2D(u_texture, uv_clamped).x;\n" +- " mediump float v_unsigned = texture2D(v_texture, uv_clamped).x;\n" +- " mediump float a_raw = texture2D(a_texture, ya_clamped).x;\n" +- " mediump vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned);\n" +- " mediump vec3 rgb = DoColorConversion(yuv);\n" +- " gl_FragColor = vec4(rgb, 1.0) * (alpha * a_raw);\n" +- "}"; +- QByteArray header(shaderHead); +- if (QOpenGLContext::currentContext()->isOpenGLES()) +- header = QByteArray("precision mediump float;\n") + header; +- m_fragmentShader = header + m_csShader + QByteArray(shader); ++ R"SHADER( ++void main() { ++ vec2 ya_clamped = ++ max(ya_clamp_rect.xy, min(ya_clamp_rect.zw, v_yaTexCoord)); ++ float y_raw = texture(y_texture, ya_clamped).x; ++ vec2 uv_clamped = ++ max(uv_clamp_rect.xy, min(uv_clamp_rect.zw, v_uvTexCoord)); ++ float u_unsigned = texture(u_texture, uv_clamped).x; ++ float v_unsigned = texture(v_texture, uv_clamped).x; ++ float a_raw = texture(a_texture, ya_clamped).x; ++ vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned); ++ vec3 rgb = DoColorConversion(yuv); ++ fragColor = vec4(rgb, 1.0) * (alpha * a_raw); ++} ++ )SHADER"; ++ m_fragmentShader = QByteArray(shaderHead) + m_csShader + QByteArray(shader); + } + void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override; +