Merge pull request #16404 from highfidelity/master

Merge master into instancing
This commit is contained in:
Sabrina Shanman 2019-10-25 09:55:44 -07:00 committed by GitHub
commit e74de214e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 1018 additions and 312 deletions

View file

@ -265,7 +265,7 @@ static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
quint64 start = usecTimestampNow(); quint64 start = usecTimestampNow();
if (node->getType() == NodeType::Agent && node->getLinkedData() && node->getActiveSocket() && !node->isUpstream()) { if ((node->getType() == NodeType::Agent || node->getType() == NodeType::EntityScriptServer) && node->getLinkedData() && node->getActiveSocket() && !node->isUpstream()) {
broadcastAvatarDataToAgent(node); broadcastAvatarDataToAgent(node);
} else if (node->getType() == NodeType::DownstreamAvatarMixer) { } else if (node->getType() == NodeType::DownstreamAvatarMixer) {
broadcastAvatarDataToDownstreamMixer(node); broadcastAvatarDataToDownstreamMixer(node);

View file

@ -86,8 +86,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
this, "handleOctreePacket"); this, "handleOctreePacket");
packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat");
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket"); packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket");
packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket"); packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket");
packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket"); packetReceiver.registerListener(PacketType::EntityServerScriptLog, this, "handleEntityServerScriptLogPacket");
@ -255,6 +253,7 @@ void EntityScriptServer::handleEntityScriptCallMethodPacket(QSharedPointer<Recei
void EntityScriptServer::run() { void EntityScriptServer::run() {
DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT); DependencyManager::set<ScriptEngines>(ScriptEngine::ENTITY_SERVER_SCRIPT);
DependencyManager::set<EntityScriptServerServices>(); DependencyManager::set<EntityScriptServerServices>();
DependencyManager::set<AvatarHashMap>();
// make sure we request our script once the agent connects to the domain // make sure we request our script once the agent connects to the domain
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
@ -448,6 +447,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue); newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data()); newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
newEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarHashMap>().data());
// connect this script engines printedMessage signal to the global ScriptEngines these various messages // connect this script engines printedMessage signal to the global ScriptEngines these various messages
auto scriptEngines = DependencyManager::get<ScriptEngines>().data(); auto scriptEngines = DependencyManager::get<ScriptEngines>().data();

View file

@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject) include(ExternalProject)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL https://public.highfidelity.com/dependencies/qtaudio_wasapi12.zip URL https://public.highfidelity.com/dependencies/qtaudio_wasapi13.zip
URL_MD5 9e2eef41165f85344808f754b48bf08d URL_MD5 aa56a45f19c18caee13d29a40d1d7d28
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
BUILD_COMMAND "" BUILD_COMMAND ""
INSTALL_COMMAND "" INSTALL_COMMAND ""

View file

@ -1,4 +1,4 @@
Source: hifi-deps Source: hifi-deps
Version: 0.1 Version: 0.3
Description: Collected dependencies for High Fidelity applications Description: Collected dependencies for High Fidelity applications
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib, webrtc (!android) Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib, webrtc (!android)

View file

@ -265,7 +265,7 @@ endif()
if platform.system() == 'Windows': if platform.system() == 'Windows':
url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-windows3.tar.gz' url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-windows3.tar.gz'
elif platform.system() == 'Darwin': 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': elif platform.system() == 'Linux':
if platform.linux_distribution()[1][:3] == '16.': 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' url = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/qt5-install-5.12.3-ubuntu-16.04-with-symbols.tar.gz'

View file

@ -4736,32 +4736,6 @@
}, },
{ {
"children": [ "children": [
{
"children": [
],
"data": {
"endFrame": 30,
"loopFlag": true,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/side_step_short_left.fbx"
},
"id": "strafeLeftShortStep_c",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 20,
"loopFlag": true,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/side_step_left.fbx"
},
"id": "strafeLeftStep_c",
"type": "clip"
},
{ {
"children": [ "children": [
], ],
@ -4819,8 +4793,6 @@
"alpha": 0, "alpha": 0,
"alphaVar": "moveLateralAlpha", "alphaVar": "moveLateralAlpha",
"characteristicSpeeds": [ "characteristicSpeeds": [
0.1,
0.5,
1, 1,
2.55, 2.55,
3.35, 3.35,
@ -4834,34 +4806,6 @@
}, },
{ {
"children": [ "children": [
{
"children": [
],
"data": {
"endFrame": 30,
"loopFlag": true,
"mirrorFlag": true,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/side_step_short_left.fbx"
},
"id": "strafeRightShortStep_c",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 20,
"loopFlag": true,
"mirrorFlag": true,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/side_step_left.fbx"
},
"id": "strafeRightStep_c",
"type": "clip"
},
{ {
"children": [ "children": [
], ],
@ -4923,8 +4867,6 @@
"alpha": 0, "alpha": 0,
"alphaVar": "moveLateralAlpha", "alphaVar": "moveLateralAlpha",
"characteristicSpeeds": [ "characteristicSpeeds": [
0.1,
0.5,
1, 1,
2.55, 2.55,
3.4, 3.4,
@ -5617,7 +5559,7 @@
}, },
{ {
"state": "STRAFELEFT", "state": "STRAFELEFT",
"var": "isInputLeft" "var": "isMovingLeft"
}, },
{ {
"state": "turnRight", "state": "turnRight",
@ -5681,7 +5623,7 @@
}, },
{ {
"state": "STRAFERIGHT", "state": "STRAFERIGHT",
"var": "isInputRight" "var": "isMovingRight"
}, },
{ {
"state": "turnRight", "state": "turnRight",

View file

@ -133,15 +133,12 @@ Item {
ListElement { ListElement {
text: "Low World Detail" text: "Low World Detail"
worldDetailQualityValue: 0.25
} }
ListElement { ListElement {
text: "Medium World Detail" text: "Medium World Detail"
worldDetailQualityValue: 0.5
} }
ListElement { ListElement {
text: "Full World Detail" text: "Full World Detail"
worldDetailQualityValue: 0.75
} }
} }
@ -158,14 +155,7 @@ Item {
currentIndex: -1 currentIndex: -1
function refreshWorldDetailDropdown() { function refreshWorldDetailDropdown() {
var currentWorldDetailQuality = LODManager.worldDetailQuality; worldDetailDropdown.currentIndex = LODManager.worldDetailQuality;
if (currentWorldDetailQuality <= 0.25) {
worldDetailDropdown.currentIndex = 0;
} else if (currentWorldDetailQuality <= 0.5) {
worldDetailDropdown.currentIndex = 1;
} else {
worldDetailDropdown.currentIndex = 2;
}
} }
Component.onCompleted: { Component.onCompleted: {
@ -173,7 +163,7 @@ Item {
} }
onCurrentIndexChanged: { onCurrentIndexChanged: {
LODManager.worldDetailQuality = model.get(currentIndex).worldDetailQualityValue; LODManager.worldDetailQuality = currentIndex;
worldDetailDropdown.displayText = model.get(currentIndex).text; worldDetailDropdown.displayText = model.get(currentIndex).text;
} }
} }

View file

@ -258,13 +258,12 @@ Flickable {
Layout.preferredHeight: contentItem.height Layout.preferredHeight: contentItem.height
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false interactive: false
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true clip: true
model: AudioScriptingInterface.devices.input model: AudioScriptingInterface.devices.input
delegate: Item { delegate: Item {
width: parent.width width: parent.width
height: inputDeviceCheckbox.height height: model.type != "hmd" ? inputDeviceCheckbox.height + simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0
visible: model.type != "hmd"
SimplifiedControls.RadioButton { SimplifiedControls.RadioButton {
id: inputDeviceCheckbox id: inputDeviceCheckbox
anchors.left: parent.left anchors.left: parent.left
@ -354,13 +353,12 @@ Flickable {
Layout.preferredHeight: contentItem.height Layout.preferredHeight: contentItem.height
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false interactive: false
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true clip: true
model: AudioScriptingInterface.devices.output model: AudioScriptingInterface.devices.output
delegate: Item { delegate: Item {
width: parent.width width: parent.width
height: outputDeviceCheckbox.height height: model.type != "hmd" ? outputDeviceCheckbox.height +simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0
visible: model.type != "hmd"
SimplifiedControls.RadioButton { SimplifiedControls.RadioButton {
id: outputDeviceCheckbox id: outputDeviceCheckbox
anchors.left: parent.left anchors.left: parent.left

View file

@ -259,12 +259,12 @@ Flickable {
Layout.preferredHeight: contentItem.height Layout.preferredHeight: contentItem.height
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false interactive: false
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true clip: true
model: AudioScriptingInterface.devices.input model: AudioScriptingInterface.devices.input
delegate: Item { delegate: Item {
width: parent.width width: parent.width
height: inputDeviceCheckbox.height height: model.type != "desktop" ? inputDeviceCheckbox.height + simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0
visible: model.type != "desktop"
SimplifiedControls.RadioButton { SimplifiedControls.RadioButton {
id: inputDeviceCheckbox id: inputDeviceCheckbox
@ -355,13 +355,12 @@ Flickable {
Layout.preferredHeight: contentItem.height Layout.preferredHeight: contentItem.height
Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin Layout.topMargin: simplifiedUI.margins.settings.settingsGroupTopMargin
interactive: false interactive: false
spacing: simplifiedUI.margins.settings.spacingBetweenRadiobuttons
clip: true clip: true
model: AudioScriptingInterface.devices.output model: AudioScriptingInterface.devices.output
delegate: Item { delegate: Item {
width: parent.width width: parent.width
height: outputDeviceCheckbox.height height: model.type != "desktop" ? outputDeviceCheckbox.height + simplifiedUI.margins.settings.spacingBetweenRadiobuttons : 0
visible: model.type != "desktop"
SimplifiedControls.RadioButton { SimplifiedControls.RadioButton {
id: outputDeviceCheckbox id: outputDeviceCheckbox
anchors.left: parent.left anchors.left: parent.left

View file

@ -429,7 +429,7 @@ Rectangle {
SimplifiedControls.TextField { SimplifiedControls.TextField {
id: goToTextField id: goToTextField
readonly property string shortPlaceholderText: "Jump to..." 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 anchors.centerIn: parent
width: Math.min(parent.width, 445) width: Math.min(parent.width, 445)
height: 35 height: 35

View file

@ -0,0 +1,10 @@
{
"DataVersion": 3,
"Paths": {
"/": "/0, 0, 0/0,0,0,0"
},
"Entities": [
],
"Id": "{5807d519-eb7d-496d-b22a-0820811291c9}",
"Version": 120
}

View file

@ -5845,14 +5845,14 @@ void Application::pushPostUpdateLambda(void* key, const std::function<void()>& f
// to everyone. // to everyone.
// The principal result is to call updateLookAtTargetAvatar() and then setLookAtPosition(). // 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. // 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"); PerformanceTimer perfTimer("lookAt");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()");
auto myAvatar = getMyAvatar(); auto myAvatar = getMyAvatar();
FaceTracker* faceTracker = getActiveFaceTracker(); FaceTracker* faceTracker = getActiveFaceTracker();
myAvatar->updateLookAtPosition(faceTracker, _myCamera); myAvatar->updateEyesLookAtPosition(faceTracker, _myCamera, deltaTime);
} }
void Application::updateThreads(float deltaTime) { void Application::updateThreads(float deltaTime) {
@ -6636,7 +6636,7 @@ void Application::update(float deltaTime) {
{ {
PROFILE_RANGE(simulation, "MyAvatar"); PROFILE_RANGE(simulation, "MyAvatar");
PerformanceTimer perfTimer("MyAvatar"); PerformanceTimer perfTimer("MyAvatar");
qApp->updateMyAvatarLookAtPosition(); qApp->updateMyAvatarLookAtPosition(deltaTime);
avatarManager->updateMyAvatar(deltaTime); avatarManager->updateMyAvatar(deltaTime);
} }
} }
@ -7516,6 +7516,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine
scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data()); scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data()); scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());
qScriptRegisterMetaType(scriptEngine.data(), worldDetailQualityToScriptValue, worldDetailQualityFromScriptValue);
scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get<KeyboardScriptingInterface>().data()); scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get<KeyboardScriptingInterface>().data());
scriptEngine->registerGlobalObject("Performance", new PerformanceScriptingInterface()); scriptEngine->registerGlobalObject("Performance", new PerformanceScriptingInterface());

View file

@ -292,7 +292,7 @@ public:
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override; virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
void updateMyAvatarLookAtPosition(); void updateMyAvatarLookAtPosition(float deltaTime);
float getGameLoopRate() const { return _gameLoopCounter.rate(); } float getGameLoopRate() const { return _gameLoopCounter.rate(); }

View file

@ -19,11 +19,8 @@
#include "ui/DialogsManager.h" #include "ui/DialogsManager.h"
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
const float LODManager::DEFAULT_DESKTOP_LOD_DOWN_FPS = LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS; Setting::Handle<int> desktopWorldDetailQuality("desktopWorldDetailQuality", (int)DEFAULT_WORLD_DETAIL_QUALITY);
const float LODManager::DEFAULT_HMD_LOD_DOWN_FPS = LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS; Setting::Handle<int> hmdWorldDetailQuality("hmdWorldDetailQuality", (int)DEFAULT_WORLD_DETAIL_QUALITY);
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", LODManager::DEFAULT_DESKTOP_LOD_DOWN_FPS);
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", LODManager::DEFAULT_HMD_LOD_DOWN_FPS);
LODManager::LODManager() { LODManager::LODManager() {
} }
@ -326,19 +323,21 @@ QString LODManager::getLODFeedbackText() {
} }
void LODManager::loadSettings() { void LODManager::loadSettings() {
setDesktopLODTargetFPS(desktopLODDecreaseFPS.get()); auto desktopQuality = static_cast<WorldDetailQuality>(desktopWorldDetailQuality.get());
Setting::Handle<bool> firstRun { Settings::firstRun, true }; auto hmdQuality = static_cast<WorldDetailQuality>(hmdWorldDetailQuality.get());
Setting::Handle<bool> firstRun{ Settings::firstRun, true };
if (qApp->property(hifi::properties::OCULUS_STORE).toBool() && firstRun.get()) { if (qApp->property(hifi::properties::OCULUS_STORE).toBool() && firstRun.get()) {
const float LOD_HIGH_QUALITY_LEVEL = 0.75f; hmdQuality = WORLD_DETAIL_HIGH;
setHMDLODTargetFPS(LOD_HIGH_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS);
} else {
setHMDLODTargetFPS(hmdLODDecreaseFPS.get());
} }
setWorldDetailQuality(desktopQuality, false);
setWorldDetailQuality(hmdQuality, true);
} }
void LODManager::saveSettings() { void LODManager::saveSettings() {
desktopLODDecreaseFPS.set(getDesktopLODTargetFPS()); desktopWorldDetailQuality.set((int)_desktopWorldDetailQuality);
hmdLODDecreaseFPS.set(getHMDLODTargetFPS()); hmdWorldDetailQuality.set((int)_hmdWorldDetailQuality);
} }
const float MIN_DECREASE_FPS = 0.5f; const float MIN_DECREASE_FPS = 0.5f;
@ -393,54 +392,33 @@ float LODManager::getLODTargetFPS() const {
} }
} }
void LODManager::setWorldDetailQuality(float quality) { void LODManager::setWorldDetailQuality(WorldDetailQuality quality, bool isHMDMode) {
static const float MIN_FPS = 10; float desiredFPS = isHMDMode ? QUALITY_TO_FPS_HMD[quality] : QUALITY_TO_FPS_DESKTOP[quality];
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;
}
if (isHMDMode) { if (isHMDMode) {
_hmdWorldDetailQuality = quality;
setHMDLODTargetFPS(desiredFPS); setHMDLODTargetFPS(desiredFPS);
} else { } else {
_desktopWorldDetailQuality = quality;
setDesktopLODTargetFPS(desiredFPS); setDesktopLODTargetFPS(desiredFPS);
} }
}
void LODManager::setWorldDetailQuality(WorldDetailQuality quality) {
setWorldDetailQuality(quality, qApp->isHMDMode());
emit worldDetailQualityChanged(); emit worldDetailQualityChanged();
} }
float LODManager::getWorldDetailQuality() const { WorldDetailQuality LODManager::getWorldDetailQuality() const {
return qApp->isHMDMode() ? _hmdWorldDetailQuality : _desktopWorldDetailQuality;
}
static const float LOW = 0.25f; QScriptValue worldDetailQualityToScriptValue(QScriptEngine* engine, const WorldDetailQuality& worldDetailQuality) {
static const float MEDIUM = 0.5f; return worldDetailQuality;
static const float HIGH = 0.75f; }
bool inHMD = qApp->isHMDMode(); void worldDetailQualityFromScriptValue(const QScriptValue& object, WorldDetailQuality& worldDetailQuality) {
worldDetailQuality =
float targetFPS = 0.0f; static_cast<WorldDetailQuality>(std::min(std::max(object.toInt32(), (int)WORLD_DETAIL_LOW), (int)WORLD_DETAIL_HIGH));
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 LODManager::setLODQualityLevel(float quality) { void LODManager::setLODQualityLevel(float quality) {

View file

@ -23,22 +23,48 @@
#include <render/Args.h> #include <render/Args.h>
/**jsdoc
* <p>The world detail quality rendered.</p>
* <table>
* <thead>
* <tr><th>Value</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>Low world detail quality.</td></tr>
* <tr><td><code>1</code></td><td>Medium world detail quality.</td></tr>
* <tr><td><code>2</code></td><td>High world detail quality.</td></tr>
* </tbody>
* </table>
* @typedef {number} LODManager.WorldDetailQuality
*/
enum WorldDetailQuality {
WORLD_DETAIL_LOW = 0,
WORLD_DETAIL_MEDIUM,
WORLD_DETAIL_HIGH
};
Q_DECLARE_METATYPE(WorldDetailQuality);
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
const float LOD_DEFAULT_QUALITY_LEVEL = 0.2f; // default quality level setting is High (lower framerate) const float LOD_DEFAULT_QUALITY_LEVEL = 0.2f; // default quality level setting is High (lower framerate)
#else #else
const float LOD_DEFAULT_QUALITY_LEVEL = 0.5f; // default quality level setting is Mid const float LOD_DEFAULT_QUALITY_LEVEL = 0.5f; // default quality level setting is Mid
#endif #endif
const float LOD_MAX_LIKELY_DESKTOP_FPS = 60.0f; // this is essentially, V-synch fps
#ifdef Q_OS_ANDROID #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<float> QUALITY_TO_FPS_DESKTOP = { 60.0f, 30.0f, 15.0f };
const std::vector<float> QUALITY_TO_FPS_HMD = { 25.0f, 16.0f, 10.0f };
#else #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<float> QUALITY_TO_FPS_DESKTOP = { 60.0f, 30.0f, 15.0f };
const std::vector<float> QUALITY_TO_FPS_HMD = { 90.0f, 45.0f, 22.5f };
#endif #endif
const float LOD_OFFSET_FPS = 5.0f; // offset of FPS to add for computing the target framerate const float LOD_OFFSET_FPS = 5.0f; // offset of FPS to add for computing the target framerate
class AABox; class AABox;
/**jsdoc /**jsdoc
* The LOD class manages your Level of Detail functions within Interface. * The LOD class manages your Level of Detail functions within Interface.
* @namespace LODManager * @namespace LODManager
@ -51,12 +77,12 @@ class AABox;
* @property {number} engineRunTime <em>Read-only.</em> * @property {number} engineRunTime <em>Read-only.</em>
* @property {number} gpuTime <em>Read-only.</em> * @property {number} gpuTime <em>Read-only.</em>
*/ */
class LODManager : public QObject, public Dependency { class LODManager : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
SINGLETON_DEPENDENCY 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) Q_PROPERTY(float lodQualityLevel READ getLODQualityLevel WRITE setLODQualityLevel NOTIFY lodQualityLevelChanged)
@ -193,8 +219,8 @@ public:
float getSmoothRenderTime() const { return _smoothRenderTime; }; float getSmoothRenderTime() const { return _smoothRenderTime; };
float getSmoothRenderFPS() const { return (_smoothRenderTime > 0.0f ? (float)MSECS_PER_SECOND / _smoothRenderTime : 0.0f); }; float getSmoothRenderFPS() const { return (_smoothRenderTime > 0.0f ? (float)MSECS_PER_SECOND / _smoothRenderTime : 0.0f); };
void setWorldDetailQuality(float quality); void setWorldDetailQuality(WorldDetailQuality quality);
float getWorldDetailQuality() const; WorldDetailQuality getWorldDetailQuality() const;
void setLODQualityLevel(float quality); void setLODQualityLevel(float quality);
float getLODQualityLevel() const; float getLODQualityLevel() const;
@ -220,9 +246,6 @@ public:
float getPidOd() const; float getPidOd() const;
float getPidO() const; float getPidO() const;
static const float DEFAULT_DESKTOP_LOD_DOWN_FPS;
static const float DEFAULT_HMD_LOD_DOWN_FPS;
signals: signals:
/**jsdoc /**jsdoc
@ -244,6 +267,8 @@ signals:
private: private:
LODManager(); LODManager();
void setWorldDetailQuality(WorldDetailQuality quality, bool isHMDMode);
std::mutex _automaticLODLock; std::mutex _automaticLODLock;
bool _automaticLODAdjust = true; bool _automaticLODAdjust = true;
@ -258,8 +283,11 @@ private:
float _lodQualityLevel{ LOD_DEFAULT_QUALITY_LEVEL }; float _lodQualityLevel{ LOD_DEFAULT_QUALITY_LEVEL };
float _desktopTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS }; WorldDetailQuality _desktopWorldDetailQuality { DEFAULT_WORLD_DETAIL_QUALITY };
float _hmdTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS }; 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); float _lodHalfAngle = getHalfAngleFromVisibilityDistance(DEFAULT_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT);
int _boundaryLevelAdjust = 0; int _boundaryLevelAdjust = 0;
@ -269,4 +297,7 @@ private:
glm::vec4 _pidOutputs{ 0.0f }; glm::vec4 _pidOutputs{ 0.0f };
}; };
QScriptValue worldDetailQualityToScriptValue(QScriptEngine* engine, const WorldDetailQuality& worldDetailQuality);
void worldDetailQualityFromScriptValue(const QScriptValue& object, WorldDetailQuality& worldDetailQuality);
#endif // hifi_LODManager_h #endif // hifi_LODManager_h

View file

@ -92,7 +92,7 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
RenderScriptingInterface::getInstance()->setShadowsEnabled(true); RenderScriptingInterface::getInstance()->setShadowsEnabled(true);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME); qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::REALTIME);
DependencyManager::get<LODManager>()->setWorldDetailQuality(0.75f); DependencyManager::get<LODManager>()->setWorldDetailQuality(WORLD_DETAIL_HIGH);
break; break;
case PerformancePreset::MID: case PerformancePreset::MID:
@ -104,7 +104,7 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
RenderScriptingInterface::getInstance()->setShadowsEnabled(false); RenderScriptingInterface::getInstance()->setShadowsEnabled(false);
qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::INTERACTIVE); qApp->getRefreshRateManager().setRefreshRateProfile(RefreshRateManager::RefreshRateProfile::INTERACTIVE);
DependencyManager::get<LODManager>()->setWorldDetailQuality(0.5f); DependencyManager::get<LODManager>()->setWorldDetailQuality(WORLD_DETAIL_MEDIUM);
break; break;
case PerformancePreset::LOW: case PerformancePreset::LOW:
@ -114,7 +114,7 @@ void PerformanceManager::applyPerformancePreset(PerformanceManager::PerformanceP
RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommandedPpiScale); RenderScriptingInterface::getInstance()->setViewportResolutionScale(recommandedPpiScale);
DependencyManager::get<LODManager>()->setWorldDetailQuality(0.25f); DependencyManager::get<LODManager>()->setWorldDetailQuality(WORLD_DETAIL_LOW);
break; break;
case PerformancePreset::UNKNOWN: case PerformancePreset::UNKNOWN:

View file

@ -906,6 +906,25 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
updateViewBoom(); 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 // update sensorToWorldMatrix for camera and hand controllers
// before we perform rig animations and IK. // before we perform rig animations and IK.
updateSensorToWorldMatrix(); updateSensorToWorldMatrix();
@ -957,18 +976,6 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
head->setPosition(headPosition); head->setPosition(headPosition);
head->setScale(getModelScale()); head->setScale(getModelScale());
head->simulate(deltaTime); 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. // 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 DISTANCE_FACTOR = 3.14f;
const float MY_ANGLE_FACTOR = 1.0f; const float MY_ANGLE_FACTOR = 1.0f;
const float OTHER_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 LOOKING_AT_OTHER_ALREADY_TERM = lookingAtOtherAlready ? -0.2f : 0.0f;
const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; // meters 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) { void MyAvatar::computeMyLookAtTarget(const AvatarHash& hash) {
glm::vec3 myForward = _lookAtYaw * IDENTITY_FORWARD; glm::vec3 myForward = _lookAtYaw * IDENTITY_FORWARD;
if (_skeletonModel->isLoaded()) {
myForward = getHeadJointFrontVector();
}
glm::vec3 myPosition = getHead()->getEyePosition(); glm::vec3 myPosition = getHead()->getEyePosition();
CameraMode mode = qApp->getCamera().getMode(); CameraMode mode = qApp->getCamera().getMode();
if (mode == CAMERA_MODE_FIRST_PERSON_LOOK_AT || mode == CAMERA_MODE_FIRST_PERSON) { 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) { foreach (const AvatarSharedPointer& avatarData, hash) {
std::shared_ptr<Avatar> avatar = std::static_pointer_cast<Avatar>(avatarData); std::shared_ptr<Avatar> avatar = std::static_pointer_cast<Avatar>(avatarData);
if (!avatar->isMyAvatar() && avatar->isInitialized()) { if (!avatar->isMyAvatar() && avatar->isInitialized()) {
glm::vec3 otherForward = avatar->getHead()->getForwardDirection(); glm::vec3 otherForward = avatar->getHeadJointFrontVector();
glm::vec3 otherPosition = avatar->getHead()->getEyePosition(); glm::vec3 otherPosition = avatar->getHead()->getEyePosition();
const float TIME_WITHOUT_TALKING_THRESHOLD = 1.0f; const float TIME_WITHOUT_TALKING_THRESHOLD = 1.0f;
bool otherIsTalking = avatar->getHead()->getTimeWithoutTalking() <= TIME_WITHOUT_TALKING_THRESHOLD; bool otherIsTalking = avatar->getHead()->getTimeWithoutTalking() <= TIME_WITHOUT_TALKING_THRESHOLD;
@ -2277,7 +2287,9 @@ void MyAvatar::updateLookAtTargetAvatar() {
AvatarHash hash = DependencyManager::get<AvatarManager>()->getHashCopy(); AvatarHash hash = DependencyManager::get<AvatarManager>()->getHashCopy();
// determine what the best look at target for my avatar should be. // determine what the best look at target for my avatar should be.
if (!_scriptControlsEyesLookAt) {
computeMyLookAtTarget(hash); computeMyLookAtTarget(hash);
}
// snap look at position for avatars that are looking at me. // snap look at position for avatars that are looking at me.
snapOtherAvatarLookAtTargetsToMe(hash); snapOtherAvatarLookAtTargetsToMe(hash);
@ -6612,7 +6624,7 @@ bool MyAvatar::getIsJointOverridden(int jointIndex) const {
return _skeletonModel->getIsJointOverridden(jointIndex); return _skeletonModel->getIsJointOverridden(jointIndex);
} }
void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera) { void MyAvatar::updateEyesLookAtPosition(FaceTracker* faceTracker, Camera& myCamera, float deltaTime) {
updateLookAtTargetAvatar(); updateLookAtTargetAvatar();
@ -6642,6 +6654,13 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera)
} else { } else {
lookAtSpot = myHead->getEyePosition() + glm::normalize(leftVec) * 1000.0f; 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 { } else {
controller::Pose leftEyePose = getControllerPoseInAvatarFrame(controller::Action::LEFT_EYE); controller::Pose leftEyePose = getControllerPoseInAvatarFrame(controller::Action::LEFT_EYE);
controller::Pose rightEyePose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_EYE); controller::Pose rightEyePose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_EYE);
@ -6704,8 +6723,9 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera)
if (headPose.isValid()) { if (headPose.isValid()) {
lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE)); lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE));
} else { } else {
lookAtSpot = myHead->getEyePosition() + lookAtSpot = _shouldTurnToFaceCamera ?
(getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); 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); getHead()->setLookAtPosition(lookAtSpot);
} }
@ -6808,6 +6828,17 @@ void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) {
_lookAtScriptTarget = 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 MyAvatar::getLookAtPivotPoint() {
glm::vec3 avatarUp = getWorldOrientation() * Vectors::UP; glm::vec3 avatarUp = getWorldOrientation() * Vectors::UP;
glm::vec3 yAxisEyePosition = getWorldPosition() + avatarUp * glm::dot(avatarUp, _skeletonModel->getDefaultEyeModelPosition()); 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); POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING);
} }
} }

View file

@ -1765,10 +1765,26 @@ public:
/**jsdoc /**jsdoc
* Returns the current head look at target point in world coordinates. * Returns the current head look at target point in world coordinates.
* @function MyAvatar.getHeadLookAt * @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; } 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 /**jsdoc
* Aims the pointing directional blending towards the provided target point. * Aims the pointing directional blending towards the provided target point.
* The "point" reaction should be triggered before using this method. * The "point" reaction should be triggered before using this method.
@ -1906,7 +1922,7 @@ public:
bool getFlowActive() const; bool getFlowActive() const;
bool getNetworkGraphActive() 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 // sets the reaction enabled and triggered parameters of the passed in params
// also clears internal reaction triggers // also clears internal reaction triggers
@ -2666,6 +2682,9 @@ private:
eyeContactTarget _eyeContactTarget; eyeContactTarget _eyeContactTarget;
float _eyeContactTargetTimer { 0.0f }; float _eyeContactTargetTimer { 0.0f };
ThreadSafeValueCache<glm::vec3> _eyesLookAtTarget { glm::vec3() };
bool _scriptControlsEyesLookAt{ false };
float _scriptEyesControlTimer{ 0.0f };
glm::vec3 _trackedHeadPosition; glm::vec3 _trackedHeadPosition;

View file

@ -44,7 +44,8 @@ enum AudioDeviceRole {
SelectedDesktopRole, SelectedDesktopRole,
SelectedHMDRole, SelectedHMDRole,
PeakRole, PeakRole,
InfoRole InfoRole,
TypeRole
}; };
QHash<int, QByteArray> AudioDeviceList::_roles { QHash<int, QByteArray> AudioDeviceList::_roles {
@ -52,7 +53,8 @@ QHash<int, QByteArray> AudioDeviceList::_roles {
{ SelectedDesktopRole, "selectedDesktop" }, { SelectedDesktopRole, "selectedDesktop" },
{ SelectedHMDRole, "selectedHMD" }, { SelectedHMDRole, "selectedHMD" },
{ PeakRole, "peak" }, { PeakRole, "peak" },
{ InfoRole, "info" } { InfoRole, "info" },
{ TypeRole, "type"}
}; };
static QString getTargetDevice(bool hmd, QAudio::Mode mode) { 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); auto& setting = getSetting(hmd, mode);
if (setting.isSet()) { if (setting.isSet()) {
deviceName = setting.get(); deviceName = setting.get();
} else if (hmd) {
if (mode == QAudio::AudioInput) {
deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice();
} else { // if (_mode == QAudio::AudioOutput)
deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
}
} else { } else {
deviceName = HifiAudioDeviceInfo::DEFAULT_DEVICE_NAME; deviceName = HifiAudioDeviceInfo::DEFAULT_DEVICE_NAME;
} }
@ -144,6 +140,8 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
return _devices.at(index.row())->selectedHMD; return _devices.at(index.row())->selectedHMD;
} else if (role == InfoRole) { } else if (role == InfoRole) {
return QVariant::fromValue<HifiAudioDeviceInfo>(_devices.at(index.row())->info); return QVariant::fromValue<HifiAudioDeviceInfo>(_devices.at(index.row())->info);
} else if (role == TypeRole) {
return _devices.at(index.row())->type;
} else { } else {
return QVariant(); return QVariant();
} }
@ -167,7 +165,7 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) {
// FIXME can't use blocking connections here, so we can't determine whether the switch succeeded or not // 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 // We need to have the AudioClient emit signals on switch success / failure
QMetaObject::invokeMethod(client, "switchAudioDevice", QMetaObject::invokeMethod(client, "switchAudioDevice",
Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName), Q_ARG(bool, contextIsHMD));
#if 0 #if 0
bool switchResult = false; bool switchResult = false;
@ -265,13 +263,20 @@ void AudioDeviceList::onDevicesChanged(const QList<HifiAudioDeviceInfo>& devices
bool hmdIsSelected = false; bool hmdIsSelected = false;
bool desktopIsSelected = false; bool desktopIsSelected = false;
if (!_backupSelectedDesktopDeviceName.isEmpty() && !_backupSelectedHMDDeviceName.isEmpty()) {
foreach(const HifiAudioDeviceInfo& deviceInfo, devices) { foreach(const HifiAudioDeviceInfo& deviceInfo, devices) {
for (bool isHMD : {false, true}) { for (bool isHMD : {false, true}) {
auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName; auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName;
if (deviceInfo.deviceName() == backupSelectedDeviceName) { if (deviceInfo.deviceName() == backupSelectedDeviceName) {
HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; if (isHMD && deviceInfo.getDeviceType() != HifiAudioDeviceInfo::desktop) {
selectedDevice = deviceInfo; _selectedHMDDevice= deviceInfo;
backupSelectedDeviceName.clear(); backupSelectedDeviceName.clear();
} else if (!isHMD && deviceInfo.getDeviceType() != HifiAudioDeviceInfo::hmd) {
_selectedDesktopDevice = deviceInfo;
backupSelectedDeviceName.clear();
}
}
} }
} }
} }
@ -281,11 +286,19 @@ void AudioDeviceList::onDevicesChanged(const QList<HifiAudioDeviceInfo>& devices
device.info = deviceInfo; device.info = deviceInfo;
if (deviceInfo.isDefault()) { if (deviceInfo.isDefault()) {
if (deviceInfo.getDeviceType() == HifiAudioDeviceInfo::desktop) {
if (deviceInfo.getMode() == QAudio::AudioInput) { if (deviceInfo.getMode() == QAudio::AudioInput) {
device.display = "Computer's default microphone (recommended)"; device.display = "Computer's default microphone (recommended)";
} else { } else {
device.display = "Computer's default audio (recommended)"; 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 { } else {
device.display = device.info.deviceName() device.display = device.info.deviceName()
.replace("High Definition", "HD") .replace("High Definition", "HD")
@ -293,6 +306,19 @@ void AudioDeviceList::onDevicesChanged(const QList<HifiAudioDeviceInfo>& devices
.replace(" )", ")"); .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}) { for (bool isHMD : {false, true}) {
HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; HifiAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
bool& isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; bool& isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
@ -302,8 +328,14 @@ void AudioDeviceList::onDevicesChanged(const QList<HifiAudioDeviceInfo>& devices
} }
else { else {
//no selected device for context. fallback to saved //no selected device for context. fallback to saved
const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName;
isSelected = (device.info.deviceName() == savedDeviceName);
if (device.info.deviceName() == savedDeviceName) {
if ((isHMD && device.info.getDeviceType() != HifiAudioDeviceInfo::desktop) ||
(!isHMD && device.info.getDeviceType() != HifiAudioDeviceInfo::hmd)) {
isSelected = true;
}
}
} }
if (isSelected) { if (isSelected) {

View file

@ -29,6 +29,7 @@ public:
QString display; QString display;
bool selectedDesktop { false }; bool selectedDesktop { false };
bool selectedHMD { false }; bool selectedHMD { false };
QString type;
}; };
class AudioDeviceList : public QAbstractListModel { class AudioDeviceList : public QAbstractListModel {

View file

@ -55,12 +55,14 @@ static const QVariantMap DOCK_AREA {
/**jsdoc /**jsdoc
* The possible "relative position anchors" of an <code>InteractiveWindow</code>. Used when defining the `relativePosition` property of an `InteractiveWindow`. * The possible "relative position anchors" of an <code>InteractiveWindow</code>. Used when defining the `relativePosition` property of an `InteractiveWindow`.
* @typedef {object} InteractiveWindow.RelativePositionAnchors * @typedef {object} InteractiveWindow.RelativePositionAnchors
* @property {InteractiveWindow.RelativePositionAnchor} NO_ANCHOR - Specifies that the position of the `InteractiveWindow` will not be relative to any part of the Interface window.
* @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top left of the Interface window. * @property {InteractiveWindow.RelativePositionAnchor} TOP_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top left of the Interface window.
* @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top right of the Interface window. * @property {InteractiveWindow.RelativePositionAnchor} TOP_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the top right of the Interface window.
* @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom right of the Interface window. * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_RIGHT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom right of the Interface window.
* @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom left of the Interface window. * @property {InteractiveWindow.RelativePositionAnchor} BOTTOM_LEFT - Specifies that the `relativePosition` of the `InteractiveWindow` will be offset from the bottom left of the Interface window.
*/ */
static const QVariantMap RELATIVE_POSITION_ANCHOR { static const QVariantMap RELATIVE_POSITION_ANCHOR {
{ "NO_ANCHOR", RelativePositionAnchor::NO_ANCHOR },
{ "TOP_LEFT", RelativePositionAnchor::TOP_LEFT }, { "TOP_LEFT", RelativePositionAnchor::TOP_LEFT },
{ "TOP_RIGHT", RelativePositionAnchor::TOP_RIGHT }, { "TOP_RIGHT", RelativePositionAnchor::TOP_RIGHT },
{ "BOTTOM_RIGHT", RelativePositionAnchor::BOTTOM_RIGHT }, { "BOTTOM_RIGHT", RelativePositionAnchor::BOTTOM_RIGHT },

View file

@ -18,15 +18,23 @@
#include "DependencyManager.h" #include "DependencyManager.h"
/**jsdoc /**jsdoc
* The Keyboard API provides facilities to use 3D Physical keyboard. * The <code>Keyboard</code> 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 * @namespace Keyboard
* *
* @hifi-interface * @hifi-interface
* @hifi-client-entity * @hifi-client-entity
* @hifi-avatar * @hifi-avatar
* *
* @property {bool} raised - <code>true</code> If the keyboard is visible <code>false</code> otherwise * @property {boolean} raised - <code>true</code> if the virtual keyboard is visible, <code>false</code> if it isn't.
* @property {bool} password - <code>true</code> Will show * instead of characters in the text display <code>false</code> otherwise * @property {boolean} password - <code>true</code> if <code>"*"</code>s are displayed on the virtual keyboard's display
* instead of the characters typed, <code>false</code> if the actual characters are displayed.
* @property {boolean} use3DKeyboard - <code>true</code> if user settings have "Use Virtual Keyboard" enabled,
* <code>false</code> if it's disabled. <em>Read-only.</em>
* @property {boolean} preferMalletsOverLasers - <code>true</code> if user settings for the virtual keyboard have "Mallets"
* selected, <code>false</code> if "Lasers" is selected. <em>Read-only.</em>
*/ */
class KeyboardScriptingInterface : public QObject, public Dependency { class KeyboardScriptingInterface : public QObject, public Dependency {
@ -39,14 +47,61 @@ class KeyboardScriptingInterface : public QObject, public Dependency {
public: public:
KeyboardScriptingInterface() = default; KeyboardScriptingInterface() = default;
~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); 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(); 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(); 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(); 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(); 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); 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); 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} <code>true</code> if the entity is part of the virtual keyboard, <code>false</code> if it isn't.
*/
Q_INVOKABLE bool containsID(const QUuid& overlayID) const; Q_INVOKABLE bool containsID(const QUuid& overlayID) const;
private: private:
bool getPreferMalletsOverLasers() const; bool getPreferMalletsOverLasers() const;
bool isRaised() const; bool isRaised() const;

View file

@ -41,7 +41,8 @@ void MenuScriptingInterface::removeMenu(const QString& menu) {
bool MenuScriptingInterface::menuExists(const QString& menu) { bool MenuScriptingInterface::menuExists(const QString& menu) {
if (QThread::currentThread() == qApp->thread()) { if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->menuExists(menu); Menu* menuInstance = Menu::getInstance();
return menuInstance && menuInstance->menuExists(menu);
} }
bool result { false }; bool result { false };
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuExists", 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) { bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) {
if (QThread::currentThread() == qApp->thread()) { if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->menuItemExists(menu, menuitem); Menu* menuInstance = Menu::getInstance();
return menuInstance && menuInstance->menuItemExists(menu, menuitem);
} }
bool result { false }; bool result { false };
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuItemExists", BLOCKING_INVOKE_METHOD(Menu::getInstance(), "menuItemExists",
@ -96,7 +98,8 @@ bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString&
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
if (QThread::currentThread() == qApp->thread()) { if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->isOptionChecked(menuOption); Menu* menuInstance = Menu::getInstance();
return menuInstance && menuInstance->isOptionChecked(menuOption);
} }
bool result { false }; bool result { false };
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isOptionChecked", BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isOptionChecked",
@ -113,7 +116,8 @@ void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool
bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) { bool MenuScriptingInterface::isMenuEnabled(const QString& menuOption) {
if (QThread::currentThread() == qApp->thread()) { if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->isOptionChecked(menuOption); Menu* menuInstance = Menu::getInstance();
return menuInstance && menuInstance->isMenuEnabled(menuOption);
} }
bool result { false }; bool result { false };
BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isMenuEnabled", BLOCKING_INVOKE_METHOD(Menu::getInstance(), "isMenuEnabled",

View file

@ -116,9 +116,10 @@ void InteractiveWindow::forwardKeyReleaseEvent(int key, int modifiers) {
} }
void InteractiveWindow::onMainWindowGeometryChanged(QRect geometry) { void InteractiveWindow::onMainWindowGeometryChanged(QRect geometry) {
// This handler is only connected `if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NONE)`.
if (_isFullScreenWindow) { if (_isFullScreenWindow) {
repositionAndResizeFullScreenWindow(); repositionAndResizeFullScreenWindow();
} else { } else if (_relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) {
setPositionUsingRelativePositionAndAnchor(geometry); setPositionUsingRelativePositionAndAnchor(geometry);
} }
} }
@ -326,7 +327,9 @@ InteractiveWindow::InteractiveWindow(const QString& sourceUrl, const QVariantMap
connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection); connect(object, SIGNAL(presentationModeChanged()), this, SLOT(parentNativeWindowToMainWindow()), Qt::QueuedConnection);
#endif #endif
if (_isFullScreenWindow || _relativePositionAnchor != RelativePositionAnchor::NO_ANCHOR) {
connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &InteractiveWindow::onMainWindowGeometryChanged, Qt::QueuedConnection); connect(qApp->getWindow(), &MainWindow::windowGeometryChanged, this, &InteractiveWindow::onMainWindowGeometryChanged, Qt::QueuedConnection);
}
QUrl sourceURL{ sourceUrl }; QUrl sourceURL{ sourceUrl };
// If the passed URL doesn't correspond to a known scheme, assume it's a local file path // If the passed URL doesn't correspond to a known scheme, assume it's a local file path
@ -494,6 +497,9 @@ void InteractiveWindow::setPositionUsingRelativePositionAndAnchor(const QRect& m
newPosition.x = mainWindowGeometry.x() + relativePosition.x; newPosition.x = mainWindowGeometry.x() + relativePosition.x;
newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y; newPosition.y = mainWindowGeometry.y() + mainWindowGeometry.height() - relativePosition.y;
break; break;
case RelativePositionAnchor::NO_ANCHOR:
// No-op.
break;
} }
// Make sure we include the dimensions of the docked widget! // Make sure we include the dimensions of the docked widget!

View file

@ -91,6 +91,7 @@ namespace InteractiveWindowEnums {
Q_ENUM_NS(DockArea); Q_ENUM_NS(DockArea);
enum RelativePositionAnchor { enum RelativePositionAnchor {
NO_ANCHOR,
TOP_LEFT, TOP_LEFT,
TOP_RIGHT, TOP_RIGHT,
BOTTOM_RIGHT, BOTTOM_RIGHT,
@ -147,7 +148,7 @@ private:
Q_INVOKABLE glm::vec2 getPosition() const; Q_INVOKABLE glm::vec2 getPosition() const;
Q_INVOKABLE void setPosition(const glm::vec2& position); Q_INVOKABLE void setPosition(const glm::vec2& position);
RelativePositionAnchor _relativePositionAnchor{ RelativePositionAnchor::TOP_LEFT }; RelativePositionAnchor _relativePositionAnchor{ RelativePositionAnchor::NO_ANCHOR };
Q_INVOKABLE RelativePositionAnchor getRelativePositionAnchor() const; Q_INVOKABLE RelativePositionAnchor getRelativePositionAnchor() const;
Q_INVOKABLE void setRelativePositionAnchor(const RelativePositionAnchor& position); Q_INVOKABLE void setRelativePositionAnchor(const RelativePositionAnchor& position);

View file

@ -58,18 +58,19 @@ void setupPreferences() {
static const QString GRAPHICS_QUALITY { "Graphics Quality" }; static const QString GRAPHICS_QUALITY { "Graphics Quality" };
{ {
auto getter = []()->float { auto getter = []()->float {
return DependencyManager::get<LODManager>()->getWorldDetailQuality(); return (int)DependencyManager::get<LODManager>()->getWorldDetailQuality();
}; };
auto setter = [](float value) { auto setter = [](int value) {
DependencyManager::get<LODManager>()->setWorldDetailQuality(value); DependencyManager::get<LODManager>()->setWorldDetailQuality(static_cast<WorldDetailQuality>(value));
}; };
auto wodSlider = new SliderPreference(GRAPHICS_QUALITY, "World Detail", getter, setter); auto wodButtons = new RadioButtonsPreference(GRAPHICS_QUALITY, "World Detail", getter, setter);
wodSlider->setMin(0.25f); QStringList items;
wodSlider->setMax(0.75f); items << "Low World Detail" << "Medium World Detail" << "High World Detail";
wodSlider->setStep(0.25f); wodButtons->setHeading("World Detail");
preferences->addPreference(wodSlider); wodButtons->setItems(items);
preferences->addPreference(wodButtons);
auto getterShadow = []()->bool { auto getterShadow = []()->bool {
auto menu = Menu::getInstance(); auto menu = Menu::getInstance();

View file

@ -6,6 +6,7 @@ setup_hifi_library(Network Multimedia ${PLATFORM_QT_COMPONENTS})
link_hifi_libraries(audio plugins) link_hifi_libraries(audio plugins)
include_hifi_library_headers(shared) include_hifi_library_headers(shared)
include_hifi_library_headers(networking) include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)
if (ANDROID) if (ANDROID)
else () else ()

View file

@ -39,6 +39,7 @@
#include <QtMultimedia/QAudioInput> #include <QtMultimedia/QAudioInput>
#include <QtMultimedia/QAudioOutput> #include <QtMultimedia/QAudioOutput>
#include <shared/QtHelpers.h>
#include <ThreadHelpers.h> #include <ThreadHelpers.h>
#include <NodeList.h> #include <NodeList.h>
#include <plugins/CodecPlugin.h> #include <plugins/CodecPlugin.h>
@ -47,6 +48,7 @@
#include <SettingHandle.h> #include <SettingHandle.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <Transform.h> #include <Transform.h>
#include <plugins/DisplayPlugin.h>
#include "AudioClientLogging.h" #include "AudioClientLogging.h"
#include "AudioLogging.h" #include "AudioLogging.h"
@ -81,20 +83,50 @@ Mutex _recordMutex;
HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode); 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 // thread-safe
QList<HifiAudioDeviceInfo> getAvailableDevices(QAudio::Mode mode) { QList<HifiAudioDeviceInfo> 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 // NOTE: availableDevices() clobbers the Qt internal device list
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
auto devices = QAudioDeviceInfo::availableDevices(mode); auto devices = QAudioDeviceInfo::availableDevices(mode);
QList<HifiAudioDeviceInfo> newDevices; QList<HifiAudioDeviceInfo> newDevices;
for (auto& device : devices) { for (auto& device : devices) {
newDevices.push_back(HifiAudioDeviceInfo(device, false, mode)); newDevices.push_back(HifiAudioDeviceInfo(device, false, mode));
} }
newDevices.push_front(defaultAudioDeviceForMode(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; return newDevices;
} }
@ -110,8 +142,8 @@ void AudioClient::checkDevices() {
auto inputDevices = getAvailableDevices(QAudio::AudioInput); auto inputDevices = getAvailableDevices(QAudio::AudioInput);
auto outputDevices = getAvailableDevices(QAudio::AudioOutput); auto outputDevices = getAvailableDevices(QAudio::AudioOutput);
QMetaObject::invokeMethod(this, "changeDefault", Q_ARG(HifiAudioDeviceInfo, inputDevices.first()), Q_ARG(QAudio::Mode, QAudio::AudioInput)); checkDefaultChanges(inputDevices);
QMetaObject::invokeMethod(this, "changeDefault", Q_ARG(HifiAudioDeviceInfo, outputDevices.first()), Q_ARG(QAudio::Mode, QAudio::AudioOutput)); checkDefaultChanges(outputDevices);
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
if (inputDevices != _inputDevices) { if (inputDevices != _inputDevices) {
@ -125,6 +157,14 @@ void AudioClient::checkDevices() {
} }
} }
void AudioClient::checkDefaultChanges(QList<HifiAudioDeviceInfo>& 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 { HifiAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const {
Lock lock(_deviceMutex); Lock lock(_deviceMutex);
@ -386,14 +426,16 @@ 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; HifiAudioDeviceInfo result;
foreach (HifiAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { foreach (HifiAudioDeviceInfo audioDevice, getAvailableDevices(mode)) {
if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) {
if ((!isHmd && audioDevice.getDeviceType() != HifiAudioDeviceInfo::hmd) || (isHmd && audioDevice.getDeviceType() != HifiAudioDeviceInfo::desktop)) {
result = audioDevice; result = audioDevice;
break; break;
} }
} }
}
return result; 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 // find a device in the list that matches the name we have and return it
foreach(QAudioDeviceInfo audioDevice, devices){ foreach(QAudioDeviceInfo audioDevice, devices){
if (audioDevice.deviceName() == CFStringGetCStringPtr(deviceName, kCFStringEncodingMacRoman)) { 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; HifiAudioDeviceInfo foundDevice;
foreach(QAudioDeviceInfo audioDevice, devices) { foreach(QAudioDeviceInfo audioDevice, devices) {
if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) {
foundDevice=HifiAudioDeviceInfo(audioDevice,true,mode); foundDevice = HifiAudioDeviceInfo(audioDevice, true, mode, HifiAudioDeviceInfo::desktop);
break; break;
} }
} }
@ -558,8 +600,8 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
} }
#endif #endif
// fallback for failed lookup is the default device // fallback for failed lookup is the default device
return (mode == QAudio::AudioInput) ? HifiAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice(), true,mode) : return (mode == QAudio::AudioInput) ? HifiAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice(), true, mode, HifiAudioDeviceInfo::desktop) :
HifiAudioDeviceInfo(QAudioDeviceInfo::defaultOutputDevice(), true, mode); HifiAudioDeviceInfo(QAudioDeviceInfo::defaultOutputDevice(), true, mode, HifiAudioDeviceInfo::desktop);
} }
bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) { 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) // Destruction of the pointers will occur when the parent object (this) is destroyed)
{ {
Lock lock(_checkDevicesMutex); Lock lock(_checkDevicesMutex);
_checkDevicesTimer->stop();
_checkDevicesTimer = nullptr; _checkDevicesTimer = nullptr;
} }
{ {
@ -948,13 +991,18 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
void AudioClient::changeDefault(HifiAudioDeviceInfo newDefault, QAudio::Mode mode) { void AudioClient::changeDefault(HifiAudioDeviceInfo newDefault, QAudio::Mode mode) {
HifiAudioDeviceInfo currentDevice = mode == QAudio::AudioInput ? _inputDeviceInfo : _outputDeviceInfo; 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); switchAudioDevice(mode, newDefault);
} }
} }
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo) { bool AudioClient::switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo& deviceInfo) {
auto device = deviceInfo; auto device = deviceInfo;
if (deviceInfo.getDevice().isNull()) {
qCDebug(audioclient) << __FUNCTION__ << " switching to null device :"
<< deviceInfo.deviceName() << " : " << deviceInfo.getDevice().deviceName();
}
if (mode == QAudio::AudioInput) { if (mode == QAudio::AudioInput) {
return switchInputToAudioDevice(device); return switchInputToAudioDevice(device);
} else { } else {
@ -962,8 +1010,8 @@ bool AudioClient::switchAudioDevice(QAudio::Mode mode, const HifiAudioDeviceInfo
} }
} }
bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName) { bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName, bool isHmd) {
return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName)); return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName, isHmd));
} }
void AudioClient::configureReverb() { void AudioClient::configureReverb() {
@ -1771,7 +1819,8 @@ void AudioClient::outputFormatChanged() {
bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) {
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << _inputDeviceInfo.deviceName() <<"----"<<inputDeviceInfo.getDevice().deviceName() << "]"; qCDebug(audioclient) << __FUNCTION__ << "_inputDeviceInfo: [" << _inputDeviceInfo.deviceName() << ":" << _inputDeviceInfo.getDevice().deviceName()
<< "-- inputDeviceInfo:" << inputDeviceInfo.deviceName() << ":" << inputDeviceInfo.getDevice().deviceName() << "]";
bool supportedFormat = false; bool supportedFormat = false;
// NOTE: device start() uses the Qt internal device list // NOTE: device start() uses the Qt internal device list
@ -1823,8 +1872,9 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice
} }
if (!inputDeviceInfo.getDevice().isNull()) { if (!inputDeviceInfo.getDevice().isNull()) {
qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available."; qCDebug(audioclient) << "The audio input device" << inputDeviceInfo.deviceName() << ":" << inputDeviceInfo.getDevice().deviceName() << "is available.";
//do not update UI that we're changing devices if default or same device
bool doEmit = _inputDeviceInfo.deviceName() != inputDeviceInfo.deviceName(); bool doEmit = _inputDeviceInfo.deviceName() != inputDeviceInfo.deviceName();
_inputDeviceInfo = inputDeviceInfo; _inputDeviceInfo = inputDeviceInfo;
if (doEmit) { if (doEmit) {
@ -1959,9 +2009,9 @@ void AudioClient::setHeadsetPluggedIn(bool pluggedIn) {
bool aecEnabled = enableAEC.get(); bool aecEnabled = enableAEC.get();
if ((pluggedIn || !aecEnabled) && _inputDeviceInfo.deviceName() != VOICE_RECOGNITION) { if ((pluggedIn || !aecEnabled) && _inputDeviceInfo.deviceName() != VOICE_RECOGNITION) {
switchAudioDevice(QAudio::AudioInput, VOICE_RECOGNITION); switchAudioDevice(QAudio::AudioInput, VOICE_RECOGNITION, false);
} else if (!pluggedIn && aecEnabled && _inputDeviceInfo.deviceName() != VOICE_COMMUNICATION) { } else if (!pluggedIn && aecEnabled && _inputDeviceInfo.deviceName() != VOICE_COMMUNICATION) {
switchAudioDevice(QAudio::AudioInput, VOICE_COMMUNICATION); switchAudioDevice(QAudio::AudioInput, VOICE_COMMUNICATION, false);
} }
} }
_isHeadsetPluggedIn = pluggedIn; _isHeadsetPluggedIn = pluggedIn;
@ -2007,7 +2057,8 @@ void AudioClient::noteAwakening() {
bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) {
Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread"); Q_ASSERT_X(QThread::currentThread() == thread(), Q_FUNC_INFO, "Function invoked on wrong thread");
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; qCDebug(audioclient) << __FUNCTION__ << "_outputdeviceInfo: [" << _outputDeviceInfo.deviceName() << ":" << _outputDeviceInfo.getDevice().deviceName()
<< "-- outputDeviceInfo:" << outputDeviceInfo.deviceName() << ":" << outputDeviceInfo.getDevice().deviceName() << "]";
bool supportedFormat = false; bool supportedFormat = false;
// NOTE: device start() uses the Qt internal device list // NOTE: device start() uses the Qt internal device list
@ -2063,7 +2114,9 @@ bool AudioClient::switchOutputToAudioDevice(const HifiAudioDeviceInfo outputDevi
} }
if (!outputDeviceInfo.getDevice().isNull()) { if (!outputDeviceInfo.getDevice().isNull()) {
qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; qCDebug(audioclient) << "The audio output device" << outputDeviceInfo.deviceName() << ":" << outputDeviceInfo.getDevice().deviceName() << "is available.";
//do not update UI that we're changing devices if default or same device
bool doEmit = _outputDeviceInfo.deviceName() != outputDeviceInfo.deviceName(); bool doEmit = _outputDeviceInfo.deviceName() != outputDeviceInfo.deviceName();
_outputDeviceInfo = outputDeviceInfo; _outputDeviceInfo = outputDeviceInfo;
if (doEmit) { if (doEmit) {

View file

@ -238,10 +238,11 @@ public slots:
bool shouldLoopbackInjectors() override { return _shouldEchoToServer; } bool shouldLoopbackInjectors() override { return _shouldEchoToServer; }
Q_INVOKABLE void changeDefault(HifiAudioDeviceInfo newDefault, QAudio::Mode mode); Q_INVOKABLE void changeDefault(HifiAudioDeviceInfo newDefault, QAudio::Mode mode);
void checkDefaultChanges(QList<HifiAudioDeviceInfo>& devices);
// calling with a null QAudioDevice will use the system default // 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 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 // Qt opensles plugin is not able to detect when the headset is plugged in
void setHeadsetPluggedIn(bool pluggedIn); void setHeadsetPluggedIn(bool pluggedIn);

View file

@ -22,10 +22,10 @@ HifiAudioDeviceInfo& HifiAudioDeviceInfo::operator=(const HifiAudioDeviceInfo& o
_audioDeviceInfo = other.getDevice(); _audioDeviceInfo = other.getDevice();
_mode = other.getMode(); _mode = other.getMode();
_isDefault = other.isDefault(); _isDefault = other.isDefault();
_deviceType = other.getDeviceType();
return *this; return *this;
} }
bool HifiAudioDeviceInfo::operator==(const HifiAudioDeviceInfo& rhs) const { bool HifiAudioDeviceInfo::operator==(const HifiAudioDeviceInfo& rhs) const {
//Does the QAudioDeviceinfo match as well as is this the default device or //Does the QAudioDeviceinfo match as well as is this the default device or
return getDevice() == rhs.getDevice() && isDefault() == rhs.isDefault(); return getDevice() == rhs.getDevice() && isDefault() == rhs.isDefault();
@ -33,4 +33,3 @@ bool HifiAudioDeviceInfo::operator==(const HifiAudioDeviceInfo& rhs) const {
bool HifiAudioDeviceInfo::operator!=(const HifiAudioDeviceInfo& rhs) const { bool HifiAudioDeviceInfo::operator!=(const HifiAudioDeviceInfo& rhs) const {
return getDevice() != rhs.getDevice() || isDefault() != rhs.isDefault(); return getDevice() != rhs.getDevice() || isDefault() != rhs.isDefault();
} }

View file

@ -23,17 +23,25 @@ class HifiAudioDeviceInfo : public QObject {
Q_OBJECT Q_OBJECT
public: public:
enum DeviceType {
desktop,
hmd,
both
};
HifiAudioDeviceInfo() : QObject() {} HifiAudioDeviceInfo() : QObject() {}
HifiAudioDeviceInfo(const HifiAudioDeviceInfo &deviceInfo) : QObject(){ HifiAudioDeviceInfo(const HifiAudioDeviceInfo &deviceInfo) : QObject(){
_audioDeviceInfo = deviceInfo.getDevice(); _audioDeviceInfo = deviceInfo.getDevice();
_mode = deviceInfo.getMode(); _mode = deviceInfo.getMode();
_isDefault = deviceInfo.isDefault(); _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), _audioDeviceInfo(deviceInfo),
_isDefault(isDefault), _isDefault(isDefault),
_mode(mode){ _mode(mode),
_deviceType(devType){
} }
void setMode(QAudio::Mode mode) { _mode = mode; } void setMode(QAudio::Mode mode) { _mode = mode; }
@ -52,7 +60,7 @@ public:
QAudioDeviceInfo getDevice() const { return _audioDeviceInfo; } QAudioDeviceInfo getDevice() const { return _audioDeviceInfo; }
bool isDefault() const { return _isDefault; } bool isDefault() const { return _isDefault; }
QAudio::Mode getMode() const { return _mode; } QAudio::Mode getMode() const { return _mode; }
DeviceType getDeviceType() const { return _deviceType; }
HifiAudioDeviceInfo& operator=(const HifiAudioDeviceInfo& other); HifiAudioDeviceInfo& operator=(const HifiAudioDeviceInfo& other);
bool operator==(const HifiAudioDeviceInfo& rhs) const; bool operator==(const HifiAudioDeviceInfo& rhs) const;
bool operator!=(const HifiAudioDeviceInfo& rhs) const; bool operator!=(const HifiAudioDeviceInfo& rhs) const;
@ -61,6 +69,7 @@ private:
QAudioDeviceInfo _audioDeviceInfo; QAudioDeviceInfo _audioDeviceInfo;
bool _isDefault { false }; bool _isDefault { false };
QAudio::Mode _mode { QAudio::AudioInput }; QAudio::Mode _mode { QAudio::AudioInput };
DeviceType _deviceType{ both };
public: public:
static const QString DEFAULT_DEVICE_NAME; static const QString DEFAULT_DEVICE_NAME;

View file

@ -96,10 +96,16 @@ void Head::simulate(float deltaTime) {
// no blinking when brows are raised; blink less with increasing loudness // no blinking when brows are raised; blink less with increasing loudness
const float BASE_BLINK_RATE = 15.0f / 60.0f; const float BASE_BLINK_RATE = 15.0f / 60.0f;
const float ROOT_LOUDNESS_TO_BLINK_INTERVAL = 0.25f; 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))) { ROOT_LOUDNESS_TO_BLINK_INTERVAL) / BASE_BLINK_RATE, deltaTime))) {
float randSpeedVariability = randFloat(); float randSpeedVariability = randFloat();
float eyeBlinkVelocity = BLINK_SPEED + randSpeedVariability * BLINK_SPEED_VARIABILITY; 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; _leftEyeBlinkVelocity = eyeBlinkVelocity;
_rightEyeBlinkVelocity = eyeBlinkVelocity; _rightEyeBlinkVelocity = eyeBlinkVelocity;
if (randFloat() < 0.5f) { if (randFloat() < 0.5f) {
@ -114,13 +120,12 @@ void Head::simulate(float deltaTime) {
if (_leftEyeBlink == FULLY_CLOSED) { if (_leftEyeBlink == FULLY_CLOSED) {
_leftEyeBlinkVelocity = -BLINK_SPEED; _leftEyeBlinkVelocity = -BLINK_SPEED;
updateEyeLookAt();
} else if (_leftEyeBlink == FULLY_OPEN) { } else if (_leftEyeBlink == FULLY_OPEN) {
_leftEyeBlinkVelocity = 0.0f; _leftEyeBlinkVelocity = 0.0f;
} }
if (_rightEyeBlink == FULLY_CLOSED) { if (_rightEyeBlink == FULLY_CLOSED) {
_rightEyeBlinkVelocity = -BLINK_SPEED; _rightEyeBlinkVelocity = -BLINK_SPEED;
} else if (_rightEyeBlink == FULLY_OPEN) { } else if (_rightEyeBlink == FULLY_OPEN) {
_rightEyeBlinkVelocity = 0.0f; _rightEyeBlinkVelocity = 0.0f;
} }
@ -350,3 +355,24 @@ float Head::getFinalPitch() const {
float Head::getFinalRoll() const { float Head::getFinalRoll() const {
return glm::clamp(_baseRoll + _deltaRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); 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;
}

View file

@ -79,6 +79,9 @@ public:
float getTimeWithoutTalking() const { return _timeWithoutTalking; } float getTimeWithoutTalking() const { return _timeWithoutTalking; }
virtual void setLookAtPosition(const glm::vec3& lookAtPosition) override;
void updateEyeLookAt();
protected: protected:
// disallow copies of the Head, copy of owning Avatar is disallowed too // disallow copies of the Head, copy of owning Avatar is disallowed too
Head(const Head&); Head(const Head&);
@ -123,6 +126,10 @@ protected:
int _leftEyeLookAtID; int _leftEyeLookAtID;
int _rightEyeLookAtID; int _rightEyeLookAtID;
glm::vec3 _requestLookAtPosition;
bool _forceBlinkToRetarget { false };
bool _isEyeLookAtUpdated { false };
// private methods // private methods
void calculateMouthShapes(float timeRatio); void calculateMouthShapes(float timeRatio);
void applyEyelidOffset(glm::quat headOrientation); void applyEyelidOffset(glm::quat headOrientation);

View file

@ -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;
}

View file

@ -1484,6 +1484,7 @@ public:
std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonData() const; std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonData() const;
void sendSkeletonData() const; void sendSkeletonData() const;
QVector<JointData> getJointData() const; QVector<JointData> getJointData() const;
glm::vec3 getHeadJointFrontVector() const;
signals: signals:

View file

@ -64,7 +64,7 @@ public:
void setBlendshapeCoefficients(const QVector<float>& blendshapeCoefficients) { _blendshapeCoefficients = blendshapeCoefficients; } void setBlendshapeCoefficients(const QVector<float>& blendshapeCoefficients) { _blendshapeCoefficients = blendshapeCoefficients; }
const glm::vec3& getLookAtPosition() const { return _lookAtPosition; } const glm::vec3& getLookAtPosition() const { return _lookAtPosition; }
void setLookAtPosition(const glm::vec3& lookAtPosition) { virtual void setLookAtPosition(const glm::vec3& lookAtPosition) {
if (_lookAtPosition != lookAtPosition) { if (_lookAtPosition != lookAtPosition) {
_lookAtPositionChanged = usecTimestampNow(); _lookAtPositionChanged = usecTimestampNow();
} }

View file

@ -13,6 +13,7 @@
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QPluginLoader> #include <QtCore/QPluginLoader>
#include <shared/QtHelpers.h>
//#define HIFI_PLUGINMANAGER_DEBUG //#define HIFI_PLUGINMANAGER_DEBUG
#if defined(HIFI_PLUGINMANAGER_DEBUG) #if defined(HIFI_PLUGINMANAGER_DEBUG)
@ -21,6 +22,7 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#include <UserActivityLogger.h> #include <UserActivityLogger.h>
#include <QThreadPool>
#include "RuntimePlugin.h" #include "RuntimePlugin.h"
#include "CodecPlugin.h" #include "CodecPlugin.h"
@ -221,7 +223,17 @@ const OculusPlatformPluginPointer PluginManager::getOculusPlatformPlugin() {
return oculusPlatformPlugin; 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 std::once_flag once;
static auto deviceAddedCallback = [](QString deviceName) { static auto deviceAddedCallback = [](QString deviceName) {
qCDebug(plugins) << "Added device: " << deviceName; qCDebug(plugins) << "Added device: " << deviceName;

View file

@ -51,6 +51,7 @@ public:
using PluginFilter = std::function<bool(const QJsonObject&)>; using PluginFilter = std::function<bool(const QJsonObject&)>;
void setPluginFilter(PluginFilter pluginFilter) { _pluginFilter = pluginFilter; } void setPluginFilter(PluginFilter pluginFilter) { _pluginFilter = pluginFilter; }
Q_INVOKABLE DisplayPluginList getAllDisplayPlugins();
signals: signals:
void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices); void inputDeviceRunningChanged(const QString& pluginName, bool isRunning, const QStringList& runningDevices);

View file

@ -0,0 +1,43 @@
//
// PropItem.qml
//
// Created by Sam Gateau on 3/2/2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
PropItem {
Global { id: global }
id: root
// Scalar Prop
// property bool integral: false
// property var numDigits: 2
Rectangle {
id: valueLabel
anchors.left: root.splitter.right
anchors.right: root.right
anchors.verticalCenter: root.verticalCenter
// horizontalAlignment: global.valueTextAlign
height: global.slimHeight
function getColor() {
var c = root.valueVarGetter();
return Qt.rgba(c.red, c.green, c.blue, 1.0);
}
// background: Rectangle {
color: { return getColor() }
border.color: global.colorBorderLight
border.width: global.valueBorderWidth
radius: global.valueBorderRadius
// }
}
}

View file

@ -13,12 +13,19 @@ import QtQuick.Layouts 1.3
import stylesUit 1.0 import stylesUit 1.0
import controlsUit 1.0 as HifiControls import controlsUit 1.0 as HifiControls
import "../lib/prop" as Prop
Rectangle { Rectangle {
HifiConstants { id: hifi;} HifiConstants { id: hifi;}
color: Qt.rgba(hifi.colors.baseGray.r, hifi.colors.baseGray.g, hifi.colors.baseGray.b, 0.8); color: Qt.rgba(hifi.colors.baseGray.r, hifi.colors.baseGray.g, hifi.colors.baseGray.b, 0.8);
id: root; id: root;
property var theMaterial: {}
property var theMaterialAttributes: {}
property var hasMaterial: false
property var isReadOnly: true
function fromScript(message) { function fromScript(message) {
switch (message.method) { switch (message.method) {
case "setObjectInfo": case "setObjectInfo":
@ -26,10 +33,19 @@ Rectangle {
break; break;
case "setMaterialJSON": case "setMaterialJSON":
materialJSONText.text = message.params.materialJSONText; materialJSONText.text = message.params.materialJSONText;
theMaterial = JSON.parse(message.params.materialJSONText)
theMaterialAttributes = theMaterial.materials
hasMaterial = (theMaterial !== undefined)
break; break;
} }
} }
Column {
anchors.left: parent.left
anchors.right: parent.right
Rectangle { Rectangle {
id: entityIDContainer id: entityIDContainer
height: 52 height: 52
@ -45,8 +61,171 @@ Rectangle {
} }
} }
Prop.PropString {
visible: hasMaterial && ("name" in theMaterialAttributes)
label: "name"
object: theMaterialAttributes
property: "name"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("model" in theMaterialAttributes)
label: "model"
object: theMaterialAttributes
property: "model"
readOnly: isReadOnly
}
Prop.PropColor {
visible: hasMaterial && ("albedo" in theMaterialAttributes)
label: "albedo"
object: theMaterialAttributes
property: "albedo"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("albedoMap" in theMaterialAttributes)
label: "albedoMap"
object: theMaterialAttributes
property: "albedoMap"
readOnly: isReadOnly
}
Prop.PropScalar {
visible: hasMaterial && ("opacity" in theMaterialAttributes)
label: "opacity"
object: theMaterialAttributes
property: "opacity"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("opacityMap" in theMaterialAttributes)
label: "opacityMap"
object: theMaterialAttributes
property: "opacityMap"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("opacityMapMode" in theMaterialAttributes)
label: "opacityMapMode"
object: theMaterialAttributes
property: "opacityMapMode"
readOnly: isReadOnly
}
/*Prop.PropEnum {
visible: hasMaterial && ("opacityMapMode" in theMaterialAttributes)
label: "opacityMapMode"
object: theMaterialAttributes
property: "opacityMapMode"
readOnly: isReadOnly
enums: ["None", "Mask", "Blend"]
} */
Prop.PropScalar {
visible: hasMaterial && ("opacityCutoff" in theMaterialAttributes)
label: "opacity Cutoff"
object: theMaterialAttributes
property: "opacityCutoff"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("occlusionMap" in theMaterialAttributes)
label: "occlusionMap"
object: theMaterialAttributes
property: "occlusionMap"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("normalMap" in theMaterialAttributes)
label: "normalMap"
object: theMaterialAttributes
property: "normalMap"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("bumpMap" in theMaterialAttributes)
label: "normalMap from bumpMap"
object: theMaterialAttributes
property: "bumpMap"
readOnly: isReadOnly
}
Prop.PropScalar {
visible: hasMaterial && ("roughness" in theMaterialAttributes)
label: "roughness"
object: theMaterialAttributes
property: "roughness"
readOnly: isReadOnly
}
Prop.PropScalar {
visible: hasMaterial && ("metallic" in theMaterialAttributes)
label: "metallic"
object: theMaterialAttributes
property: "metallic"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("roughnessMap" in theMaterialAttributes)
label: "roughnessMap"
object: theMaterialAttributes
property: "roughnessMap"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("glossMap" in theMaterialAttributes)
label: "roughnessMap from glossMap"
object: theMaterialAttributes
property: "glossMap"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("metallicMap" in theMaterialAttributes)
label: "metallicMap"
object: theMaterialAttributes
property: "metallicMap"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("specularMap" in theMaterialAttributes)
label: "metallicMap from specularMap"
object: theMaterialAttributes
property: "specularMap"
readOnly: isReadOnly
}
Prop.PropScalar {
visible: hasMaterial && ("scattering" in theMaterialAttributes)
label: "scattering"
object: theMaterialAttributes
property: "scattering"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("scatteringMap" in theMaterialAttributes)
label: "scatteringMap"
object: theMaterialAttributes
property: "scatteringMap"
readOnly: isReadOnly
}
Prop.PropColor {
visible: hasMaterial && ("emissive" in theMaterialAttributes)
label: "emissive"
object: theMaterialAttributes
property: "emissive"
readOnly: isReadOnly
}
Prop.PropString {
visible: hasMaterial && ("emissiveMap" in theMaterialAttributes)
label: "emissiveMap"
object: theMaterialAttributes
property: "emissiveMap"
readOnly: isReadOnly
}
Original.ScrollView { Original.ScrollView {
anchors.top: entityIDContainer.bottom // anchors.top: entityIDContainer.bottom
height: root.height - entityIDContainer.height height: root.height - entityIDContainer.height
width: root.width width: root.width
clip: true clip: true
@ -62,4 +241,5 @@ Rectangle {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
} }
}
} }

View file

@ -377,7 +377,7 @@ function displayInitialLaunchWindow() {
initialLaunchWindow.fromQml.connect(onMessageFromInitialLaunchWindow); 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"); var SECOND_LAUNCH_QML_PATH = Script.resolvePath("simplifiedFTUE/SecondLaunchWindow.qml");
@ -405,7 +405,7 @@ function displaySecondLaunchWindow() {
secondLaunchWindow.fromQml.connect(onMessageFromSecondLaunchWindow); secondLaunchWindow.fromQml.connect(onMessageFromSecondLaunchWindow);
Window.location = "file:///~/serverless/tutorial.json"; Window.location = "file:///~/serverless/empty.json";
} }
function closeInitialLaunchWindow() { function closeInitialLaunchWindow() {

View file

@ -61,17 +61,21 @@ Item {
Rectangle { Rectangle {
width: 5 width: 5
height: 5 height: 5
color: "red" color: "blue"
ColorAnimation on color { loops: Animation.Infinite; from: "red"; to: "yellow"; duration: 1000 } ColorAnimation on color { loops: Animation.Infinite; from: "blue"; to: "yellow"; duration: 1000 }
} }
WebEngineView { WebEngineView {
id: root id: root
url: "https://google.com/" url: "https://www.webrtc-experiment.com/Pluginfree-Screen-Sharing/#19583796789766627"
x: 6; y: 6; // url: "https://vimeo.com/108650530"
width: parent.width * 0.8 // url: "https://www.youtube.com/watch?v=7EWQOeQf32U&autoplay=1&loop=1"
height: parent.height * 0.8 // x: 6; y: 6;
anchors.fill: parent
// width: parent.width * 0.8
// height: parent.height * 0.8
} }
} }

View file

@ -39,7 +39,7 @@ void MacQml::init() {
_surface->load(url, callback); _surface->load(url, callback);
_surface->resize(_window->size()); _surface->resize(_window->size());
_surface->resume(); _surface->resume();
_window->installEventFilter(_surface.get());
} }
void MacQml::draw() { void MacQml::draw() {

View file

@ -9,7 +9,7 @@ public:
QmlPtr _surface; QmlPtr _surface;
GLuint _fbo{ 0 }; GLuint _fbo{ 0 };
MacQml(const QWindow* window) : Parent(window) {} MacQml(QWindow* window) : Parent(window) {}
void update() override; void update() override;
void init() override; void init() override;
void draw() override; void draw() override;

View file

@ -24,7 +24,7 @@ public:
std::array<std::array<QmlInfo, DIVISIONS_Y>, DIVISIONS_X> _surfaces; std::array<std::array<QmlInfo, DIVISIONS_Y>, DIVISIONS_X> _surfaces;
GLuint _fbo{ 0 }; GLuint _fbo{ 0 };
StressWeb(const QWindow* window) : Parent(window) {} StressWeb(QWindow* window) : Parent(window) {}
static QString getSourceUrl(bool video); static QString getSourceUrl(bool video);
void buildSurface(QmlInfo& qmlInfo, bool video); void buildSurface(QmlInfo& qmlInfo, bool video);
void destroySurface(QmlInfo& qmlInfo); void destroySurface(QmlInfo& qmlInfo);

View file

@ -8,8 +8,8 @@
class TestCase { class TestCase {
public: public:
using QmlPtr = QSharedPointer<hifi::qml::OffscreenSurface>; using QmlPtr = QSharedPointer<hifi::qml::OffscreenSurface>;
using Builder = std::function<TestCase*(const QWindow*)>; using Builder = std::function<TestCase*(QWindow*)>;
TestCase(const QWindow* window) : _window(window) {} TestCase(QWindow* window) : _window(window) {}
virtual void init(); virtual void init();
virtual void destroy(); virtual void destroy();
virtual void update(); virtual void update();
@ -18,6 +18,6 @@ public:
protected: protected:
QOpenGLFunctions_4_1_Core _glf; QOpenGLFunctions_4_1_Core _glf;
const QWindow* _window; QWindow* _window;
std::function<void(uint32_t, void*)> _discardLamdba; std::function<void(uint32_t, void*)> _discardLamdba;
}; };

View file

@ -205,12 +205,22 @@ void TestWindow::resizeEvent(QResizeEvent* ev) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
#ifdef Q_OS_MAC
auto format = getDefaultOpenGLSurfaceFormat(); 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); QSurfaceFormat::setDefaultFormat(format);
#endif
QGuiApplication app(argc, argv); 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); TestWindow window(builder);
return app.exec(); return app.exec();
} }

View file

@ -1,8 +1,10 @@
# General # General
This document describes the process to build Qt 5.12.3. 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. Note that there are several patches.
The second patch is to OpenSL ES audio. * 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 third is a patch to QScriptEngine to prevent crashes in QScriptEnginePrivate::reportAdditionalMemoryCost, during garbage collection. See https://bugreports.qt.io/browse/QTBUG-76176 * 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 ## Requirements
### Windows ### Windows
1. Visual Studio 2017 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` `cd qt5`
`git apply --ignore-space-change --ignore-whitespace patches/aec.patch` `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/qtscript-crash-fix.patch`
`git apply --ignore-space-change --ignore-whitespace patches/mac-web-video.patch`
`cd ..` `cd ..`
#### Configuring #### Configuring
`mkdir qt5-install` `mkdir qt5-install`

View file

@ -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<gfx::ColorTransform> 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;